首页 » SEO优化 » php缓存phpini技巧_教你运用 koa2 vite ts vue3 pinia 构建前端 SSR 企业级项目

php缓存phpini技巧_教你运用 koa2 vite ts vue3 pinia 构建前端 SSR 企业级项目

访客 2024-11-17 0

扫一扫用手机浏览

文章目录 [+]

如果大家对 Vite 感兴趣可以去看看专栏: 《Vite 从入门到精通》

理解 SSR什么是 SSR

做事器端渲染(Server-Side Rendering)是指由做事端完成页面的 HTML 构造拼接的页面处理技能,发送到浏览器,然后为其绑定状态与事宜,成为完备可交互页面的过程。

php缓存phpini技巧_教你运用 koa2  vite  ts  vue3  pinia 构建前端 SSR 企业级项目

大略理解便是html是由做事端写出,可以动态改变页面内容,即所谓的动态页面。
从前的 php、asp 、jsp 这些 Server page 都是 SSR 的。

php缓存phpini技巧_教你运用 koa2  vite  ts  vue3  pinia 构建前端 SSR 企业级项目
(图片来自网络侵删)
为什么利用 SSR网页内容在做事器端渲染完成,一次性传输到浏览器,以是 首屏加载速率非常快;有利于SEO,由于做事器返回的是一个完全的 html,在浏览器可以看到完全的 dom,对付爬虫、百度搜索等引擎就比较友好;快速查看

github 仓库地址

长话短说,直接开干 ~

建议包管理器利用优先级:pnpm > yarn > npm > cnpm

一、初始化项目

pnpm create vite koa2-ssr-vue3-ts-pinia -- --template vue-ts复制代码集成基本配置

由于本文的重点在于 SSR 配置,为了优化读者的不雅观感体验,以是项目的基本配置就不做详细先容,在我上一篇文章《手把手教你用 vite+vue3+ts+pinia+vueuse 打造企业级前端项目》中已详细先容,大家可以自行查阅

修正 tsconfig.json :查看代码修正 vite.config.ts:查看代码集成 eslint 和 prettier 统一代码质量风格的:查看教程集成 commitizen 和 husky 规范 git 提交:查看教程

到这里我们项目的基本框架都搭建完成啦~

二、修正客户端入口修正 ~/src/main.ts

import { createSSRApp } from "vue";import App from "./App.vue";// 为了担保数据的互不滋扰,每次要求须要导出一个新的实例export const createApp = () => { const app = createSSRApp(App); return { app };}复制代码新建 ~/src/entry-client.ts

import { createApp } from "./main"const { app } = createApp();app.mount("#app");复制代码修正 ~/index.html 的入口

<!DOCTYPE html><html lang="en"> ... <script type="module" src="/src/entry-client.ts"></script> ...</html>复制代码

到这里你运行 pnpm run dev ,创造页面中还是可以正常显示,由于到目前只是做了一个文件的拆分,以及改换了 createSSRApp 方法;

三、创建开拓做事器利用 Koa2安装 koa2

pnpm i koa --save && pnpm i @types/koa --save-dev复制代码安装中间件 koa-connect

pnpm i koa-connect --save复制代码利用:新建 ~/server.js

备注:由于该文件为 node 运行入口,以是用 js 即可,如果用 ts 文件,需单独利用 ts-node 等去运行,导致程序变繁芜

const Koa = require('koa');(async () => { const app = new Koa(); app.use(async (ctx) => { ctx.body = `<!DOCTYPE html> <html lang="en"> <head><title>koa2 + vite + ts + vue3 + vue-router</title></head> <body> <h1 style="text-align: center;">利用 koa2 + vite + ts + vue3 + vue-router 集成前端 SSR 企业级项目</h1> </body> </html>`; }); app.listen(9000, () => { console.log('server is listening in 9000'); });})();复制代码运行 node server.js结果:

渲染更换成项目根目录下的index.html修正 server.js 中的 ctx.body 返回的是 index.html

