实现workloop
On this page
大家好,我是万维读客的讲师曹欢欢。上节学习了fiber异步渲染原理和数据模型,这里我们开始构建异步渲染模型workloop,逐步构建fiber节点树。
测试用例
创建一个treact03文件夹,创建测试用例jsx.test.jsx,测试treact异步渲染能力,测试用例代码如下:
import {describe, it, expect} from 'vitest';
import * as Treact from './Treact';
function wait(){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(1);
}, 500);
});
}
describe('async render',()=>{
it('render in async', async ()=>{
const ele = (
<div className="container">
<span>hello</span>
<a>w3cdoc.com</a>
</div>);
const container = document.createElement('div');
const root = Treact.createRoot(container);
root.render(ele);
expect(container.innerHTML).toBe('');
await wait();
expect(container.innerHTML).toBe('<div class="container"><span>hello</span><a>w3cdoc.com</a></div>');
})
})
render函数执行后,同步root.container.innerHTML的内容没有更新,所以应该是空的。我们异步等待一段时间,然后root.container.innerHTML应该能更新到最终渲染结果。
查看控制台肯定会报错,这时候我们来把render函数修改成异步。
实现异步render
我们参考上一节学习的fiber的原理和react相关的代码,修改treat代码结构如下:
let workInProgress = null; // 当前fiber节点
let workInProgressRoot = null; // 所有fiber节点的root节点
class TreactRoot {
_internalRoot = null;
constructor(container) {
// this.container = container;
this._internalRoot = {
current: null,
containerInfo: container
}
}
workloop(){
while(workInProgress){
workInProgress = this.performUnitOfWork(workInProgress);
}
}
performUnitOfWork(fiber){
// 具体执行fiber节点渲染的逻辑
}
render(element) {
// this.renderElement(element, this.container);
this._internalRoot.current = {
alternate: {
stateNode: this._internalRoot.containerInfo,
props:{
children: [element]
}
}
}
workInProgressRoot = this._internalRoot;
workInProgress = this._internalRoot.current.alternate;
setTimeout(this.workloop.bind(this));
}
上面的代码我们先初始化了workInProgress和workInProgressRoot 这两个节点,其中workInProgressRoot是所有fiber节点的根节点,然后我们来构建fiber节点树。
我们修改了构造函数方法,通过_internalRoot来存储当前根节点容器,然后在render方法中来初始化alternate这个fiber节点,并设置根节点fiber和当前要处理的fiber节点。
最后开始异步调用workloop函数,这里先简单用定时器实现,后面会用调度器来替代。workloop函数中也是通过循环来执行fiber节点的渲染逻辑,暂时没有实现yield让出控制权,后面通过调度器一起实现。
这里我们整体构建好了workloop异步渲染的框架,下一步就可以开始实现fiber节点渲染了。
参考
- 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