文章目录
- CSR 优缺点
- SSR
- Server + Client 同构
- Hydrate 水合(客户端激活)
- 数据的获取和初始化
- 预加载资源
- 避免应用单例
- 避免全局副作用代码
CSR 优缺点
优点
- 整个网站打包进 JavaScript 里,当 JavaScript 下载完毕后,相当于网站的页面资源都被下载好了。这样在跳转新页面的时候,不需要向服务器再次请求资源( JavaScript 会直接操作 DOM 进行页面渲染),从而让整个网站的使用体验上更加流畅
缺点
- 在 JavaScript 体积较大的情况下,会有白屏问题
- 因为会先下载一个空的 HTML,然后才通过 JavaScript 进行渲染,这个空的 HTML 会导致某些搜索引擎无法通过爬虫正确获取网站信息,从而影响网站的搜索引擎排名
SSR
优点
- HTML 在服务器端就已经渲染好了,浏览器拿到就可以渲染,减少白屏时间
- 服务器端渲染拥有良好的首屏性能和 SEO
缺点
- 每次跳转页面都要向服务器重新请求,意味着用户每次切换页面都要等待一小段时间
- SSR 相比 CSR 会占用较多的服务器端资源
// 以 vue 为例
import { renderToString } from 'vue/server-renderer'
import { createSSRApp } from 'vue'// 一个计数的 vue 组件
function createApp() {// 通过 createSSRApp 创建一个vue实例return createSSRApp({data: () => ({ count: 1 }),template: `<button @click="count++">{{ count }}</button>`,});
}const app = createApp();// 通过 renderToString 将 vue 实例渲染成字符串
renderToString(app).then((html) => {// 将字符串插入到 html 模板中const htmlStr = `<!DOCTYPE html><html><head><title>Vue SSR Example</title></head><body><div id="app">${html}</div></body></html>`;console.log(htmlStr);
});
Server + Client 同构
- 开始的步骤和 SSR 相同,将生成的 HTML 字符串返回给客户端,同时将 CSR 需要的 JavaScript 也一并发送给客户端
- 客户端在接收到 SSR 生成的 HTML 后,页面还会再执行一次 CSR 的流程
- 客户端只有请求的第一个页面是在服务器端渲染的,其它页面则都是在客户端进行的
- 这样就同时兼顾首屏、SEO和用户体验的网站
Hydrate 水合(客户端激活)
- 服务器执行应用的初始渲染,生成静态 HTML,并将其发送给客户端,这一步其实发送的是静态的模版( Dehydrate 脱水)
- 客户端加载额外的 JavaScript 代码,并在已有的静态 HTML 上绑定事件监听器等,使页面变得可交互
- SSR 的瓶颈也就取决于 Hydrate 的过程
数据的获取和初始化
- 挂载到 window 上或者 Vuex、Pinia 等其它方案
const htmlStr = `<!DOCTYPE html><html><head>...// 将数据格式化成json字符串,放到script标签中<script>window.__INITIAL_DATA__ = ${JSON.stringify(initData)}</script></head>...</html>
`;
组件中获取数据
function createApp() {return createSSRApp({data: () => ({ count: 1 }),template: `<button @click="count++">{{ count }}</button>`,// 自定义一个名为 asyncData 的函数asyncData: async () => { // 在处理远程数据并 return 出去const data = await getSomeData()return data; },async mounted() {// 如果已经有数据了,直接从 window 中获取if (window.__INITIAL_DATA__) {// 有服务端数据时,使用服务端渲染时的数据this.count = window.__INITIAL_DATA__;window.__INITIAL_DATA__ = undefined;return;} else {// 如果没有数据,就请求数据this.count = await getSomeData();}}});
}
预加载资源
- 在打包过程中生成 manifest
- 作用是将打包后的模块 ID 与它们关联的 Chunk 和资源文件进行映射
- 依靠这个 manifest 获取资源的路径,然后创建 Link 标签拼接到 HTML 模板中即可
避免应用单例
- 服务器端返回给客户端的每个请求都应该是全新的、独立的应用程序实例,避免直接将对象或变量创建在全局作用域,否则它将在所有请求之间共享,在不同请求之间造成状态污染
避免全局副作用代码
- 比如 vue 服务器端渲染只会执行 beforeCreate 和 created 生命周期,应该避免在这两个生命周期里产生全局副作用的代码
- 例如使用 setInterval 设置定时器。在纯客户端的代码中,我们可以设置一个定时器,然后在 beforeDestroy 或 destroyed 生命周期中将其销毁。但是在 SSR 期间并不会调用销毁钩子函数,所以定时器将永远保留下来,最终造成服务器内存溢出