现代浏览器网页渲染原理,关键渲染路径( critical rendering path )
javascript(交互、动画等视觉变化) -> style(样式重新计算) -> layouts(布局) -> paint(绘制) -> composite(图层组合)
- 1,浏览器构建对象模型
- 浏览器构建 DOM 对象,html -> DOM
- 构建 CSSOM 对象,css -> CSSOM
- 2,浏览器构建渲染树,DOM + CSSOM -> Render Tree 结合渲染路径,有一些样式并不需要执行布局和绘制的步骤,这样渲染速度就会被加快,也是性能优化的一个思路
回流与重绘,如何避免布局抖动
布局与绘制,再次布局又叫做回流 reflow
- 渲染树只包含网页需要的节点
- 布局是计算每个节点精确的位置和大小 - 盒模型
- 绘制是像素化每个节点的过程
影响回流的操作
- 添加、删除元素
- 操作 styles
- display: none
- offsetLeft/offsetTop/clientWidth
- 移动元素位置
- 修改浏览器大小,字体大小
tips:有一些动画是使用 GPU 进行加速的,这种动画效果不会触发布局和重绘,而直接进行复合的过程。
避免布局抖动( layout thrashing )
- 避免回流,比如用 transform 中的 translate 来做位移,避免回流和重绘。
- 读写分离,比如第三方框架使用 vdom
使用 FastDom 插件防止布局抖动
- 如何使用 FastDom Apis
复合线程( composition thread )与图层( layers )
- 将页面拆分图层进行绘制再进行复合
- 利用 DevTools 了解网页的图层拆分情况
- 仅影响复合的样式
减少重绘( repaint )
只影响复合的属性,比如 transform/opacity。把只影响复合的元素提取到一个单独的图层里
- 利用 DevTools 识别 paint 的瓶颈
- 利用 will-change 创建新的图层
高频事件防抖,解救页面卡顿
利用 Chrome DevTools 打开时可以复现抖动的问题( pointer events ) 对一些高频事件做一些长处理,就容易出现抖动问题,可以使用 requestAnimationFrame 函数解决这个问题。
rAF 是执行在布局和绘制之前的函数
对于高频率触发的事件,一帧之内多次触发处理函数是没有意义的,一帧之内触发一次就可以了,这时候就可以使用去抖动( debounce )。去抖动就是把原来频率很高的一个事情,变成按照需要的频率触发一次。
let ticking = false;
window.addEventListener('pointermove',e => {
let pos = e.clientX;
if(ticking) return;
ticking = true;
window.requestAnimationFrame(()=>{
changeWidth(pos)
})
})
React 时间调度实现
React16 做了一个较大的革新,实现了一个 fiber 机制。可以将 DOM 的修改,一个批量的任务拆解为许多的小任务,然后通过时间调度实现完成,从而保证用户交互的空闲时间。React 时间调度的实现就是借用了 rAF 方法。 基本原理:
- requestIdleCallback 的问题
- requestIdleCallback 是一帧 16ms 时间还有空余的时间的情况下,再做一些事情,但是这个函数没有被浏览器很好的支持
- 所以 React 通过 rAF 模拟 rIC
在一帧的关键渲染周期内