fiber节点渲染
大家好,我是万维读客的讲师曹欢欢。上节我们搭建好了异步渲染调度框架,本节我们学习如何组装构建fiber链表数据结构,实现异步渲染fiber树节点。
节点遍历
这里可能先要补充一点树的遍历的基本知识,比如下面这个树形结构:
A
/ | \
B C D
/
E
/ \
F G
遍历这个树形结构一般有三种方法:前序遍历、中序遍历、后序遍历;
我们看三个动画对比就比较清楚了:
给个比较的案例:
这些图是参考网上的,谢谢网友分享。
我们前面同步渲染的时候,还记得处理children的递归吗
renderElement(ele, parent) {
const node = ele.type === 'HostText'?document.createTextNode(''):document.createElement(ele.type);
Object.keys(ele.props).filter(key => key != 'children').forEach(key => {
node[key] = ele.props[key];
})
ele.props.children.forEach(child => {
this.renderElement(child, node);
})
parent.appendChild(node);
}
这个实际上就是一种后序遍历的实现,在fiber异步渲染中为了实现中断,就不能再通过递归的方式来处理了,需要我们在处理一个节点之后,能够找到下个节点。
中序遍历
React中的解决方案是通过树遍历的方法遍历整个虚拟DOM树,遍历过程中初始化对应的Fiber节点,构造一个遍历后的顺序列表,然后逐个调度render渲染。
假设采用先序遍历后形成的Fiber节点链表如下:
DOM树
A
/ | \
B C D
/
E
/ \
F G
fiber节点链表
----------------
根节点A ---> child节点B ---> child节点E ---> child节点F ---> sibling节点G ---> sibling节点C ---> sibling节点D
实现节点渲染
我们参考上一节学习的fiber的原理和react相关的代码,修改treat代码结构如下:
/**
* 1. 处理当前执行的fiber节点,插入到父级中
* 2. 初始化children中的fiber节点,返回下一个要执行的fiber节点next
*/
performUnitOfWork(fiber) {
if (!fiber.stateNode) {
fiber.stateNode = fiber.type === 'HostText' ? document.createTextNode('') : document.createElement(fiber.type);
Object.keys(fiber.props).filter(key => key != 'children').forEach(key => {
fiber.stateNode[key] = fiber.props[key];
})
}
if(fiber.return){
fiber.return.stateNode.appendChild(fiber.stateNode);
}
// 用链表处理child
let preSibling = null;
fiber.props.children.forEach((child, idx) => {
const fiberChild = {
type: child.type,
stateNode: null,
props: child.props,
return: fiber,
}
if(idx == 0){
fiber.child = fiberChild;
}else{
preSibling.sibling = fiberChild;
}
preSibling = fiberChild;
})
return this.getNextFiber(fiber);
}
/**
* 先序遍历顺序
* child-》sibling
*/
getNextFiber(fiber){
if(fiber.child){
return fiber.child;
}
let nextFiber = fiber;
while(nextFiber){
if(nextFiber.sibling){
return nextFiber.sibling;
}else{
nextFiber = nextFiber.return;
}
}
return null;
}
编写完之后查看我们的测试用例执行情况,顺利执行,表示代码完成了异步渲染。
RERUN treact03/jsx.test.jsx x25
✓ treact03/jsx.test.jsx (1) 505ms
✓ async render (1) 505ms
✓ render in async 503ms
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 12:51:09
Duration 519ms
好了,这里我们实现了React Fiber的异步渲染,可能代码还不是很全面,喜欢动手的同学可以补充完善代码。也可以把上面的遍历算法改成中序、后序遍历试一下呢。
参考
- 树的遍历:https://developer.aliyun.com/article/978343
- 中序遍历:https://juejin.cn/post/7012772225644232741
- performConcurrentWorkOnRoot 源码:https://github.com/facebook/react/blob/4f29ba1cc52061e439cede3813e100557b23a15c/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L824
- workLoopConcurrent源码:https://github.com/facebook/react/blob/4f29ba1cc52061e439cede3813e100557b23a15c/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1824-L1829
- Fiber节点:https://github.com/facebook/react/blob/4f29ba1cc52061e439cede3813e100557b23a15c/packages/react-reconciler/src/ReactInternalTypes.js