不知不觉,春节就过完了,还没来得及好好享受就没了。好想来一场说走就走的旅行✈️,不吹水了,直接进入正题。javascript
最近在作一个需求,发现了薄弱的地方,趁这个好机会深刻了解一下,拓宽一下视野~java
众所周知,网页不只应该被快速加载,同时还应该流畅运行,好比快速响应的交互,如丝般顺滑的动画……node
在实际开发中如何作到上面所说的效果呢?web
第一个是 首屏呈现时间,网上的资料已经很是很是多了,压缩代码,使用webp图片,使用sprite,按需加载,“直出”,CDN…… canvas
第二个是 16ms 优化,本篇重点讲16ms的优化。浏览器
大多数设备的刷新频率是60次/秒,(1000/60 = 16.6ms)也就说是浏览器对每一帧画面的渲染工做要在16ms内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。缓存
这就是上图中的<16ms。浏览器在一帧里面,会作如下这些动做。 固然,有些步骤(好比 layout,paint)是能够省略的。闭包
若是改变属性在上面图中越往左,那么影响就越大,效率就越低。dom
浏览器渲染的流程以下:ide
从上面图中能够看出,若是只是改变composite(渲染层合并),那效率就会大大提升。
下面粗略地列出改变哪些样式会分别改变渲染过程的哪一模块。
从上图能够看到 transform,opacity 只会改变composite(渲染层合并),为何呢?由于开启了GPU加速。
小tips:先选中timeline
的某一帧,而后选择下面的layer
标签tab,能够左右拖动该模块出现3d
咱们能够看到页面上由以下层组成:
虽然咱们最终在浏览器上看到的只是一个复印版,即最终只有一个层。相似于PhotoShop软件中的“图层”概念,最后合并全部可视图层,输出一张图片到屏幕上
可是实际上一个页面会由于一些规则被分红相应的层,一旦被独立出来以后,便不会再影响其余dom的布局,由于它改变以后,只是“贴上”了页面。
<video>
元素<canvas>
元素
说了这么多浏览器渲染的原理,若是没有尺子测量也毫无用处。那么,下面就选尺子去丈量:谷歌开发工具的Timeline。
1. 点击左上角的录制以后,录制结束后会生成下面的样子,红色区域内就是帧了,移动上去能够看到每一帧的频率,若是>60fps,就是比较流畅,若是<60fps,就会感到卡顿。
2. 在timeline下面,能够看到各个模块的耗时,能够定位到耗时较大的函数上面,对该函数进行优化。
工具也有了,浏览器渲染的原理也知道了,接下来是结合实际项目进行优化.
结合上面的渲染流程图,咱们能够针对性的分析并优化下面的一些步骤
1. 读写分离,批量操做
JavaScript脚本运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧的值。
所以,若是你在当前帧获取属性以前又对元素节点有改动,那就会致使浏览器必须先应用属性修改,结果执行布局过程,最后再执行JavaScript逻辑。
// 先写后读,触发强制布局
function logBoxHeight() {
// 更新box样式
box.classList.add('super-big'); // 为了返回box的offersetHeight值 // 浏览器必须先应用属性修改,接着执行布局过程 console.log(box.offsetHeight); }
// 先读后写,避免强制布局 function logBoxHeight() { // 获取box.offsetHeight console.log(box.offsetHeight); // 更新box样式 box.classList.add('super-big'); }
2. 闭包缓存计算结果 (须要频繁的调用,计算的函数)
1 getMaxWidth: (function () {
2 var cache = {}; 3 function getwidth() { 4 if (maxWidth in cache) { 5 return cache[maxWidth]; 6 } 7 var target = this.node, 8 width = this.width, 9 screen = document.body.clientWidth, 10 num = target.length, 11 maxWidth = num * width + 10 * num + 20 - screen; 12 cache[maxWidth] = maxWidth; 13 return maxWidth; 14 } 15 return getwidth; 16 })(),
改为这种方式后,直接蹭蹭蹭~ 减小了10多ms
而后在调用requestAnimationFrame的时候,若是你在一开始就作了读取样式属性的操做,那么将会触发浏览器的强制同步布局操做(即在javascript阶段中执行布局),这样会致使屡次布局,效率低下。
优化以下:
window.requestAnimationFrame(function () { context.animateTo(nowPos); //须要更新位置的交给RAF });
续上面,开启paint flashing 以后,能够看到浏览器从新绘制了哪些区域。发现有一些没必要要重绘的区域也重绘了~给这些开启GPU优化(上文中提到)
直接看 timeline 效果,全绿了~悬着的心终于放下了