在上一篇文章中,我们解析了render流程中FiberRoot的生成。而这一篇着重于在render步骤中执行的另一部分内容。会涉及到队列、更新等信息。但是不会着重于这部分,后续会进行学习。
1.legacyRenderSubtreeIntoContainer
还记得上篇文章中也是这个函数入口,开始了render的流程,而我们忽略了更新部分的代码,如下:
/**
* parentComponent :父组件,可以传入null
* children ReactDOM.render()或者ReactDOM.hydrate()中的第一个参数,可以理解为根组件。也就是render里的ReactElement对象
* container: 组件需要挂在的容器
* forceHydrate: true 为 服务端渲染,false为客户端渲染,我们研究的是客户端渲染
* callback: 组件渲染完成后需要执行的回调函数,我没有使用过这个参数,大家可以尝试一下
**/
function legacyRenderSubtreeIntoContainer(parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: Container,forceHydrate: boolean,callback: ?Function,) {
// ...省略初始化fiberRoot
// 首次root为空,初始化root已经fiberRoot,参考上篇文章
if (!root) {
// .....省略创建root步骤
fiberRoot = root._internalRoot;
// unbatchedUpdates:初始化挂载不应该进行批处理
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
// 不是初始化挂在,直接调用updateContainer
fiberRoot = root._internalRoot;
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
2. unbatchedUpdates
什么是批处理,例如我们常使用的setState
,当我们进行多次设置值,但是react并不会多次二更渲染,将其优化成一次更新,这个步骤就是批量渲染(batchedUpdate)
。而unbatchedUpdates
就是不进行批量渲染。
下面的unbatchedUpdates
函数,可以看出我们只是修改了executionContext
的值,代表着不进行批量处理。
// 三次设置值,会将其合并,并只执行一次
this.setState({ name: 'xiaodong' })
this.setState({ name: 'xiaoxue' })
this.setState({ name: 'xiaomei' })
function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
// executionContext: 0b0000000
// BatchedContext: 0b0000001
// LegacyUnbatchedContext: 0b0001000
const prevExecutionContext = executionContext;
// 且操作符和取反操作符代表除去BatchedContext这个类型
// executionContext: 注意在ReactFiberWorkLoop一个全局对象,后续执行函数中可以直接获取
executionContext &= ~BatchedContext; // 0b0000000 & 0b1111110
executionContext |= LegacyUnbatchedContext; // 或操作符代表包含LegacyUnbatchedContext这个类型(遗留问题)
try {
return fn(a); // 调用回调函数
} finally {
executionContext = prevExecutionContext; // 异常时,恢复executionContext
if (executionContext === NoContext) {
// 刷新此批处理中计划的立即回调
flushSyncCallbackQueue();
}
}
}
3. updateContainer
/**
* * children ReactDOM.render()或者ReactDOM.hydrate()中的第一个参数,可以理解为根组件。也就是render里的ReactElement对象
* container:: fiberRoot对象
* parentComponent :父组件,可以传入null
* callback: 组件渲染完成后需要执行的回调函数,我没有使用过这个参数,大家可以尝试一下
**/
export function updateContainer(element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,callback: ?Function): Lane {
// 获取当前的rootFiber对象
const current = container.current;
// 获取程序运行到目前为止的时间,用于进行优先级排序
const eventTime = requestEventTime();
// 当前批量更新的配置, 是一个全局对象, 后面详细展开, 第一次执行这里返回是null
const suspenseConfig = requestCurrentSuspenseConfig();
// 同步直接返回 `SyncLane` = 1。以后开启并发和异步等返回的值就不一样了,目前只有同步这个模式
const lane = requestUpdateLane(current, suspenseConfig);
// 获取当前节点和子节点的上下文
// 首次执行返回一个emptyContext, 是一个{}
const context = getContextForSubtree(parentComponent); // {}
if (container.context === null) {
container.context = context; // fiberRoot.context = {}
} else {
container.pendingContext = context;
}
// 1. 初始化current(HostRootFiber)对象的updateQueue队列
// 1.1 创建一个更新(update)对象
const update = createUpdate(eventTime, lane, suspenseConfig);
// 1.2 设置update对象的payload, 这里element需要注意(是tag=HostRoot特有的设置, 指向<App/>)
update.payload = { element };
// 1.3 将update对象加入到当前Fiber(这里是RootFiber)的更新对列当中
enqueueUpdate(current, update);
// 调度和更新current(HostRootFiber)对象
scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
4. requestEventTime
获取程序运行到目前为止的时间,用于进行优先级排序。
let currentEventTime = -1;
function requestEventTime() {
// executionContext = 0b001000, unbatchedUpdates函数中国计算的值
// RenderContext = 0b010000;
// CommitContext = 0b100000;
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
// 我们在React内部,因此可以读取实际时间
return now();
}
// 我们不在React内部,因此我们可能处于浏览器事件的中间。
if (currentEventTime !== NoTimestamp) {
// 对所有更新使用相同的开始时间,直到我们再次进入React
return currentEventTime;
}
// 这是自React产生以来的第一次更新。计算新的开始时间。
currentEventTime = now();
return currentEventTime;
}
5. 结尾
render部分就已经完成了,这里面涉及的调度等会再组件更新部分进行学习。
6. 写在最后
render部分就已经完成了,这里面涉及的调度等会再组件更新部分进行学习。如果有哪些写的不正确的地方,请大家一定给我指正,谢谢。
- React源码系列一:React相关API
- React源码系列二:React Render之 FiberRoot
- React源码系列三:React.Render流程 二 之更新
- React源码系列四:React Fiber 架构
- React源码系列五:React Scheduler调度原理第一篇
- [React源码系列五:React Scheduler调度原理第二篇]....