# react解析: render的中的update(四)
感谢 yck: 剖析 React 源码解析 (opens new window),本篇文章是在读完他的文章的基础上,将他的文章进行拆解和加工,加入我自己的一下理解和例子,便于大家理解。觉得yck (opens new window)写的真的很棒 。React 版本为 16.8.6,关于源码的阅读,可以移步到yck react源码解析 (opens new window)
上一章节说到,不存在root数据节点,即通过createFiberRoot 函数创建FiberRoot,FiberRoot
对象是整个React应用的起点,同时也记录了整个React应用更新过程中的各种信息。
下面将要聊到的就是,当root呗创建后,还会发生什么👇👇
# legacyRenderSubtreeIntoContainer 函数
下面衔接上一部分内容,不懂得可以查看上一章节。
yck: ReactDOM 源码 554行 legacyRenderSubtreeIntoContainer (opens new window)
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
forceHydrate: boolean,
callback: ?Function,
) {
// 初始化时,container 肯定没有 _reactRootContainer属性
let root: Root = (container._reactRootContainer: any);
if (!root) {
// 省略创建root部分
unbatchedUpdates(() => {
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在root刚刚被创建时,parentComponent
一般都为null;
unbatchedUpdates
函数在这里作用是:告知React内部不进行批量更新,即不用将多个setState合并为一个;
(setState在后面的章节我们将会说到)
那么这里实际调用的就是root.render函数,root是ReactRoot实例对象,即调用 root.render函数 == ReactRoot.prototype.render函数
。
# ReactRoot.prototype.render 函数
yck: ReactRoot 源码 377行 ReactRoot.prototype.render (opens new window)
ReactRoot.prototype.render = function(
children: ReactNodeList,
callback: ?() => mixed,
): Work {
// 这里指 FiberRoot
const root = this._internalRoot;
const work = new ReactWork();
callback = callback === undefined ? null : callback;
// 如果有 callback,就 push 进 work 中的数组
if (callback !== null) {
work.then(callback);
}
// work._onCommit 就是用于执行所有回调函数的
updateContainer(children, root, null, work._onCommit);
return work;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
函数中的参数children
即是ReactElement节点对象,callback
为回调函数。ReactWork
实例对象的主要作用就是维护一个回调数组,可查看yck: ReactWork 源码 327行 (opens new window),如果传入参数中存在callback,就将其挂载ReactWork
实例对象中;
下面来看看updateContainer函数会做什么。
# updateContainer 函数
yck: ReactFiberReconciler 源码 284行 updateContainer (opens new window)
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
const current = container.current;
// 计算时间
const currentTime = requestCurrentTime();
// expirationTime 代表优先级,数字越大优先级越高
// sync 的数字是最大的,所以优先级也是最高的
const expirationTime = computeExpirationForFiber(currentTime, current);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
callback,
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
container.current
即是从FiberRoot中取出RootFiber
对象,currentTime
就是当前距离React应用初始化的时间。 **expirationTime
字面意思就是过期时间,后面我会专门花一章的时间来介绍这两个时间,这两个时间也是React应用任务调度的重点。
# scheduleRootUpdate函数
updateContainerAtExpirationTime函数实际调用的就是scheduleRootUpdate
函数,下面来说一下scheduleRootUpdate
函数的作用。
yck: ReactFiberReconciler 源码 114行 scheduleRootUpdate (opens new window)
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// 创建一个 update,就是内部有几个属性的对象
const update = createUpdate(expirationTime);
update.payload = {element};
// render中的回调函数
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
flushPassiveEffects();
// 把 update 入队,内部就是一些创建或者获取 queue(链表结构),然后给链表添加一个节点的操作
enqueueUpdate(current, update);
scheduleWork(current, expirationTime);
return expirationTime;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
下面就是update对象其中的属性:
// update对象属性
export type Update<State> = {
// 更新的过期时间
expirationTime: ExpirationTime,
// export const UpdateState = 0;
// export const ReplaceState = 1;
// export const ForceUpdate = 2;
// export const CaptureUpdate = 3;
// 指定更新的类型,值为以上几种
tag: 0 | 1 | 2 | 3,
// 更新内容,比如`setState`接收的第一个参数
payload: any,
// 对应的回调,`setState`,`render`都有
callback: (() => mixed) | null,
// 指向下一个更新
next: Update<State> | null,
// 指向下一个`side effect`
nextEffect: Update<State> | null,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
udate对象会被插入到React应用维护的任务队列中,不管你是setState还是ReactDOM.render造成的 React应用 更新都是如此。这个函数核心作用就是创建或者获取一个队列,然后把 update 对象插入队列进行更新。scheduleWork
函数就是任务调度的东西了。
更多内容:
react解析: React.createElement(一) (opens new window)
react解析: React.Children(二) (opens new window)
react解析: render的FiberRoot(三) (opens new window)
参考:
yck: 剖剖析 React 源码 (opens new window)
Jokcy 的 《React 源码解析》: react.jokcy.me/ (opens new window)
ps: 顺便推一下自己的个人公众号:Yopai,有兴趣的可以关注,每周不定期更新,分享可以增加世界的快乐
男性,武汉工作,会点Web,擅长Javascript.
技术或工作问题交流,可联系微信:1169170165.