React 15带来了Virtual DOM,React 16带来了Fiber。学习原理,天天向上!
Virtual DOM
直接学习此仓库github.com/Matt-Esch/v…,源码结构相当清晰!
Fiber
阅读了这篇文章An Introduction to React Fiber - The Algorithm Behind React。
学习Fiber之前建议先学习Virtual DOM,Fiber是对Virtual DOM的一种升级。
Virtual DOM使用栈来调度需要更新的内容,中间无法中断、暂停。Fiber支持中断,在浏览器渲染帧里面分片执行更新任务。Fiber解构让虚拟节点记录父节点、兄弟节点、子节点,形成链表树,你可以从任意顶点遍历到任意子节点,并返回。Fiber的分片操作使用requestAnimationFrame(高优先级任务)和requestIdleCallback(低优先级任务)Fiber对任务的执行优先级进行标记,高优先级的任务可以先执行,实现架构上的无阻塞
requestAnimationFrame模拟任务分片执行
MDN: window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
这里的动画可以理解为浏览器的刷新帧(对应电影里面的帧),大多数应该都是16ms
// 模拟任务中断分片执行
var tasks = new Array(100).fill(0).map((x, i) => i);
// 函数可以拿到当前执行的时刻,该值和performance.now()得到的值一致
function callFrame(timestamp) { // 片段内任务同步执行
let t = tasks.pop();
console.log("当前执行任务", t)
let end = timestamp;
// 模拟阻塞。因此这里受限于实际执行的脚本,如果运行200ms,则会阻塞下一次动画
while(tasks.length && end - timestamp < 200) {
end = performance.now()
}
// 下一帧是否需要继续运行
if (tasks.length) {
requestAnimationFrame(callFrame);
}
}
requestAnimationFrame(callFrame);
requestIdleCallback模拟任务分片执行
MDN: window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。
这里的空闲,我们大多理解为一帧,但实际上间隔要大一些,实际测试最小有十几毫秒,最大有几十毫秒。
requestIdleCallback是一个实验特性,因此在利用它做任务分片的工作时,最好有一些兜底方案,比如
setTimeoutrequestAnimationFrame
// 模拟任务中断分片执行
var tasks = new Array(100).fill(0).map((x, i) => i);
// deadline可以获取当前剩余的空闲时间
function call(deadline) {
let start = performance.now();
let t = tasks.pop();
console.log("当前执行任务", t)
let end = start;
// 模拟阻塞
while(tasks.length && end - start < 20) {
end = performance.now()
// deadline.timeRemaining()可以获取当前剩余时间
// 我们实际运行的时候,可以用这个deadline.timeRemaining()来判断是否要执行下一个任务
console.log("deadline remaining", deadline && deadline.timeRemaining())
}
// 下一帧是否需要继续运行
if (tasks.length) {
requestIdleCallback(call);
}
}
diff算法
这个就是树或者图的遍历,建议多刷leetcode就行了。
patch算法
对节点进行具体的增删改
- 增加的节点,加进
vdom - 节点属性变更,修改对应的
vnode - 兄弟变更,修改对应的节点数组
- 父节点变更,触发子节点的patch