Vue SSR

769 阅读5分钟

这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战

TIP 👉 英雄者,胸怀大志,腹有良策,有包藏宇宙之机,吞吐天地之志者也。——《三国演义》

前言

SSR

CSR VS SSR

首先让我们看看 CSR 的过程(划重点,浏览器渲染原理基本流程)

csr

过程如下:

  1. 浏览器通过请求得到一个HTML文本
  2. 渲染进程解析HTML文本,构建DOM
  3. 解析HTML的同时,如果遇到内联样式或者样式脚本,则下载并构建样式规则(stytle rules),若遇到JavaScript脚本,则会下载执行脚本。
  4. DOM树和样式规则构建完成之后,渲染进程将两者合并成渲染树(render tree
  5. 渲染进程开始对渲染树进行布局,生成布局树(layout tree
  6. 渲染进程对布局树进行绘制,生成绘制记录
  7. 渲染进程的对布局树进行分层,分别栅格化每一层,并得到合成帧
  8. 渲染进程将合成帧信息发送给GPU进程显示到页面中

很容易发现,CSR 的特点就是会在浏览器端的运行时去动态的渲染、更新 DOM 节点,特别是 SPA 应用来说,其模版 HTML 只有一个 DIV,然后是运行时(ReactVueSvelte 等)动态的往里插入内容,这样的话各种 BaiduSpider 拿不到啥有效信息,自然 SEO 就不好了,项目一旦复杂起来, bundle 可能超乎寻常的大...这也是一个开销

那么SSR 呢,则是是服务端完成了渲染过程,将渲染完成的 HTML 字符串或者流返回给浏览器,就少了脚本解析、运行这一环节,理论上 FP 表现的更佳,SEO 同样

csr vs ssr

但其实,现在 SSR 也并没有大行其道,凡事有利有弊,SSR 也是有缺点的

  1. 复杂,同构项目的代码复杂度直线上升,因为要兼容两种环境
  2. 对服务端的开销大,既然 HTML 都是拼接好的,那么传输的数据肯定就大多了,同时,拿 Node 举例,在处理 Computed 密集型逻辑的时候是阻塞的,不得不上负载均衡、缓存策略等来提升
  3. CI/CD 更麻烦了,需要在一个 Server 环境,比如 Node

一般来说,ToB 的业务场景基本不需要 SSR,需要 SSR 的一定是对首屏或者 SEO 有强诉求的,不然没必要搞那么麻烦,简洁是避免麻烦的最佳实践,同时,随着浏览器发展,越来越快,爬虫也越来越智能,SSR 的场景在被压缩

彩蛋,这里说到了 CSRSSR ,其实我们现今常见的渲染方案有6-7种吧!

render

注意,这里提到了 hydration 这个词,这是一个很棒的思路,对 FP 有帮助,但是不能提升 TTI

同构应用

我们以上面的指南为基础讲讲同构应用(因为同构应用算是比较复杂的了),通过同构应用让大家对 SSR 有一个更直观、立体的认识

首先需要了解什么是同构应用

一份代码,既可以客户端渲染,也可以服务端渲染

看看客户端渲染,对我们而言,基本可以这样概括:页面 = 模版 + 数据,应用 = 路由 + 页面

所以,同构,我们需要注意的是构了个啥?,就是 路由模版数据

同构

假定大家已经认真阅读并实际操练了 VUE SSR 指南,

现在就一些实践经验做一些补充:

  1. 服务端的 webpack 不用关注 CSS,客户端会打包出来的,到时候推 CDN,然后改一下 public path 就好了

  2. 服务端的代码不需要分 chunkNode 基于内存一次性读取反而更高效

  3. 如果有一些方法需要在特定的环境执行,比如客户端环境中上报日志,可以利用 beforeMouted 之后的生命周期都不会在服务端执行这一特点,当然也可以使用 isBrowser 这种判断

  4. CSRSSR 的切换和降级

    // 总有一些奇奇怪怪的场景,比如就只需要 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')
      }
    }
    
  5. Axios 封装,至少区分环境,在客户端环境是需要做代理的

这里的最佳实践知识抛砖引玉,还是得自己去踩坑总结

VUE-SSR 优化方案:

  1. 页面级别的缓存,比如 nginx micro-caching
  2. 设置 serverCacheKey,如果相同,将使用缓存,组件级别的缓存
  3. CGI 缓存,通过 memcache 等,将相同的数据返回缓存一下,注意设置缓存更新机制
  4. 流式传输,但是必须在 asyncData 之后,否则没有数据,说明也可能会被 CGI 耗时阻塞
  5. 分块传输,这样前置的 CGI 完成就会渲染输出,但是这个方案难啊
  6. JSC,就是不用 vue-loader

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下