不知不觉,春节就过完了,还没来得及好好享受就没了。好想来一场说走就走的旅行,不吹水了,直接进入正题。
最近在做一个需求,发现了薄弱的地方,趁这个好机会深入了解一下,拓宽一下视野~
众所周知,网页不仅应该被快速加载,同时还应该流畅运行,比如快速响应的交互,如丝般顺滑的动画……
在实际开发中如何做到上面所说的效果呢?
1. 确认渲染性能的分析标准
2. 准备尺子去衡量渲染性能标准
3. 对耗时多的地方进行优化
我们可以粗略的得到下面的优化目标
第一个是 首屏呈现时间,网上的资料已经非常非常多了,压缩代码,使用webp图片,使用sprite,按需加载,“直出”,CDN……
第二个是 16ms 优化,本篇重点讲16ms的优化。
一. 浏览器渲染原理介绍
大多数设备的刷新频率是60次/秒,(1000/60 = 16.6ms)也就说是浏览器对每一帧画面的渲染工作要在16ms内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。
这就是上图中的<16ms。浏览器在一帧里面,会做以下这些动作。 当然,有些步骤(比如 layout,paint)是可以省略的。
如果改变属性在上面图中越往左,那么影响就越大,效率就越低。
浏览器渲染的流程如下:
从上面图中可以看出,如果只是改变composite(渲染层合并),那效率就会大大提高。
下面粗略地列出改变哪些样式会分别改变渲染过程的哪一模块。
从上图可以看到 transform,opacity 只会改变composite(渲染层合并),为什么呢?因为开启了GPU加速。
开启 GPU 加速
字面上的解释: 纹理能够以很低的代价映射到不同的位置,而且还能够以很低的代价通过把它们应用到一个非常简单的矩形网格中进行变形。
【字面上的理解非常地绕口,还是老道理,能用图讲清的道理不要用文字。】
小tips:先选中timeline的某一帧,然后选择下面的layer标签tab,可以左右拖动该模块出现3d
我们可以看到页面上由如下层组成:
虽然我们最终在浏览器上看到的只是一个复印版,即最终只有一个层。类似于PhotoShop软件中的“图层”概念,最后合并所有可视图层,输出一张图片到屏幕上
但是实际上一个页面会因为一些规则被分成相应的层,一旦被独立出来之后,便不会再影响其他dom的布局,因为它改变之后,只是“贴上”了页面。
目前下面这些因素都会引起Chrome创建层:
需要注意的是,不要创建过多的渲染层,这意味着新的内存分配和更复杂的层管理。不要滥用GPU加速,注意看 composite layouts 是否超出了 16ms
说了这么多浏览器渲染的原理,如果没有尺子测量也毫无用处。那么,下面就选尺子去丈量:谷歌开发工具的Timeline。
二. 谷歌开发工具 Timeline 的常用功能1. 点击左上角的录制之后,录制结束后会生成下面的样子,红色区域内就是帧了,移动上去可以看到每一帧的频率,如果>60fps,就是比较流畅,如果<60fps,就会感到卡顿。
2. 在timeline下面,可以看到各个模块的耗时,可以定位到耗时较大的函数上面,对该函数进行优化。
3. 按照下面步骤选择,即可看到独立的层,高亮重绘的区域,方便找出不必要重绘的区域,进行优化
选择之后,当前页面会出现下面2中颜色边框
黄色边框: 有动画3d变换的元素,表示放到了一个新的复合层(composited layer)中渲染
蓝色的栅格:这些分块可以看作是比层更低一级的单位,Chrome以这些分块为单位,一次向GPU上传一个分块的内容。
工具也有了,浏览器渲染的原理也知道了,接下来是结合实际项目进行优化.
三. 在实际项目中进行 16.6ms 优化结合上面的渲染流程图,我们可以针对性的分析并优化下面的一些步骤
1. 读写分离,批量操作
JavaScript脚本运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧的值。
因此,如果你在当前帧获取属性之前又对元素节点有改动,那就会导致浏览器必须先应用属性修改,结果执行布局过程,最后再执行JavaScript逻辑。
// 先写后读,触发强制布局 function logBoxHeight() { // 更新box样式 box.classList.add('super-big'); // 为了返回box的offersetHeight值 // 浏览器必须先应用属性修改,接着执行布局过程 console.log(box.offsetHeight); }
优化之后: