用JS渲染的单页面应用其实性能还是比较差的
2016-04-16 09:43
671 查看
https://github.com/xufei/blog/issues/5
证明这个结论之前,要先阐述一下浏览器的渲染机制,这里先祭出这篇文章:《关键呈现路径》,文章主要介绍了浏览器渲染过程,其实大家也大概都了解过:
如上图,浏览器通过网络请求加载页面资源,在页面呈现之前无论如何都要经历以下过程:
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吧。。。
JS和数据没加载完,JS拼装数据的逻辑没执行完,浏览器不能开始正常的渲染流程。
这个性能差异我感觉短时间内这种JS渲染的webapp是无法跟传统页面输出模式相比较的,因为浏览器的各种渲染优化策略基本上都是围绕着传统页面时序展开的。有没有办法突破这个性能瓶颈,并且兼顾SEO,但还保留单页面应用的体验呢?
答案是:有办法。
有人或许会想到
Isomorphic Javascript,所谓的同构JavaScript(这个有机会另外讨论),采用同构JavaScript需要一定的架构支持,需要能解决后端渲染的组件反射问题,目前仅 react 或 类react框架才支持。
除了同构JS,还有一种方案,也比较简单,页面还是服务端拼装好的,CSS在head中,主文档是完整的HTML,JS在body尾部;但需要在后端模板中实现一种功能:允许通过特殊的ajax请求以json格式响应页面中的局部区域。这项技术被称为
Quickling。
此外,单页面应用还有一项优化手段,叫PageCache,前端控制页面切换时,把之前的页面缓存到内存中,下次再回到这个页面就直接展现,不用再次请求数据拼装模板渲染,进一步优化用户在站内浏览的体验。
基于Quickling和PageCache我们在印度市场(网络环境超差)实现了两个单页面应用产品:YoloSong 和
Huntnews ,其优化点:
首次访问服务端渲染,页面间Quickling切换,单页面体验
所有链接可爬取,解决SEO问题
PageCache缓存已访问页面,加速切换,历史记录前进后退
可 全站禁用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(这个有机会另外讨论),采用同构JavaScript需要一定的架构支持,需要能解决后端渲染的组件反射问题,目前仅 react 或 类react框架才支持。
除了同构JS,还有一种方案,也比较简单,页面还是服务端拼装好的,CSS在head中,主文档是完整的HTML,JS在body尾部;但需要在后端模板中实现一种功能:允许通过特殊的ajax请求以json格式响应页面中的局部区域。这项技术被称为
Quickling。
此外,单页面应用还有一项优化手段,叫PageCache,前端控制页面切换时,把之前的页面缓存到内存中,下次再回到这个页面就直接展现,不用再次请求数据拼装模板渲染,进一步优化用户在站内浏览的体验。
基于Quickling和PageCache我们在印度市场(网络环境超差)实现了两个单页面应用产品:YoloSong 和
Huntnews ,其优化点:
首次访问服务端渲染,页面间Quickling切换,单页面体验
所有链接可爬取,解决SEO问题
PageCache缓存已访问页面,加速切换,历史记录前进后退
可 全站禁用JS,不影响浏览体验
按需加载,请求合并
相关文章推荐
- python之路(十七)-javascript
- js动态创建json对象
- javascript 判断是否数字类型
- js中实现数组的indexOf方法
- JS去掉空格
- js单元测试sinonjs库
- 用Javascript检测浏览器音频支持情况
- js的一套日期控件简单版
- JavaScript模板引擎
- javascript的闭包和匿名函数
- js断点调试心得
- JSP取得绝对路径
- 如何将js获取的变量付给jsp中表单的action
- js实现的老黄历项目总结
- jy901 ajson树莓派上编译错误
- js去除字符串中所有html标签及 符号
- JS学习 第一天--
- JS继承
- 使用文件流ADS 及 JavaScript 进行钓鱼攻击
- 【BZOJ1561】[JSOI2009]去括号【表达式相关】