这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战
TIP 👉 英雄者,胸怀大志,腹有良策,有包藏宇宙之机,吞吐天地之志者也。——《三国演义》
前言
SSR
CSR VS SSR
首先让我们看看 CSR 的过程(划重点,浏览器渲染原理基本流程)
过程如下:
- 浏览器通过请求得到一个
HTML
文本 - 渲染进程解析
HTML
文本,构建DOM
树 - 解析
HTML
的同时,如果遇到内联样式或者样式脚本,则下载并构建样式规则(stytle rules
),若遇到JavaScrip
t脚本,则会下载执行脚本。 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
优化方案:
- 页面级别的缓存,比如
nginx
micro-caching
- 设置
serverCacheKey
,如果相同,将使用缓存,组件级别的缓存 CGI
缓存,通过memcache
等,将相同的数据返回缓存一下,注意设置缓存更新机制- 流式传输,但是必须在
asyncData
之后,否则没有数据,说明也可能会被CGI
耗时阻塞 - 分块传输,这样前置的
CGI
完成就会渲染输出,但是这个方案难啊 - JSC,就是不用
vue-loader
「欢迎在评论区讨论」