React Fiber 渐进式遍历详解
欢迎关注我的公众号睿Talk
,获取我最新的文章:
一、前言
之前写的一篇文章,React Fiber 原理介绍,介绍了 React Fiber 的实现原理,其中的关键是使用Fiber
链的数据结构,将递归的Stack Reconciler
改写为循环的Fiber Reconciler
。今天将手写一个 demo,详细讲解遍历Fiber
链的实现方式。
二、Stack Reconciler
假设有以下组件树:
对应的 JS 代码如下:
const a1 = {name: 'a1'};
const b1 = {name: 'b1'};
const b2 = {name: 'b2'};
const b3 = {name: 'b3'};
const c1 = {name: 'c1'};
const c2 = {name: 'c2'};
const d1 = {name: 'd1'};
const d2 = {name: 'd2'};
a1.render = () => [b1, b2, b3];
b1.render = () => [];
b2.render = () => [c1];
b3.render = () => [c2];
c1.render = () => [d1, d2];
c2.render = () => [];
d1.render = () => [];
d2.render = () => [];
使用Stack Reconciler
递归的方式来遍历组件树,大概是这个样子:
function doWork(o) {
console.log(o.name);
}
function walk(instance) {
doWork(instance);
const children = instance.render();
children.forEach(walk);
}
walk(a1);
// 输出结果:a1, b1, b2, c1, d1, d2, b3, c2
二、Fiber Reconciler
下面我们用 Fiber 的数据结构来改写遍历过程。首先定义数据结构,然后在遍历的过程中通过link
方法创建节点间的关系:
// 定义 Fiber 数据结构
class Node {
constructor(instance) {
this.instance = instance;
this.child = null;
this.sibling = null;
this.return = null;
}
}
// 创建关系链
function link(parent, children) {
if (children === null) children = [];
// child 指向第一个子元素
parent.child = children.reduceRight((previous, current) => {
const node = new Node(current);
node.return = parent;
// sibling 指向前面处理的元素
node.sibling = previous;
return node;
}, null);
return parent.child;
}
遍历完成后会得出如下的关系链:
下面来详细看下遍历的过程。还是沿用之前的walk
和doWork
方法名:
function doWork(node) {
console.log(node.instance.name);
// 创建关系链
const children = node.instance.render();
return link(node, children);
}
function walk() {
while (true) {
let child = doWork(node);
if (child) {
node = child;
continue;
}
if (node === root) {
return;
}
while (!node.sibling) {
if (!node.return || node.return === root) {
return;
}
node = node.return;
}
node = node.sibling;
}
}
const hostNode = new Node(a1);
const root = hostNode;
let node = root;
walk();
// 输出结果:a1, b1, b2, c1, d1, d2, b3, c2
上面就是递归改循环的代码了。可以看到循环的结束条件是当前处理的节点等于根节点。在循环开始的时候,以深度优先一层一层往下递进。当没有子节点和兄弟节点的时候,当前节点会往上层节点回溯,直至根节点为止。
下面再来看看怎么结合requestIdleCallback
API,实现渐进式遍历。由于完成这个遍历所需时间实在太短,因此每处理 3 个节点,我们sleep
1 秒,从而达到退出当前requestIdleCallback
的目的,然后再创建一个新的回调任务:
function sleep(n) {
const start = +new Date();
while(true) if(+new Date() - start > n) break;
}
function walk(deadline) {
let i = 1;
while (deadline.timeRemaining() > 0 || deadline.didTimeout) {
console.log(deadline.timeRemaining(), deadline.didTimeout);
let child = doWork(node);
if (i > 2) {
sleep(1000);
}
i++;
if (child) {
node = child;
continue;
}
if (node === root) {
console.log('================ Task End ===============');
return;
}
while (!node.sibling) {
if (!node.return || node.return === root) {
console.log('================ Task End ===============');
return;
}
node = node.return;
}
node = node.sibling;
}
console.log('================ Task End ===============');
requestIdleCallback(walk);
}
requestIdleCallback(walk);
// 输出结果:
15.845 false
a1
15.14 false
b1
14.770000000000001 false
b2
================ Task End ===============
15.290000000000001 false
c1
14.825000000000001 false
d1
14.485000000000001 false
d2
================ Task End ===============
14.96 false
b3
14.475000000000001 false
c2
================ Task End ===============
三、总结
本文通过一个 demo,讲解了如何利用React Fiber
的数据结构,递归改循环,实现组件树的渐进式遍历。
睿Talk
记录个人和团队技术成长的点点滴滴
5.5k 声望
418 粉丝
推荐阅读
Web前端主题切换的几种方案
这种方案利用了css多层样式精确匹配的特点,通过样式覆盖的方式实现主题的切换。首先需要在应用的根元素中设一个 class,切换主题时给 class 赋上对应的值,下面以theme1/theme2为例。
Dickens赞 8阅读 7.5k
正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...
寒青赞 56阅读 8.4k评论 11
JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...
jenemy赞 48阅读 6.9k评论 12
从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...
乌柏木赞 75阅读 7k评论 16
再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...
libinfs赞 42阅读 6.8k评论 12
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...
乌柏木赞 45阅读 8.5k评论 6
从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...
乌柏木赞 35阅读 6.6k评论 10
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。