Thinking系列,旨在利用10分钟的时间传达一种可落地的编程思想。
对于大量操作 DOM 的场景,页面时常会出现卡顿现象,导致用户体验不佳。卡顿的原因是由于掉帧导致!!
掉帧
现在屏幕大部分的固定刷新频率为60Hz,浏览器会在这个间隔 16ms(
1000
/
60
1000/60
1000/60)进行绘制操作,以确保流畅性。
显然,16ms 渲染一次是确保浏览器不卡顿的重点。如果没在 16ms 内进行一次渲染,则意味着该帧丢失了(掉帧)。
浏览器完成一次渲染,需要完成如下步骤:
- 处理 HTML 标记并构建 DOM 树。
- 处理 CSS 标记并构建 CSSOM 树。
- 将 DOM 与 CSSOM 合并成一个渲染树。
- 根据渲染树来布局,以计算每个节点的几何信息。
- 将各个节点绘制到屏幕上。
详细了解该部分,可查看 前端优化–关键渲染路径
为什么会丢帧?
javascript 为单线程,如果存在大量的 js 计算,会导致阻塞,绘制时间被延后,出现丢帧。
如何引起重绘?
// Suboptimal, causes layout twice.
var newWidth = aDiv.offsetWidth + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.height = newHeight + 'px'; // Write
// Better, only one layout.
var newWidth = aDiv.offsetWidth + 10; // Read
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
aDiv.style.height = newHeight + 'px'; // Write
http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html
requestAnimationFrame
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
window.requestAnimationFrame(callback);
FastDom
fastdom 通过批处理 DOM 读/写操作 消除布局抖动。官方提供的两个示例:
关于动画:http://wilsonpage.github.io/fastdom/examples/animation.html
关于改变DOM宽高:http://wilsonpage.github.io/fastdom/examples/aspect-ratio.html
通过数据发现:FastDom > requestAnimationFrame > 原生(不借助任何函数)
剖析
fastdom.measure(() => { console.log('读DOM') })
fastdom.mutate(() => { console.log('写DOM') })
fastdom.measure(() => { console.log('读DOM') })
fastdom.mutate(() => { console.log('写DOM') })
// 读DOM 读DOM 写DOM 写DOM
其核心是通过批处理,将对 DOM 的读写操作进行分类管理,并一次性处理。
每一个 measure(读)/mutate(写) 操作任务都会被添加到对应的队列中。队列在下一帧使用 window.requestAnimationFrame()
执行。
FastDom 的目标是在应用程序的所有模块中像一个单例,返回相同实例,统一对 DOM 进行 读/写 操作。
源码地址:https://github.com/wilsonpage/fastdom/blob/HEAD/fastdom.js#L71-L72