在阅读本文以前,建议先看一看下面这几篇文章:react
React组件: 开发者写的组件(class类型或者function类型等)。在执行时会被React解析成React元素。算法
React元素: 描述组件实例或DOM节点及其所需属性的普通对象。bash
DOM元素: DOM节点对象。架构
在React执行内部,上面三者之间的转换关系是下面这样的:app
React组件 --> React元素 --> DOM元素
复制代码
React组件之间能够层层嵌套会造成“组件树”,与之对应的就是:dom
React组件树 --> React Fiber树 --> DOM
复制代码
React元素在React执行时会被放入到React Fiber对象中。函数
app.js源码分析
import React from 'react';
import {render} from 'react-dom';
import HelloReact from './HelloReact';
render(<HelloReact name="Taylor" />, document.getElementById('root')); 复制代码
HelloReact.jspost
class HelloReact extends Component{
render() {
return (
<div> <span>Hello {this.props.name}</span> </div>
);
}
}
export default HelloReact
复制代码
咱们将程序的“首次渲染”过程分为三个阶段:构建fiberRoot,渲染(render) fiberRoot,提交(commit) fiberRoot。ui
fiberRoot是FiberRootNode的实例,其构造函数为
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.current = null;
this.containerInfo = containerInfo; // DOM容器元素,即document.getElementById('root')
this.pendingChildren = null;
this.pingCache = null;
this.finishedExpirationTime = NoWork;
this.finishedWork = null; // 执行到后面会被赋值由组件树解析而成的fiber树
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.firstBatch = null;
this.callbackNode = null;
this.callbackExpirationTime = NoWork;
this.firstPendingTime = NoWork;
this.lastPendingTime = NoWork;
this.pingTime = NoWork;
if (enableSchedulerTracing) {
this.interactionThreadID = tracing.unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
}
复制代码
fiberRoot的产生入口-legacyRenderSubtreeIntoContainer
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
...
var root = container._reactRootContainer; // 首次加载时返回值undefined
var fiberRoot = void 0;
if (!root) {
// 首次加载时赋值逻辑
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
fiberRoot = root._internalRoot;
// 首次加载不能批量更新.
unbatchedUpdates(function () {
updateContainer(children, fiberRoot, parentComponent, callback);
});
}
...
}
复制代码
fiberRoot建立函数-legacyCreateRootFromDOMContainer
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
...
return new ReactSyncRoot(container, LegacyRoot, shouldHydrate);
}
复制代码
ReactSyncRoot构造函数
function ReactSyncRoot(container, tag, hydrate) {
var root = createContainer(container, tag, hydrate);
this._internalRoot = root;
}
复制代码
createContainer函数
function createContainer(containerInfo, tag, hydrate) {
return createFiberRoot(containerInfo, tag, hydrate);
}
复制代码
createFiberRoot函数
function createFiberRoot(containerInfo, tag, hydrate) {
var root = new FiberRootNode(containerInfo, tag, hydrate);
// 循环结构,这就欺骗了类型系统,由于stateNode是任意类型
var uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
return root;
}
复制代码
createHostRootFiber函数
function createHostRootFiber(tag) {
var mode = void 0;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode | BatchedMode | StrictMode;
} else if (tag === BatchedRoot) {
mode = BatchedMode | StrictMode;
} else {
mode = NoMode;
}
...
return createFiber(HostRoot, null, null, mode);
}
复制代码
createFiber函数
var createFiber = function (tag, pendingProps, key, mode) {
return new FiberNode(tag, pendingProps, key, mode);
};
复制代码
FiberNode构造函数
function FiberNode(tag, pendingProps, key, mode) {
// 此处属性用于生成DOM节点实例
this.tag = tag; // 用于标识组件的类型,好比class组件、function组件、宿主组件等
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// 此处属性用于协调算法遍历Fiber节点树
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// 此处属性用于反作用(的调用,好比生命周期函数等)
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
this.alternate = null;
...
...
}
复制代码
上面各个函数之间来回调用,关系有些混乱,它们执行结束后获得的root
和fiberRoot
对象,其关系像下图这样:
fiberRoot对象是FiberRootNode的实例,其current
属性指向了FiberNode实例,FiberNode实例中的stateNode
属性又指向了fiberRoot对象。一种多么奇妙的循环设计! 这种设计方式在后续文章中进行讨论。
接下来开始执行unbatchedUpdates函数:
// 首次渲染不进行批量更新
unbatchedUpdates(function () {
// 此时的children是React元素而不是fiber对象
updateContainer(children, fiberRoot, parentComponent, callback);
});
复制代码
updateContainer函数
function updateContainer(element, container, parentComponent, callback) {
var current$$1 = container.current;
var currentTime = requestCurrentTime();
...
var suspenseConfig = requestCurrentSuspenseConfig();
// 这里定义了有效时间
var expirationTime = computeExpirationForFiber(currentTime, current$$1, suspenseConfig);
return updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, suspenseConfig, callback);
}
复制代码
updateContainerAtExpirationTime函数
function updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, suspenseConfig, callback) {
var current$$1 = container.current;
...
var context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
return scheduleRootUpdate(current$$1, element, expirationTime, suspenseConfig, callback);
}
复制代码
scheduleRootUpdate函数
function scheduleRootUpdate(current$$1, element, expirationTime, suspenseConfig, callback) {
...
// 建立一个“更新”
var update = createUpdate(expirationTime, suspenseConfig);
update.payload = { element: element };
...
// 将“更新”加入队列
enqueueUpdate(current$$1, update);
// 开始调度工做
scheduleWork(current$$1, expirationTime);
return expirationTime;
}
复制代码
在开始调度工做时(也就是开始执行scheduleWork
函数)的fiberRoot
和current$$1
的updateQueue
属性已经被加入相应的“更新-update”,其内部以下图所示:
scheduleWork函数被赋值scheduleUpdateOnFiber函数
var scheduleWork = scheduleUpdateOnFiber;
复制代码
function scheduleUpdateOnFiber(fiber, expirationTime) {
...
// 在这里要进入“渲染”阶段了
var callback = renderRoot(root, Sync, true);
while (callback !== null) {
callback = callback(true);
}
...
}
复制代码
在进入“渲染”阶段前,React内部建立了fiberRoot
,也就是React fiber树的入口。React会把组件树的根组件转换成React元素,而后封装成update
对象并将其加入到updateQueue
对象中。
fiberRoot
对象的stateNode
属性指向了FiberNode的实例,该实例被赋值到current$$1
中。最后current$$1
对象携带者updateQueue
进入下一(渲染)阶段。