1.屏幕刷新率
目前大多数设备显示器刷新率——60次/秒
浏览器渲染动画或页面的每一帧的速率也需要跟设备屏幕的刷新率保持一致
页面是一帧一帧绘制出来的,当每秒绘制的帧数达到60时,页面流畅,<60s就会感觉卡顿
需要1/60 约等于 16.6ms 一帧
比如高帧显示器 120HZ是 8.3ms
所以我们写代码的时候,力求不让每一帧的工作量超过 16ms ,当然这个还与设备性能相关
返回文档
点击添加图片说明
2.帧
返回文档
frame
一帧开始:Vsync 触发, 一帧开始。(最初 Webkit 使用定时器进行渲染间隔控制, 2014 年时开始 使用显示器的 vsync 信号控制渲染,这意味着 16ms 内多次 commit 的 DOM 改动会合并为一次渲染。)
事件处理:执行事件的回调
—————
rAF: 因为它离vsync很近,可以就近获取input data。所以这是操作dom理想的地方,例如修改 100 个元素的 class,并不会导致100 次样式计算style calculations,而是会在之后的管道流中批量处理。(优化方案)
—————
解析HTML: 构建HTML,例如appendChild
解析CSS:
布局(回流):计算可见元素几何(盒模型)信息(位置、尺寸),通常会对整个文档操作一遍,产生的开销和 DOM 个数成正比例关系。
Update Layer Tree:创建层叠上下文和元素层级顺序。
Paint:其实这是绘画的第一步,这一步记录需要调用绘制的方法 draw calls (fill a rectangle here, write text there)。 第二步是光栅化 Rasterization (下面会提到),draw calls 会被执行。 第一步显然速度要快于第二步,但经常把这两步都成为 painting。
Composite:合成各层的渲染结果
因为js引擎执行js和渲染引擎渲染页面是系统一个线程,两者是互斥的,
如果某个任务事件时间特别长,就会堵塞后面GUI的渲染
events(input events) — js —begin frame(resize scroll) — rAF — layout—painting ( idle 空闲时间)
js阻塞渲染的例子
返回文档
JavaScript
<div id="message"></div><script>var then = Date.now()var i = 0var el = document.getElementById('message')while (true) {
var now = Date.now()
if (now - then > 1000) {
if (i++ >= 5) {
break;
}
el.innerText += 'hello!\n'
console.log(i)
then = now
}}</script>
可以观察到虽然每秒都会写一次 DOM,但在 5s 结束后才会全部渲染出来,明显耗时脚本阻塞了渲染。
2.1 rAF
requestAnimationFrame 回调函数会在下一帧绘制之前执行
2.2 requestIdleCallback
返回文档
点击添加图片说明
请求空闲回调
返回文档
JavaScript
// 我作为用户,告诉浏览器哦,现在执行callback函数,但是它的优先级比较低,告诉浏览器,可以空闲时间执行callback,
// d但是如果到了超时时间timeout,必须马上执行
window.requestIdleCallback(cb, { timeout: 1000})
fiber是把整个任务分成很多小任务,每次执行一个任务,如果还有剩余的空闲时间,就会继续执行下一个任务
最小任务不能分割执行,如果有剩余时间,且执行一个最小任务,需要将当前的最小任务执行完毕之后才能把执行权,重新还给浏览器。
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback
3.单链表
单链表中的数据是以节点方式表示的,每个节点由:元素+指针(指向下一个元素)构成
updateQueue
返回文档
点击添加图片说明
为什么使用链表? 因为链表可中断和恢复
4.fiber
https://juejin.im/post/5ecdd66ff265da76d53c094a#heading-11
4.1 fiber之前的调和
react递归遍历 vdom,找出需要变动的节点,然后同步跟新他们,这个过程 react叫做 Reconcilation
在Reconcilation期间,因为其不可中断,react会一直占用浏览器资源,一是用户反馈得不到响应,二是会掉帧,导致页面卡顿
4.2 fiber的调和
我们可以通过某种调度策略合理的分配浏览器资源,从而提高用户响应速度。
通过Fiber架构,让react中的 Reconcilation 过程变得可中断,在不影响用户体验的前提下,适时的控制权交给浏览器,属于平常我们所称的 “协程”。
4.2.1 Fiber是一个执行单元
一个fiber就是一个执行单元,每执行完一个fiber单元,react会看是否还有剩余时间,如果没时间就让出控制权,暂停fiber执行
返回文档
点击添加图片说明
4.2.2 Fiber是一种数据结构
react目前的做法是使用链表,每个vdom节点内部表示为一个Fiber.
返回文档
点击添加图片说明
返回文档
TypeScript
type Fiber = {
type: any, // 类型
return: Fiber, // 父节点
child: Fiber, // 指向第一个子节点
sibling: Fiber, // 指向下一个兄弟节点
}
5.Fiber执行阶段
每次渲染有两个阶段: Reconcilation(调和\render阶段) 、commit(提交阶段)
调和阶段:可以认为是diff阶段,这个阶段可以被中断,在这个阶段会找出所有的节点变更,例如节点新增、删除、属性变更等等,这些变更在react中叫做 effect(副作用)
提交阶段:将上一阶段的所有副作用一次执行,这个阶段会同步执行,不能被打断
5.1 render阶段
render阶段会构建fiber树
5.1.1 element.js
5.1.2 fiber.js
返回文档
点击添加图片说明