Vue框架使用SSR

Vue 同构渲染

Vue.js 是一个构建客户端应用的框架,组件的代码会在浏览器中运行,然后向页面输出 DOM 元素,也就是我们最常用的方式,即 客户端渲染(client-side rendering,CSR) . 实际上 Vue.js 还可以在 Node.js 环境中运行,即可将相同组件渲染成相应的字符串,并发送给浏览器进行渲染,这就是 服务端渲染(server-side rendering,SSR) . Vue.js 作为现代前端框架,除了能够分别支持 CSR 或 SSR 渲染之外,还能够同时支持 CSR 和 SSR,这就是所谓的 同构渲染(isomorphic rendering).

客户端渲染 CSR

  • CSR 优点
    • 对于 SPA 应用,跳转之前没有卡断和刷新,体验会比较好。基于前端路由的方式不会导致真正的页面跳转,带来了使用过程中的流畅度。
    • 占用的服务端压力较少。CRS 的渲染是交给客户端额,服务端不用关注页面的渲染和计算过程,只需要关注数据。
  • CSR 缺陷
    • 白屏时间会很长。因为 CSR 渲染需要.js 的支持,js 如果很大,请求和执行都很耗时间,如果用户网络环境较差,就会导致很长的白屏时间。
    • 对 SEO 的支持不友好。因为白屏时间较长,也会导致在一段时间内没有重要的内容交给搜索引擎进行分类、打标等,并且搜索引擎并不会等待页面渲染完成,因此对 SEO 优化很不友好。

服务端渲染 SSR

渲染流程

  • 搭建 node 服务
    • Node.js 服务器是长期运行的进程,当代码第一次被导入进程时,它会被执行一次然后保留在内存里
    • 如果只创建了一个 Vue 的单例对象,它将被每次发来的请求共享,这是不符合实际需求的,因此我们需要为每个请求重新生成一个 vue 实例,避免相互影响
  • SSR 优势
    • 首屏渲染速度快,用户体验好,不存在白屏时间过长的问题
    • SEO 友好,搜索引擎可以爬取到完整的 html 内容,也不存在异步的接口调用
  • SSR 劣势
    • 需要保障服务端和客户端的代码一致性,否则会出现不一致的情况。(比如:路由,vuex 等等)
    • 保证开发一致性:浏览器特有的代码只能在特定的生命周期钩子中使用,一些外部库在服务端渲染时可能需要特殊处理
    • 提供稳定的能够支持高并发的服务器环境,需要考虑服务器的负载,内存,CPU,带宽等
    • 更多的服务端荷载,需要考虑服务端渲染的优化,比如:缓存,压缩,静态资源分离等
      • 更多的服务端负载:在 Node.js 中渲染一个完整的应用,需要更密集的 CPU 运算,如果访问的流量很高,我们需要考虑负载均衡、合理的缓存策略、容错机制
  • SSR 和 预渲染
    • 预渲染可以在构建的时为指定的路由生成静态的 HTML 文件,预渲染需要和打包构建工具(webpack、Vite......)配合,我们使用静态工具打包出 HTML 后,发布到 CDN 上即可,用户访问时直接访问的是静态的 HTML 文件,无需经过服务器渲染,所以首屏加载速度非常快,但是预渲染无法处理异步数据,所以预渲染一般用于静态的页面,比如:博客,文档等

同构渲染(isomorphic rendering)

壹. 同构流程

  • 服务端渲染应用快照 (静态 HTML 页面)
    • 生成快照的同时,还会当成当前数据状态的初始数据,给客户端做初始化处理
    • 应用快照不具备交互能力,即定义好的事件不会注册到 DOM 上,需要在客户端激活
  • 客户端激活
    • 把当前页面已经渲染的 DOM 元素和 Vue.js 渲染的虚拟 DOM 建立联系
    • 由于真实 DOM 和虚拟 DOM 都是树形结构,并且节点之间存在相互关系的,激活就可以通过递归在 DOM 和虚拟 DOM 之间建立联系。即 vnode.el = el,并且保证是从容器元素的第一个子节点开始,即 el.firstChild
    • 为页面中 DOM 元素添加事件绑定,使页面具备交互能力
    • Vue.js 从 HTML 中提取服务端序列化后发送过来的数据,用于初始化整个 Vue.js 的应用

同构渲染就是一套代码,既可以在服务端执行,也可以在客户端执行,从而在服务端生成 html,在客户端执行 js,从而实现首屏渲染。

importmap

<script> 元素的 type 属性的 importmap 值表示元素的主体包含一个导入映射。

贰. 同构案例

  1. 服务器端渲染 HTML 字符串
  2. 通过服务器发送 HTML 字符串
  3. 激活客户端渲染

