Web单页应用的渲染模式
用JS渲染的单页面应用其实性能还是比较差的
证明这个结论之前,要先阐述一下浏览器的渲染机制,这里先祭出这篇文章:《关键呈现路径》,文章主要介绍了浏览器渲染过程,其实大家也大概都了解过:
如上图,浏览器通过网络请求加载页面资源,在页面呈现之前无论如何都要经历以下过程:
HTML→DOM
CSS→CSSOM
DOM + CSSOM → Render Tree
对Render Tree进行布局计算(Layout)
对布局结果进行屏幕绘制(Paint)
如果在JS渲染页面模式下,需要在前端用JS加载样式并组装数据生成HTML插入页面,以上浏览器渲染过程必须等到页面加载完CSS,并且JS加载完数据拼装好HTML之后才能开始进行,一般的网络时序如下:
大概阐述一下这个流程:
浏览器发起请求加载主文档
服务端响应一个基本骨架的主文档
浏览器加载主文档中外链的loader.js(根据路由控制资源加载的)
服务端响应loader.js
loader.js执行,根据页面url判断用户访问到哪个虚拟页面,然后再发起请求加载对应页面的js和css
页面所需JS和CSS都加载完毕,JS执行,发起请求加载数据
数据加载完毕,JS执行前端模板拼装,插入DOM节点,然后浏览器开始前述渲染过程
最终页面呈现
概括一下,加载时序大概是这样的:
以上加载过程均为串行,需要至少多付出3次RTT。如果把这种架构应用在高延迟的网络环境下(比如移动2G),那就是找死啊(其实国内现在的网络环境很好了,这样搞问题或许不太明显)。
当然,上面的例子还是常规了一些,有些请求可以适当合并,进一步优化之后,大概可以搞成这个样子:
就是首次请求的主文档尽量多内嵌一些东西,除了HTML骨架之外,把loader.js内嵌,再加一个loading界面,让用户觉得没那么长时间白屏,另外如果前端路由切换是pusState控制的话,可以在服务端知道前端路由url,然后在主文档中直接内嵌数据,主文档体积大了不少,但是可以减少2次RTT,优化对比:
当然,如果你的单页面应用体量很小,完全不用按需加载,主文档内嵌一切可以再减少一次RTT,得到:
不过这么极端的做法其限制就是:你的应用千万不能太大!
前端渲染模式我厂的代表产品:UC奇趣百科 ,其优化点:
主文档loader.js内嵌、数据内嵌、loading界面内嵌
页面资源按需加载,请求动态合并
localstorage存储JS/CSS
在国内的网络环境下感觉还OK吧。。。
兼顾性能、兼顾SEO,还是单页面应用,是可以做到的!
很明显,前端JS渲染由于违背了浏览器的优化策略,总是存在一个不可突破的瓶颈:
JS和数据没加载完,JS拼装数据的逻辑没执行完,浏览器不能开始正常的渲染流程。
这个性能差异我感觉短时间内这种JS渲染的WebApp是无法跟传统页面输出模式相比较的,因为浏览器的各种渲染优化策略基本上都是围绕着传统页面时序展开的。有没有办法突破这个性能瓶颈,并且兼顾SEO,但还保留单页面应用的体验呢?
答案是:有办法。
有人或许会想到 Isomorphic Javascript,所谓的同构JavaScript,或者什么前后端模板复用,相信我,这个概念根本就是扯淡!
其实办法很简单,根本用不着同构JS,页面还是服务端拼装好的,CSS在head中,主文档是完整的HTML,JS在body尾部;但需要在后端模板中实现一种功能:允许通过特殊的ajax请求以json格式响应页面中的局部区域。这项技术被称为 Quickling。
此外,单页面应用还有一项优化手段,叫PageCache,前端控制页面切换时,把之前的页面缓存到内存中,下次再回到这个页面就直接展现,不用再次请求数据拼装模板渲染,进一步优化用户在站内浏览的体验。
基于Quickling和PageCache我们在印度市场(网络环境超差)实现了两个单页面应用产品:YoloSong 和Huntnews ,其优化点:
首次访问服务端渲染,页面间Quickling切换,单页面体验
所有链接可爬取,解决SEO问题
PageCache缓存已访问页面,加速切换,历史记录前进后退
可 全站禁用JS,不影响浏览体验
按需加载,请求合并