离线预渲染OPR:0成本接入 媲美SSR效果

栏目: IT技术 · 发布时间: 4年前

内容简介:关注我们作者简介

关注我们 文末有福利

离线预渲染OPR:0成本接入 媲美SSR效果

作者简介

离线预渲染OPR:0成本接入 媲美SSR效果

张所勇

转转平台运营中心前端负责人,在前端领域有深入研究,包括:sketch一键切图、前端数据模型化,小程序基础能力建设等多个方面,10年工作经验中,做了2年工程师,5年CEO,3年技术管理,能写点文章,也是2018年度掘金优秀作者。

细数现阶段业内首屏优化方案,主要有:SSR、Prerender、CSR等方案,这些方案的思路几乎都在于将渲染过程放到传统SPA用户端渲染之前,而传统性能优化手段在SPA项目上面收获甚微,原因在于SPA本身的致命缺陷:

SPA方案

在SPA项目的首屏性能上,我们在长期关注和不断探索,期间我们尝试过很多方案,包括:

  • 从减少代码体积角度的:webpack优化、打包优化、tree-shaking等

  • 从减少HTTP请求角度的:接口合并、按需加载、延时加载等各种方法减少请求

  • 从缓存角度出发:离线包、http&浏览器各种缓存使用、dns预解析、dll方案、接口缓存方案等

  • 从数据获取时机角度出发:webWorker预取数据、路由进入过程读取数据等

  • 从减少图片体积和数量出发:使用webp图片、请求域名并行优化、CSS Sprite等

这些方案都能一定程度上降低白屏时间和首屏时间,但收效有限,很难像SSR方案一样大幅降低数据,究其原因,SPA页面渲染过程如下:

离线预渲染OPR:0成本接入 媲美SSR效果

滑动查看图片

从上图可以看到,白屏过程几乎是不可避免的,因为无论如何你去优化代码体积,Vue系列类库和你需要的其他核心类库文件加起来至少有几百K,在加上这些文件执行的时间(实测至少500ms),可能大多数情况,我们白屏时间至少1200ms-1500ms了。

当然,我们可以把骨架屏所需的css放到HTML里面,能尽早的显示出骨架屏(但很多低版本内核需下载&执行完全部script后才会渲染页面),但这并非真正的首屏,即使在性能统计上,也无法直观反馈出首屏的提升。

于是SSR方案成为我们的救命稻草:

SSR方案

我们再看下SSR如何解决这个问题:

离线预渲染OPR:0成本接入 媲美SSR效果

滑动查看图片

SSR方案的优势在于,浏览器下载的HTML当中已经具备了首屏渲染所需的DOM结构和样式,白屏时间几乎等于HTML文件下载时间,而这个时间相比SPA已经很少了,性能数据有显著提升。

那为什么我们不直接用SSR方案呢?

主要原因有四点:

1、SSR项目改造成本高

Vue技术栈的SSR方案主流有两种:官方方案和Nuxt.js,这两种方案相同点都是:

  • 必须把现有webpack各项配置替换成上述两种方案工程

  • 工程所有页面都必须SSR方式的要求实现

  • 必须在自定义的asyncData/preFetch生命周期内获取数据

  • 必须将接口数据使用Vuex管理

或许你认为这个也不难啊,对于一个新项目,确实不难,但对一个老项目来讲,上述的改造成本和测试成本就无比高了,这也是少有老项目改造SSR的原因。

2、SSR性能依赖接口性能

从SSR原理上你可以知道,SSR服务端渲染过程依赖于获取到全部数据才能开始渲染,一旦接口出现延时或超时,那首屏性能也会受到影响。

3、SSR负载能力和扩容能力可能成为瓶颈

几乎是业界公认,node的负载能力相比 java 等要差一些,相比nginx静态资源服务更差,并且很多公司在node服务器快速扩容上面,目前还没有太多实践和机制保障,虽然可以通过备足服务器来抵抗流量高峰,但毕竟这对应的是成本。

4、SSR无降级方案

一旦node服务故障,页面可能直接就会白屏,很多时候不是重启服务能够解决的,毕竟SSR不是像SPA一样在浏览器看见什么错误去解决或者回滚就可以的,你必须真正解决了故障才能恢复服务,这期间不能很容易的降级为SPA方案。

上述原因当中,最主要阻碍我们用SSR的原因是改造成本。

Prerender方案

Prerender是基于prerender-spa-plugin这个webpack插件实现的,原理如下:

离线预渲染OPR:0成本接入 媲美SSR效果

滑动查看图片

核心原理就是在webpack打包过程中,通过Puppeteer访问对应路由,抓取html并静态化,再部署cdn。

但业内这种方案使用的比较少,主要原因有:

  • 静态化过程发生在构建环节,用户访问时看到的数据注定是过时的。

  • 这种方案依赖于使用history方式的路由,这对老项目的改造测试成本也不低。

  • 编译时间大幅增加,想想就知道啦。

通过上述分析,我们能看出“最优方案”应该是SSR,不考虑负载能力的话,阻碍我们的只有改造成本了,能否用较低的成本实现跟SSR一样的效果呢?

离线预渲染OPR

晴空一声惊雷,OPR产生了,我们把他命名为离线预渲染OPR(Offline Prerender)。

OPR的渲染过程:

离线预渲染OPR:0成本接入 媲美SSR效果

滑动查看图片

不同于SSR在用户访问阶段的渲染,OPR是一个独立于用户访问流程的渲染服务,它通过Puppeteer定期渲染页面并上传cdn,用户访问到的页面将会是纯静态页面,可以说是结合了SSR和Prerender两种方案。

与SSR方案的区别:

  1. 渲染过程独立于用户访问,没有服务器压力,占用资源极小,一台服务器即可完成

  2. 页面几乎不需要任何改动

  3. 渲染出来的页面效果几乎和SSR一致

  4. 可降级为SPA方案

与Prerender方案的区别:

  1. 通过定时渲染,解决Prerender方案数据无法及时更新的问题

  2. 页面几乎不需要任何改动

  3. 对原本项目构架过程无任何影响

OPR方案实现过程

我们简单拆解来看:

01

定时访问页面

我们首先搭建一个node服务,通过schedule机制定期通过Puppeteer访问需要渲染的页面。

02

等待页面渲染

页面渲染是一个动态的过程,我们如何知道页面已经渲染完了呢,Puppeteer其实提供多种方案,但我们最终选用的方案是通过监听公司性能统计埋点发出时机,通过Puppeteer的page.waitForRequest方法可以很容易实现。

03

抓取HTML

你必须清楚一点:我们抓取的是浏览器渲染的HTML,并非你请求到index.html文件内容。

前者你可以理解为,通过浏览器开发者工具,选中html标签,右键拷贝outerHTML。

后者你可以通过浏览器查看下html源码,里面应该只有空白的dom和一些

关注 码农网 公众号