const fs = require('fs'); const path = require('path'); const Koa = require('koa'); (async () => { const app = new Koa(); // 获取 index.html const template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8'); ​ app.use(async (ctx) => { ctx.body = template; }); ​ app.listen(9000, () => { console.log('server is listening in 9000'); }); })();复制代码运行 node server.js后, 我们就会看到返回的是空缺内容的 index.html 了,但是我们须要返回的是 vue 模板 ,那么我们只须要做个 正则的更换给 index.html 添加 <!--app-html--> 标记

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>koa2 + vite + ts + vue3</title> </head> <body> <div id="app"><!--app-html--></div> <script type="module" src="/src/entry-client.ts"></script> </body> </html>复制代码修正 server.js 中的 ctx.body

// other code ...(async () => { const app = new Koa(); // 获取index.html const template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8'); app.use(async (ctx) => { let vueTemplate = '<h1 style="text-align:center;">现在假装这是一个vue模板</h1>'; // 更换 index.html 中的 <!--app-html--> 标记 let html = template.replace('<!--app-html-->', vueTemplate); ctx.body = html; }); app.listen(9000, () => { console.log('server is listening in 9000'); });})();复制代码运行 node server.js后,我们就会看到返回的 变量 vueTemplate 内容

那么到现在做事已正常启动了,但是我们试想一下,我们页面模板利用的是 vue,并且 vue 返回的是一个 vue 实例模板,以是我就要把这个 vue 实例模板 转换成 可渲染的 html,那么 @vue/server-renderer 就应运而生了

四、新增做事端入口

由于 vue 返回的是 vue 实例模板 而不是 可渲染的 html ,以是我们须要利用 @vue/server-renderer 进行转换

安装 @vue/server-renderer

pnpm i @vue/server-renderer --save复制代码新建 ~/src/entry-server.ts

import { createApp } from './main';import { renderToString } from '@vue/server-renderer';export const render = async () => { const { app } = createApp(); // 注入vue ssr中的高下文工具 const renderCtx: {modules?: string[]} = {} let renderedHtml = await renderToString(app, renderCtx) return { renderedHtml };}复制代码

那么如何去利用 entry-server.ts 呢,到这里就须要 vite 了

五、注入vite修正 ~/server.js

const fs = require('fs')const path = require('path')const Koa = require('koa')const koaConnect = require('koa-connect')const vite = require('vite');(async () => { const app = new Koa(); // 创建 vite 做事 const viteServer = await vite.createServer({ root: process.cwd(), logLevel: 'error', server: { middlewareMode: true, }, }) // 注册 vite 的 Connect 实例作为中间件(把稳:vite.middlewares 是一个 Connect 实例) app.use(koaConnect(viteServer.middlewares)) app.use(async ctx => { try { // 1. 获取index.html let template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8'); // 2. 运用 Vite HTML 转换。
这将会注入 Vite HMR 客户端, template = await viteServer.transformIndexHtml(ctx.path, template) // 3. 加载做事器入口, vite.ssrLoadModule 将自动转换 const { render } = await viteServer.ssrLoadModule('/src/entry-server.ts') // 4. 渲染运用的 HTML const { renderedHtml } = await render(ctx, {}) const html = template.replace('<!--app-html-->', renderedHtml) ctx.type = 'text/html' ctx.body = html } catch (e) { viteServer && viteServer.ssrFixStacktrace(e) console.log(e.stack) ctx.throw(500, e.stack) } }) app.listen(9000, () => { console.log('server is listening in 9000'); });})()复制代码
运行 node server.js 就可以看到返回的 App.vue 模板中的内容了,如下图

并且我们 右键查看显示网页源代码,也会看到渲染的正常 html

<!DOCTYPE html><html lang="en"> <head> <script type="module" src="/@vite/client"></script> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>koa2 + vite + ts + vue3</title> </head> <body> <div id="app"><!--[--><img alt="Vue logo" src="/src/assets/logo.png"><!--[--><h1 data-v-469af010>Hello Vue 3 + TypeScript + Vite</h1><p data-v-469af010> Recommended IDE setup: <a href="<https://code.visualstudio.com/>" target="_blank" data-v-469af010>VSCode</a> + <a href="<https://github.com/johnsoncodehk/volar>" target="_blank" data-v-469af010>Volar</a></p><p data-v-469af010>See <code data-v-469af010>README.md</code> for more information.</p><p data-v-469af010><a href="<https://vitejs.dev/guide/features.html>" target="_blank" data-v-469af010> Vite Docs </a> | <a href="<https://v3.vuejs.org/>" target="_blank" data-v-469af010>Vue 3 Docs</a></p><button type="button" data-v-469af010>count is: 0</button><p data-v-469af010> Edit <code data-v-469af010>components/HelloWorld.vue</code> to test hot module replacement. </p><!--]--><!--]--></div> <script type="module" src="/src/entry-client.ts"></script> </body></html>复制代码

到这里我们就已经在 开拓环境 已经正常的渲染了,但我们想一下,在 生产环境 我们该当怎么做呢,由于咱们不可能直接在 生产环境 运行利用 vite 吧!

以是咱们接下来处理如何在 生产环境 运行吧

六、添加开拓环境

为了将 SSR 项目可以在生产环境运行,我们须要:

正常构建天生一个 客户端构建包;再天生一个 SSR 构建,使其通过 require() 直接加载,这样便无需再利用 Vite 的 ssrLoadModule;修正 package.json

...{"scripts": { // 开拓环境 "dev": "node server-dev.js", // 生产环境 "server": "node server-prod.js", // 构建 "build": "pnpm build:client && pnpm build:server", "build:client": "vite build --outDir dist/client", "build:server": "vite build --ssr src/entry-server.js --outDir dist/server", },}...复制代码修正 server.js 为 server-dev.js运行 pnpm run build 构建包新增 server-prod.js

把稳:为了处理静态资源,须要在此新增 koa-send 中间件: pnpm i koa-send --save

const Koa = require('koa');const sendFile = require('koa-send');const path = require('path');const fs = require('fs');const resolve = (p) => path.resolve(__dirname, p);const clientRoot = resolve('dist/client');const template = fs.readFileSync(resolve('dist/client/index.html'), 'utf-8');const render = require('./dist/server/entry-server.js').render;const manifest = require('./dist/client/ssr-manifest.json');(async () => { const app = new Koa(); app.use(async (ctx) => {// 要求的是静态资源 if (ctx.path.startsWith('/assets')) { await sendFile(ctx, ctx.path, { root: clientRoot }); return; } const [ appHtml ] = await render(ctx, manifest); const html = template .replace('<!--app-html-->', appHtml); ctx.type = 'text/html'; ctx.body = html; }); app.listen(8080, () => console.log('started server on http://localhost:8080'));})();复制代码

到这里,我们在 开拓环境 和 天生环境 已经都可以正常访问了,那么是不是就万事无忧了呢?

为了用户的更极致的用户体验,那么 预加载 就必须要安排了

七、预加载

我们知道 vue 组件在 html 中渲染时都是动态去天生的对应的 js 和 css 等;

那么我们假如在用户获取 做事端模板 (也便是实行 vite build 后天生的 dist/client 目录) 的时候,直接在 html 中把对应的 js 和 css 文件预渲染了,这便是 静态站点天生(SSG) 的形式。

闲话少说,明白道理了之后,直接开干 ~

天生预加载指令:在 package.json 中的 build:client 添加 --ssrManifest 标志,运行后天生 ssr-manifest.json

...{"scripts": { ... "build:client": "vite build --ssrManifest --outDir dist/client", ... },}...复制代码在 entry-sercer.ts 中添加解析天生的 ssr-manifest.json 方法

export const render = async ( ctx: ParameterizedContext, manifest: Record<string, string[]>): Promise<[string, string]> => { const { app } = createApp(); console.log(ctx, manifest, ''); const renderCtx: { modules?: string[] } = {}; const renderedHtml = await renderToString(app, renderCtx); const preloadLinks = renderPreloadLinks(renderCtx.modules, manifest); return [renderedHtml, preloadLinks];};/ 解析须要预加载的链接 @param modules @param manifest @returns string /function renderPreloadLinks( modules: undefined | string[], manifest: Record<string, string[]>): string { let links = ''; const seen = new Set(); if (modules === undefined) throw new Error(); modules.forEach((id) => { const files = manifest[id]; if (files) { files.forEach((file) => { if (!seen.has(file)) { seen.add(file); links += renderPreloadLink(file); } }); } }); return links;}/ 预加载的对应的地址 下面的方法只针对了 js 和 css,如果须要处理其它文件,自行添加即可 @param file @returns string /function renderPreloadLink(file: string): string { if (file.endsWith('.js')) { return `<link rel="modulepreload" crossorigin href="${file}">`; } else if (file.endsWith('.css')) { return `<link rel="stylesheet" href="${file}">`; } else { return ''; }}复制代码给 index.html 添加 <!--preload-links--> 标记改造 server-prod.js

...(async () => { const app = new Koa(); app.use(async (ctx) => {... const [appHtml, preloadLinks] = await render(ctx, manifest); const html = template .replace('<!--preload-links-->', preloadLinks) .replace('<!--app-html-->', appHtml); // do something }); app.listen(8080, () => console.log('started server on http://localhost:8080'));})();复制代码运行 pnpm run build && pnpm run serve 就可正常显示了

到这里基本的渲染就完成了,由于我们是须要在浏览器上渲染的,以是 路由 vue-router 就必不可少了

八、集成 vue-router安装 vue-router

pnpm i vue-router --save复制代码新增对应的路由页面 index.vue 、 login.vue 、 user.vue新增 src/router/index.ts

import { createRouter as createVueRouter, createMemoryHistory, createWebHistory, Router} from 'vue-router';export const createRouter = (type: 'client' | 'server'): Router => createVueRouter({ history: type === 'client' ? createWebHistory() : createMemoryHistory(), routes: [ { path: '/', name: 'index', meta: { title: '首页', keepAlive: true, requireAuth: true }, component: () => import('@/pages/index.vue') }, { path: '/login', name: 'login', meta: { title: '登录', keepAlive: true, requireAuth: false }, component: () => import('@/pages/login.vue') }, { path: '/user', name: 'user', meta: { title: '用户中央', keepAlive: true, requireAuth: true }, component: () => import('@/pages/user.vue') } ] });复制代码修正入口文件 src/enter-client.ts

import { createApp } from './main';import { createRouter } from './router';const router = createRouter('client');const { app } = createApp();app.use(router);router.isReady().then(() => { app.mount('#app', true);});复制代码修正入口文件 src/enter-server.ts

...import { createRouter } from './router'const router = createRouter('client');export const render = async ( ctx: ParameterizedContext, manifest: Record<string, string[]>): Promise<[string, string]> => { const { app } = createApp(); // 路由注册 const router = createRouter('server'); app.use(router); await router.push(ctx.path); await router.isReady(); ...};...复制代码运行 pnpm run build && pnpm run serve 就可正常显示了九、集成 pinia安装

pnpm i pinia --save复制代码新建 src/store/user.ts

import { defineStore } from 'pinia';export default defineStore('user', { state: () => { return { name: '张三', age: 20 }; }, actions: { updateName(name: string) { this.name = name; }, updateAge(age: number) { this.age = age; } }});复制代码新建 src/store/index.ts

import { createPinia } from 'pinia';import useUserStore from './user';export default () => { const pinia = createPinia(); useUserStore(pinia); return pinia;};复制代码新建 UsePinia.vue 利用,并且在 pages/index.vue 中引入

<template> <h2>欢迎利用vite+vue3+ts+pinia+vue-router4</h2> <div>{{ userStore.name }}的年事: {{ userStore.age }}</div ><br /> <button @click="addAge">点击给{{ userStore.name }}的年事增加一岁</button> <br /></template><script lang="ts"> import { defineComponent } from 'vue'; import useUserStore from '@/store/user'; export default defineComponent({ name: 'UsePinia', setup() { const userStore = useUserStore(); const addAge = () => { userStore.updateAge(++userStore.age); }; return { userStore, addAge }; } });</script>复制代码注入 pinia :修正 src/entry-client.ts

...import createStore from '@/store';const pinia = createStore();const { app } = createApp();app.use(router);app.use(pinia);// 初始化 pini// 把稳:__INITIAL_STATE__须要在 src/types/shims-global.d.ts中定义if (window.__INITIAL_STATE__) { pinia.state.value = JSON.parse(window.__INITIAL_STATE__);}...复制代码修正 src/entry-server.ts

...import createStore from '@/store';export const render = () => { ... // pinia const pinia = createStore(); app.use(pinia); const state = JSON.stringify(pinia.state.value); ... return [renderedHtml, state, preloadLinks];}...复制代码修正 server-dev.js 和 server-prod.js

...const [renderedHtml, state, preloadLinks] = await render(ctx, {});const html = template .replace('<!--app-html-->', renderedHtml) .replace('<!--pinia-state-->', state); // server-prod.js .replace('<!--preload-links-->', preloadLinks)...复制代码给 index.html 添加 <!--pinia-state--> 标记

<script> window.__INITIAL_STATE__ = '<!--pinia-state-->';</script>复制代码运行 pnpm run dev 就可正常显示了

备注:集成 pinia 这块由于注入较为繁芜且方法不一,暂时不做详细讲解,如果大家有须要,后面会出详细解析!

十、其它vueuse 的集成:可参考 《手把手教你用 vite+vue3+ts+pinia+vueuse 打造大厂企业级前端项目》CSS 集成:参考如上可利用:原生 css variable 新特性、scss 或者 lessCSS 的 UI 库:参考同上须要把稳的是 按需引入当然还有很多须要考量的,比如 压测, 并发 , 负载均衡 等,但是这些不在文章主题范围内,这里就不做详细先容,感兴趣的可以留言,后面有韶光会新开对应的专栏个中 负载均衡 这块前端同学可利用 pm2, 或者直接丢给运维去搞个 docker项目模板地址

传送门

末了

交情提示:目前 Vite 的 SSR 支持还处于试验阶段,可能会碰着一些未知 bug ,以是在公司的生产环境请谨慎利用,个人项目中可以滥用哟 ~

该系列会是一个持续更新系列,关于全体《Vite 从入门到精通》专栏,我紧张会从如下图几个方面讲解,请大家拭目以待吧!


靓仔靓女们,都看到这里了,要不点个赞再走呗

作者:易师傅

链接:https://juejin.cn/post/7086467466703929358

标签:

相关文章

介绍百度网盘,云端存储时代的创新先锋

随着互联网技术的飞速发展,云计算已经成为现代生活不可或缺的一部分。而在这其中,百度网盘作为国内领先的云存储服务提供商,以其卓越的性...

SEO优化 2025-01-03 阅读3 评论0

介绍监控屏蔽技术,守护个人隐私的利器

随着科技的发展,监控设备已经深入到我们生活的方方面面。在享受便利的隐私安全问题也日益凸显。如何有效屏蔽监控,保护个人隐私,成为人们...

SEO优化 2025-01-03 阅读3 评论0

介绍番号观看方法,轻松驾驭影视世界

随着互联网的普及,网络影视资源日益丰富,番号作为影视作品的标识码,已经成为广大观众了解、搜索和观看影视作品的重要途径。如何正确地使...

SEO优化 2025-01-03 阅读1 评论0

介绍盗微信号黑幕,网络安全的严峻挑战

在数字化时代,微信已成为人们生活中不可或缺的通讯工具。随着微信用户数量的激增,盗微信号的事件也日益增多。本文将深入剖析盗微信号的方...

SEO优化 2025-01-03 阅读1 评论0