一、浏览器渲染原理和关键渲染路径
关于这点建议参考下:
①一次搞定前端“核心主线”——从输入URL到页面展示发生了什么
二、回流与重绘, 如何避免布局抖动
1. 回流
影响回流的操作:
- 添加/删除元素
- 操作styles,具体可以参考csstriggers
- display:none
- offsetLeft、scrollTop、clientWidth (获取布局信息的操作的时候,会强制队列刷新)
- 移动元素位置
- 修改浏览器大小、字体大小
参考文章:
② 浏览器渲染详细过程:重绘、重排和 composite 只是冰山一角
查看是否发生回流:
通过下图方式打开Rendering选项卡:Rendering选项卡,顾名思义是做一些渲染相关的事。
下面是选项卡的界面(勾选Layout Shift Regions):
- Paint flashing 高亮页面重绘区域。
- Layout Shift Regions 高亮布局变动区域(重排)。
- Layer borders 高亮合成层边框,对于减少合成层还是挺好用的。
- Scrolling Performance Issues 用于优化滚动性能问题。
- scroll大家可能会比较理解,touch的话其实也可以影响滚动性能,最直观的就是只要我们禁止了某元素上touch的事件中禁止浏览器默认行为,那么就不会再触发scroll事件。
- 当您开Scrolling Performance Issues发现页面上一堆高亮的touch事件时,可以考虑touch-action:auto来去除。
- Highlight ad frames 高亮用于广告的iframe(试了下,谷歌的推广广告识别没问题,百度的没测)。
- Hit-test borders 展示点击测试的区域。(鸡肋,请忘记)。
- Emulate CSS media type 模拟媒体查询是打印还是终端屏幕。
- Emulate CSS media feature prefers-color-scheme 模拟媒体查询的系统主题,具体参考prefers-color-scheme。
- Emulate CSS media feature prefers-reduced-momition 模拟媒体查询的开启动画减弱功能,具体参考prefers-reduced-motion。
参考文章:
2.避免布局抖动
- 避免回流
参考:利用transform:translate(x轴平移,y轴平移)实现平移,既不会触发回流也不会触发重绘,只会触发复合的过程。
- 读写分离
参考:
- 虚拟DOM
- 利用FastDom工具
Eliminates layout thrashing by batching DOM read/write operations (580 bytes gzipped compressed).
fastdom.measure(function() {
console.log('measure');
});
fastdom.mutate(function() {
console.log('mutate');
});
fastdom.measure(function() {
console.log('measure');
});
fastdom.mutate(function() {
console.log('mutate');
});
Outputs:
measure
measure
mutate
mutate
三、 复合线程(compositior thread)与图层(layers)
1.复合线程做了什么
复合可以简单理解为把页面拆分为多个图层,一一进行绘制。
- 将页面拆分图层进行绘制再进行复合
- 利用DevTools了解网页的图层拆分情况(如何看图层)
- 哪些样式仅影响复合
2.怎样拆分图层
- 浏览器规定
- 主动把某些元素作为单独图层
3.哪些样式仅影响复合
因为使用transform和opacity做CSS动画的时候,会将元素提升为一个复合层;而使用js操作css属性做动画时,必须使用translateZ或will-change才能将元素强行提升至一个复合层。
元素本身使用transform和opacity做CSS动画的时候,会提前告诉GPU动画如何开始和结束及所需要的指令;所以会创建一个复合层(渲染层),并把页面所有的复合层发送给GPU;作为图像缓存,然后动画的发生仅仅是复合层间相对移动。
而使用JS做动画,JS必须在动画的每一帧计算元素的状态;发送给GPU,但不会将元素提升至一个复合层;所以想让元素提升至一个复合层,必须使用translateZ或will-change: transform, opacity。
参考文章:
4.减少重绘
- 利用DevTools识别paint的瓶颈
- command + P 打开工具,检查重绘
- Paint flashing
- 利用will-change创建新的图层
will-change属性允许你提前告诉你的浏览器,你可能会改变元素的哪些属性。这样浏览器可以在元素需要之前设置合适的优化。避免了一个不小的启动成本,而这样的成本对页面的响应速度有着副作用。元素可以更快的被改变和渲染,而页面更新有个更平滑的体验。
.element {
/* style rules */
transition: transform 1s ease-out;
}
.element:hover {
will-change: transform;
}
.element:active {
transform: rotateY(180deg);
}
注意:并不是把所有的图层单独拆分出来就最好。因为会增大开销,带来性能方面的问题。
参考:你不得不知道的关于CSS的will-change属性的所有事
四、高频事件防抖
像滚动(onscroll)、移动端触摸(touch)、指针移动(PointerMove)这类事件,触发的频率高于刷新的频率。
1.关于帧(frame)的生命周期
简单描述是:首先会有事件,通过事件触发会引起JS触发我们的这个视觉上的变化.这时候一帧就要开始了,在Layout以及Paint之前发生了rAF的调用,所以我们可以利用rAF把处理做完后再去进行布局和绘制就可以提高效率。而且rAF是由JS去进行调度的,JS会尽量在每一次绘制之前调度rAF,以满足尽量达到60pfs的效果。
2.利用window.requestAnimationFrame实现防抖
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。包上一层函数就能去抖动,达到一帧只触发一次的目的。
let ticking = false;
function doSomething(pos) {
// 根据指针移动位置做的事
}
window.addEventListener('pointermove', function(e) {
let pos = e.clientX;
if (ticking) return;
ticking = true;
window.requestAnimationFrame(() => {
doSomething(pos);
ticking = false;
}
});
也可以使用使用setTimeout() 或 CustomEvent实现喔!!