在閱讀本文以前,建議先看一看下面這幾篇文章: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
進入下一(渲染)階段。