叁. 实现脱水(Dehydrate)和注水(Hydrate)

  • 实现服务器端脱水
    • 服务器端获取到数据后,把数据跟随 HTML 一起传给客户端的过程
    • 脱水:把 Vue.js 的状态数据序列化成字符串,发送给客户端
  • 实现客户端注水
    • 客户端拿到 HTML 和数据,利用这个数据来初始化组件
    • 注水:客户端接收到脱水后的数据,把数据注入到 Vue.js 中,从而实现客户端的激活

肆. 同构渲染要点

  • 避免状态单例,也就是避免将对象或变量创建在全局作用域中,否则它会在所有请求之间共享,在不同请求中造成状态污染,导致服务端和客户端状态不一致
  • 避免访问特定平台的 API,比如:window、document、localStorage 等,这些 API 只能在浏览器环境中使用,在服务端渲染时会导致错误
  • node 端和浏览器端 API 不适配,推荐将操作 DOM 或者访问 window 等浏览器行为,写在 onMounted 生命周期中,这样就可以避免在 node 端访问浏览器 API 导致报错
js 复制代码
// 使用环境变量
// Vite 内
const isSSR = import.meta.env.SSR;
if (isSSR) {
	// 服务端渲染,仅在服务端执行的逻辑
} else {
	// 客户端渲染,仅在客户端执行的逻辑
}
  • 避免在服务端生命周期内执行全局副作用代码,比如 setInterval。因为服务端不会执行 destroy 的销毁 hook,会导致服务器内存溢出

伍. 创建实际生产中的同构应用

  • 集成前端工具链
  • 集成前端路由
    • 使用 createMemoryHistory 创建内存路由
    • 在服务端获取到用户请求的路径后,把路径传入 router.push 函数,使用 router.isReady() await 这个函数后,再渲染整个应用
  • 集成全局状态管理库
    • 需要确定你使用的状态管理工具支持 SSR
  • 处理#app节点之外的元素
  • 处理预加载资源

陆. 服务器端优化

  1. 服务器端测试
  • 我们可以通过 apach bench, jmeter 等
  • abs -n 1000 -c 100 http://localhost:3000/ ,表示以 100 并发的形式发送了 1000 个请求到 localhost: 3000
  • 我们可以接入 nodejs 的监控告警,保证服务稳定性
  1. 多进程优化
js 复制代码
const cluster = require("cluster");
// cpu总数
const numCPUs = require("os").cpus().length;
module.exports = (task) => {
	if (cluster.isMaster) {
		for (let i = 0; i < numCPUS / 2; i++) {
			const worker = cluster.fork();
		}
	} else {
		task();
	}
};
  1. 内存溢出处理
  • 我们可以通过 process 判断当前子进程用掉的内存,当占用内存大于阈值的时候,就关掉这个子进程,防止内存泄漏
js 复制代码
const timer = setInterVal(() => {
	const mem = process.memoryUsage();
	if (mem.rss > 300 * 1024 * 1024) {
		cleaterVal(timer);
		process.exit(1);
	}
}, 5000);
  1. 处理未捕获异常
js 复制代码
process.on("oncaughtException", (err) => {
	// 需要对错误进行日志上报,写入日志,比如sentry
	process.exit(1);
});
  1. 心跳包检测
  • 防止子进程卡死
    • 主进程通过 worker.send 给子进程发送消息
    • 子进程通过 process.on('messgae', () => {})订阅主进程发送的消息,然后通过 process.send 返回给主进程信息
    • 主进程通过 worker.on('message', () => {})订阅子进程发送的信息,如果累计一定次数没有收到子进程返回的信息,则关闭子进程
  1. 子进程自动重建
js 复制代码
cluster.on("exit", () => {
	// 创建新的子进程
	setTimeout(() => {
		createWorker();
	});
});

使用 PM2 部署我们的 SSR 应用 然后使用同构应用框架进行开发

相关推荐
yuren_xia3 小时前
Spring Boot中保存前端上传的图片
前端·spring boot·后端
普通网友4 小时前
Web前端常用面试题,九年程序人生 工作总结,Web开发必看
前端·程序人生·职场和发展
站在风口的猪11085 小时前
《前端面试题:CSS对浏览器兼容性》
前端·css·html·css3·html5
JohnYan6 小时前
Bun技术评估 - 04 HTTP Client
javascript·后端·bun
青莳吖7 小时前
使用 SseEmitter 实现 Spring Boot 后端的流式传输和前端的数据接收
前端·spring boot·后端
CodeCraft Studio7 小时前
PDF处理控件Aspose.PDF教程:在 C# 中更改 PDF 页面大小
前端·pdf·c#
拉不动的猪8 小时前
TS常规面试题1
前端·javascript·面试
再学一点就睡8 小时前
实用为王!前端日常工具清单(调试 / 开发 / 协作工具全梳理)
前端·资讯·如何当个好爸爸
穗余8 小时前
NodeJS全栈开发面试题讲解——P5前端能力(React/Vue + API调用)
javascript·vue.js·react.js
Jadon_z8 小时前
vue2 项目中 npm run dev 运行98% after emitting CopyPlugin 卡死
前端·npm