这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战
TIP 👉 英雄者,胸怀大志,腹有良策,有包藏宇宙之机,吞吐天地之志者也。——《三国演义》
前言
SSR
CSR VS SSR
首先让我们看看 CSR 的过程(划重点,浏览器渲染原理基本流程)
过程如下:
- 浏览器通过请求得到一个
HTML文本 - 渲染进程解析
HTML文本,构建DOM树 - 解析
HTML的同时,如果遇到内联样式或者样式脚本,则下载并构建样式规则(stytle rules),若遇到JavaScript脚本,则会下载执行脚本。 DOM树和样式规则构建完成之后,渲染进程将两者合并成渲染树(render tree)- 渲染进程开始对渲染树进行布局,生成布局树(
layout tree) - 渲染进程对布局树进行绘制,生成绘制记录
- 渲染进程的对布局树进行分层,分别栅格化每一层,并得到合成帧
- 渲染进程将合成帧信息发送给
GPU进程显示到页面中
很容易发现,CSR 的特点就是会在浏览器端的运行时去动态的渲染、更新 DOM 节点,特别是 SPA 应用来说,其模版 HTML 只有一个 DIV,然后是运行时(React,Vue,Svelte 等)动态的往里插入内容,这样的话各种 BaiduSpider 拿不到啥有效信息,自然 SEO 就不好了,项目一旦复杂起来, bundle 可能超乎寻常的大...这也是一个开销
那么SSR 呢,则是是服务端完成了渲染过程,将渲染完成的 HTML 字符串或者流返回给浏览器,就少了脚本解析、运行这一环节,理论上 FP 表现的更佳,SEO 同样

但其实,现在 SSR 也并没有大行其道,凡事有利有弊,SSR 也是有缺点的
- 复杂,同构项目的代码复杂度直线上升,因为要兼容两种环境
- 对服务端的开销大,既然
HTML都是拼接好的,那么传输的数据肯定就大多了,同时,拿Node举例,在处理Computed密集型逻辑的时候是阻塞的,不得不上负载均衡、缓存策略等来提升 - CI/CD 更麻烦了,需要在一个
Server环境,比如Node
一般来说,ToB 的业务场景基本不需要 SSR,需要 SSR 的一定是对首屏或者 SEO 有强诉求的,不然没必要搞那么麻烦,简洁是避免麻烦的最佳实践,同时,随着浏览器发展,越来越快,爬虫也越来越智能,SSR 的场景在被压缩
彩蛋,这里说到了 CSR 和 SSR ,其实我们现今常见的渲染方案有6-7种吧!
注意,这里提到了 hydration 这个词,这是一个很棒的思路,对 FP 有帮助,但是不能提升 TTI
同构应用
我们以上面的指南为基础讲讲同构应用(因为同构应用算是比较复杂的了),通过同构应用让大家对 SSR 有一个更直观、立体的认识
首先需要了解什么是同构应用
一份代码,既可以客户端渲染,也可以服务端渲染
看看客户端渲染,对我们而言,基本可以这样概括:页面 = 模版 + 数据,应用 = 路由 + 页面
所以,同构,我们需要注意的是构了个啥?,就是 路由、模版、数据
假定大家已经认真阅读并实际操练了 VUE SSR 指南,
现在就一些实践经验做一些补充:
-
服务端的
webpack不用关注CSS,客户端会打包出来的,到时候推CDN,然后改一下public path就好了 -
服务端的代码不需要分
chunk,Node基于内存一次性读取反而更高效 -
如果有一些方法需要在特定的环境执行,比如客户端环境中上报日志,可以利用
beforeMouted之后的生命周期都不会在服务端执行这一特点,当然也可以使用isBrowser这种判断 -
CSR和SSR的切换和降级// 总有一些奇奇怪怪的场景,比如就只需要 CSR,不需要 SSR // 或者在 SSR 渲染的时候出错了,页面最好不要崩溃啊,可以降级成 CSR 渲染,保证页面能够出来 // 互相切换的话,总得有个标识是吧,告诉我用 CSR 还是 SSR // search 就不错,/demo?ssr=true module.exports = function(req, res) { if(req.query.ssr === 'true'){ const context = { url: req.url } renderer.renderToString(context, (err, html) => { if(err){ res.render('demo') // views 文件下的 demo.html } res.end(html) }) } else { res.render('demo') } } -
Axios封装,至少区分环境,在客户端环境是需要做代理的
这里的最佳实践知识抛砖引玉,还是得自己去踩坑总结
VUE-SSR 优化方案:
- 页面级别的缓存,比如
nginxmicro-caching - 设置
serverCacheKey,如果相同,将使用缓存,组件级别的缓存 CGI缓存,通过memcache等,将相同的数据返回缓存一下,注意设置缓存更新机制- 流式传输,但是必须在
asyncData之后,否则没有数据,说明也可能会被CGI耗时阻塞 - 分块传输,这样前置的
CGI完成就会渲染输出,但是这个方案难啊 - JSC,就是不用
vue-loader
「欢迎在评论区讨论」