React 初始化的函數函數為ReactDom.render(), 進行更新有三種方式 ReactDom.render(), setState, forceUpdate 等。
// render 定義(1)
// ReactDom 執行render 的方法
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMLegacy.js
// 目的: 調用legacyRenderSubtreeIntoContainer 函數
export function render(
element: React$Element<any>, // ReactElement 元素
container: Container, // 容器
callback: ?Function, // 回調函數
) {
// 執行 legacyRenderSubtreeIntoContainer 方法
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
備注: React 入口函數, 傳入的參數為element, 和container 等資訊, 回調函數一般不傳入, 傳入後會執行。
// legacyRenderSubtreeIntoContainer 函數定義(2)
// 定義 legacyRenderSubtreeIntoContainer 方法
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMLegacy.js
// 主體邏輯: 根據container.root 有無,判斷是否為初始後或更新,無: 執行生成fiberRoot, 再執行非批量
// 更新 ,有: 執行普通的更新
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>, // 初始為null
children: ReactNodeList,
container: Container,
forceHydrate: boolean, // false
callback: ?Function,
) {
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
// 初始化, 無屬性,
let root: RootType = (container._reactRootContainer: any);
let fiberRoot;
// 初始化
if (!root) {
// Initial mount
// 擷取_reactRootContainer 屬性
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate, // false
);
// 定義fiberRoot
fiberRoot = root._internalRoot;
if (typeof callback === 'function') { // 回調函數
const originalCallback = callback;
callback = function() {
// 執行個體
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance); // 最原始回調函數
};
}
// Initial mount should not be batched.
// 非批量更新的方式
unbatchedUpdates(() => { // 設定執行環境
// 更新的節點, fiberRoot, parentComponent 父節點 callback : 回調
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
// 當具有root 時候, 更新的時候
fiberRoot = root._internalRoot; // fiberRoot
if (typeof callback === 'function') { // 具有回調函數
const originalCallback = callback;
callback = function() { // 包裹回調函數
// 執行個體
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance); // 回調函數調用
};
}
// Update // 進行更新, 非批量更新
updateContainer(children, fiberRoot, parentComponent, callback); // null 回調
}
return getPublicRootInstance(fiberRoot);
}
備注: 通過container._reactRootContainer 建構基本的資訊,基本的結構資訊包含fiberRoot 和rootFiber, 應用更新的初始化和更新都是從fiberRoot開始。對回調函數進行包裹處理, 最後在unbatchedUpdates 執行回調函數, 初始化時,想最快速度展示頁面,進而是非批量同步更新。 root 存在時, 也可以進行更新,連續調用React.ReactDom, 會走存在的路線。
// 定義legacyCreateRootFromDOMContainer 方法(3)
// 定義legacyCreateRootFromDOMContainer 方法,
// 擷取root // container._reactRootContainer
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMLegacy.js
/// 主題邏輯: 是否需要hydrate條件生成, 調用createLegacyRoot
function legacyCreateRootFromDOMContainer(
container: Container, // container
forceHydrate: boolean, // false
): RootType {
// 是否需要hydrate 的判斷, 可以忽略部分, 現在隻考察shouldHydrate 為false 的情況
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// First clear any existing content.
if (!shouldHydrate) {
let warned = false;
let rootSibling;
// 清空原來container 中所有子元素
while ((rootSibling = container.lastChild)) {
container.removeChild(rootSibling);
}
}
return createLegacyRoot(
container,
shouldHydrate
? {
hydrate: true,
}
: undefined,
);
}
// 定義shouldHydrateDueToLegacyHeuristic 方法(4)
// 函數shouldHydrateDueToLegacyHeuristic 定義
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMLegacy.js
// 主體邏輯: 擷取rootElement 元素, 并 進行判斷
function shouldHydrateDueToLegacyHeuristic(container) {
const rootElement = getReactRootElementInContainer(container);
return !!(
rootElement &&
rootElement.nodeType === ELEMENT_NODE &&
rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
);
}
// 定義getReactRootElementInContainer 方法(5)
// getReactRootElementInContainer 定義
代碼位置: src/react/packages/react-dom/src/client/ReactDOMLegacy.js
主題邏輯 : 先判斷存在,在判斷是否為document, 最後傳回第一個元素
function getReactRootElementInContainer(container: any) {
if (!container) {
return null;
}
// 是否為document
if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
// 第一個子節點
return container.firstChild;
}
}
// 定義 createLegacyRoot 方法(6)
// 調用 createLegacyRoot 方法
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMRoot.js
// 主題邏輯: // 傳入LegacyRoot 參數, 調用構造函數, 傳回值為container._reactRootContainer 對應的值
export function createLegacyRoot(
container: Container,
options?: RootOptions,
): RootType {
// LegacyRoot 遺留類型的root options : null
// new ReactDOMBlockingRoot() 執行個體 對應root 即 container.reactRootContainer
return new ReactDOMBlockingRoot(container, LegacyRoot, options); // LegacyRoot
}
//備注: src/react/packages/react-reconciler/src/ReactRootTags.js
export type RootTag = 0 | 1 | 2;
export const LegacyRoot = 0; // 即遺留的root
export const BlockingRoot = 1; // 阻塞的root類型
export const ConcurrentRoot = 2; // concurrentRoot
// 構造函數ReactDOMBlockingRoot 定義(7)
// ReactDOMBlockingRoot 構造函數的定義
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMRoot.js
// 主題邏輯; 在原執行個體上,增加屬性, _internalRoot 對應fiberRoot
function ReactDOMBlockingRoot(
container: Container,
tag: RootTag, // rootTag的類型
options: void | RootOptions,
) {
// root / contain._reactRootContainer 的結果就是 ReactDOMBlockingRoot 執行個體
// this._internalRoot 對應fiberRoot 屬性
this._internalRoot = createRootImpl(container, tag, options); //
// container 容器, tag 類型 options 參數
}
// 函數createRootImpl 定義(8)
// 定義 createRootImpl 函數
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMRoot.js
// 主題邏輯: 1 fiberRoot的構造, 2 fiberRoot 和container關系, 3, 基本事件的挂載
// tag : 0 , options: undefined // root 對應fiberRoot
function createRootImpl(
container: Container, // 容器類型
tag: RootTag, // rootTag 的類型
options: void | RootOptions,
) {
// Tag is either LegacyRoot or Concurrent Root
// 服務端渲染的參數
const hydrate = options != null && options.hydrate === true;
// 服務端渲染的回調參數
const hydrationCallbacks =(options != null && options.hydrationOptions) || null;
const mutableSources =(options != null &&options.hydrationOptions != null && options.hydrationOptions.mutableSources) ||
null;
// 定義root 屬性
// container 容器 tag: 0 hydrate : false, hydrationCallbacks : null
// root 對應為fiberRoot
const root = createContainer(container, tag, hydrate, hydrationCallbacks);
markContainerAsRoot(root.current, container); //
// 設定container 與root.current 的關系
const containerNodeType = container.nodeType;
// enableEagerRootListeners // true; 是否支援挂載到元素上
// true
if (enableEagerRootListeners) {
// 根容器的節點, react 17 中的事件挂載
const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement); // 挂載的節點
} else {
// 其他方式
if (hydrate && tag !== LegacyRoot) {
const doc =
containerNodeType === DOCUMENT_NODE
? container
: container.ownerDocument;
// We need to cast this because Flow doesn't work
// with the hoisted containerNodeType. If we inline
// it, then Flow doesn't complain. We intentionally
// hoist it to reduce code-size.
eagerlyTrapReplayableEvents(container, ((doc: any): Document));
} else if (
containerNodeType !== DOCUMENT_FRAGMENT_NODE &&
containerNodeType !== DOCUMENT_NODE
) {
ensureListeningTo(container, 'onMouseEnter', null);
}
}
// 服務端的參數
if (mutableSources) {
for (let i = 0; i < mutableSources.length; i++) {
const mutableSource = mutableSources[i];
registerMutableSourceForHydration(root, mutableSource);
}
}
// root 的傳回
return root; // root 對應為fiberRoot 對象
}
// 函數createContainer 定義(9)
// 建立FiberRoot的方法
// 代碼位置: src/react/packages/react-reconciler/src/ReactFiberReconciler.old.js
// 主體邏輯: 調用createFiberRoot 函數
// containerInfo: container tag: 0, // hydrate : false, hydrationCallbacks: null
export function createContainer(
containerInfo: Container,
tag: RootTag, // rootTag 的類型
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): OpaqueRoot {
return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}
// 函數createFiberRoot 定義(10)
// 建立FiberRoot的節點
// 代碼位置: src/react/packages/react-reconciler/src/ReactFiberRoot.old.js
// 主題邏輯: 1 建立fiberRoot 和rootFiber, 設立兩者關系, 初始化rootFiber 中updateQueue
export function createFiberRoot(
containerInfo: any,
tag: RootTag, // rootTag的類型
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
// 建立FiberRoot 節點 containerInfo 容器的資訊 tag:rooTag的資訊
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
//
if (enableSuspenseCallback) {
root.hydrationCallbacks = hydrationCallbacks;
}
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
// 擷取rootFiber
const uninitializedFiber = createHostRootFiber(tag); // 初始化rootFiber
root.current = uninitializedFiber; // FiberRoot 的current 執行rootFiber
uninitializedFiber.stateNode = root;
// rootFiber.stateNode 指向fiberRoot
initializeUpdateQueue(uninitializedFiber); // rootFiber
return root;
}
// FiberRootNode 構造函數定義(11)
// FiberRootNode 構造函數定義 // containerInfo 容器 tag: 0 hydrate: false
// 代碼位置: src/react/packages/react-reconciler/src/ReactFiberRoot.old.js
// 主題邏輯: 根據tag 建立fiberRoot
function FiberRootNode(containerInfo, tag, hydrate) {
// fiberRoot 對象,1: 整個應用的起點, 2, 包含應用挂載的目标節點
// 3 記錄整個應用的更新過程的全部資訊
// containerInfo 容器 tag rootTag 類型 hydrate
this.tag = tag; // tag: 0 // rootTag 類型
// react的各種模式
// NoMode = 0b00000;
// StrictMode = 0b00001;
// BlockingMode = 0b00010;
// ConcurrentMode = 0b00100;
// ProfileMode = 0b01000;
// root節點,render方法接收的第二個參數
this.containerInfo = containerInfo; // 容器資訊
// 隻有在持久更新中會用到,也就是不支援增量更新的平台,react-dom不會用到
this.pendingChildren = null; // 持久化更新的資訊
// 目前應用對應的Fiber對象,是Root Fiber
this.current = null; // root節點對應fiber 節點
this.pingCache = null;
// The earliest and latest priority levels that are suspended from committing.
// 最老和新的在送出的時候被挂起的任務
// 已經完成的任務的FiberRoot對象,如果你隻有一個Root,那他永遠隻可能是這個Root對應的Fiber,或者是null
// 在commit階段隻會處理這個值對應的任務
this.finishedWork = null;
// 在任務被挂起的時候,通過setTimeout設定的傳回的内容
// 用來下一次如果有新的任務挂起時, 清除沒有觸發的timeout
// 在任務被挂起的時候通過setTimeout設定的傳回内容,用來下一次如果有新的任務挂起時清理還沒觸發的timeout
this.timeoutHandle = noTimeout; // -1
// 頂層的context 對象, 隻有主動調用renderSubtreeIntoContainer 的時候才會用
// 頂層context對象,隻有主動調用`renderSubtreeIntoContainer`時才會有用
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoLanePriority; //
this.eventTimes = createLaneMap(NoLanes); // 31位的空的數組, 中間的值位 NoLanes: 0b0000000000000000000000000000000;
this.expirationTimes = createLaneMap(NoTimestamp); // 過期時間 -1
this.pendingLanes = NoLanes; // 0b0000000000000000000000000000000;
this.suspendedLanes = NoLanes; // 0b0000000000000000000000000000000;
this.pingedLanes = NoLanes; // 0b0000000000000000000000000000000;
this.expiredLanes = NoLanes; // 過期的 0b0000000000000000000000000000000;
this.mutableReadLanes = NoLanes; // 0b0000000000000000000000000000000;
this.finishedLanes = NoLanes; // 完成的 0b0000000000000000000000000000000;
this.entangledLanes = NoLanes; // 0b0000000000000000000000000000000;
this.entanglements = createLaneMap(NoLanes); // 0b0000000000000000000000000000000;
// 支援Hrate
if (supportsHydration) {
this.mutableSourceEagerHydrationData = null;
}
// 能夠排程追蹤,
if (enableSchedulerTracing) {
this.interactionThreadID = unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
if (enableSuspenseCallback) {
this.hydrationCallbacks = null;
}
}
// createLaneMap 定義(12)
// 建立一個LaneMAp
// 代碼位置: src/react/packages/react-reconciler/src/ReactFiberLane.js
// 主題邏輯: 建立一個包含31個元素的數組, 值為傳入的參數
export function createLaneMap<T>(initial: T): LaneMap<T> {
// Intentionally pushing one by one.
// https://v8.dev/blog/elements-kinds#avoid-creating-holes
// 空數組
const laneMap = [];
// 31
for (let i = 0; i < TotalLanes; i++) {
laneMap.push(initial);
}
return laneMap; // []
}
// createHostRootFiber 函數定義(13)
// 建立HostRootFiber 節點 tag : 0
代碼位置: src/react/packages/react-reconciler/src/ReactFiber.old.js
主題邏輯: 根據傳入的tag 确定mode , 調用建立rootFiber
export function createHostRootFiber(tag: RootTag): Fiber {
// 模式定義
let mode;
// 當模式為ConcurrentMode m模式下
if (tag === ConcurrentRoot) {
// mode 增加嚴格, concurrent
mode = ConcurrentMode | BlockingMode | StrictMode;
// ConcurrentMode 模式 BlockingMode : 模式 StrictMode: 模式
} else if (tag === BlockingRoot) {
// Blocak //
mode = BlockingMode | StrictMode;// BlockingMode 模式, StrictMode 模式
} else {
// 沒有模式
mode = NoMode;
}
if (enableProfilerTimer && isDevToolsPresent) {
mode |= ProfileMode;
}
// 建立Fiber // 比對模式
return createFiber(HostRoot, null, null, mode);
// HostRoot
}
// createFiber 函數定義 (14)
// 建立Fiber 節點的函數 // 建立HostRootFiber 節點
// 代碼位置: src/react/packages/react-reconciler/src/ReactFiber.old.js
// 主題邏輯: 調用構造函數傳回rootFiber, tag 為3, 表示root 節點
// pendingProps : null , key: null, tag: HostRoot
const createFiber = function(
tag: WorkTag,
pendingProps: mixed, // {}
key: null | string, //
mode: TypeOfMode,
): Fiber {
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
// 建立的FiberNode節點
return new FiberNode(tag, pendingProps, key, mode);
};
// FiberNode 構造函數 (15)
// FiberNode 的構造函數
// 代碼位置: src/react/packages/react-reconciler/src/ReactFiber.old.js
// 主體邏輯: 定義了構造函數
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
// 标記不同的元件類型
this.tag = tag; // 類型
// ReactElement裡面的key
this.key = key; // key
// ReactElement.type,也就是我們調用`createElement`的第一個參數
this.elementType = null;
// The resolved function/class/ associated with this fiber.
// 異步元件resolved之後傳回的内容,一般是`function`或者`class`
this.type = null; // 類型
// The local state associated with this fiber.
// 跟目前Fiber相關本地狀态(比如浏覽器環境就是DOM節點)
this.stateNode = null; // dom 節點 // dom 執行個體或者class 執行個體
// Fiber
// 指向他在Fiber節點樹中的`parent`,用來在處理完這個節點之後向上傳回
this.return = null; // 傳回的結構
// 單連結清單樹結構
// 指向自己的第一個子節點
this.child = null; // 子節點
// 指向自己的兄弟結構
// 兄弟節點的return指向同一個父節點
this.sibling = null; // 兄弟節點
this.index = 0; // 下标, 記錄位置
// ref屬性
this.ref = null; // ref , 最後使用de shui
// 新的變動帶來的新的props
this.pendingProps = pendingProps; // 待更新props
// 上一次渲染完成之後的props
this.memoizedProps = null; // 已經更新的props
// 該Fiber對應的元件産生的Update會存放在這個隊列裡面
this.updateQueue = null; // 更新隊列
// 上一次渲染的時候的state
this.memoizedState = null; // 更新的隊列
this.dependencies = null; // 同fiber 相關的context 事件
// 處于什麼模式 concurerntMode 或者其他模式
// 用來描述目前Fiber和他子樹的`Bitfield`
// 共存的模式表示這個子樹是否預設是異步渲染的
// Fiber被建立的時候他會繼承父Fiber
// 其他的辨別也可以在建立的時候被設定
// 但是在建立之後不應該再被修改,特别是他的子Fiber建立之前
this.mode = mode; // 受父節點控制
// Effects
// Effect
// 用來記錄Side Effect
this.flags = NoFlags; // 副作用标示 PLACEMENT UPDATE , DELETE
// 單連結清單用來快速查找下一個side effect
this.nextEffect = null;
// 子樹中第一個side effect
this.firstEffect = null;
// 子樹中最後一個side effect
this.lastEffect = null;
// 代表任務在未來的哪個時間點應該被完成
// 不包括他的子樹産生的任務
this.lanes = NoLanes; // 過期事件, 二進制位
// 快速确定子樹中是否有不在等待的變化
this.childLanes = NoLanes; // 子節點的
// 在Fiber樹更新的過程中,每個Fiber都會有一個跟其對應的Fiber
// 我們稱他為`current <==> workInProgress`
// 在渲染完成之後他們會交換位置
this.alternate = null; // 指向舊的fiber
// profiler 元件,計算react 的時間
if (enableProfilerTimer) {
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
}
// initializeUpdateQueue 定義(16)
// 初始化更新隊列 fiber: uninitlialFiber rootFiber
// 代碼位置: src/react/packages/react-reconciler/src/ReactUpdateQueue.old.js
// 主題邏輯: 給rootFiber 中的updateQueue 增加一個初始的queue
export function initializeUpdateQueue<State>(fiber: Fiber): void {
// 設定更新隊列
// 設定更新隊列
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState, // 原來的更新的基礎
// 記錄資料結構, 開始
firstBaseUpdate: null, // 最開始BasUpdate
// 記錄資料結構, 結束
lastBaseUpdate: null,
shared: {
pending: null,
},
effects: null, // 目前update 的副作用
};
// 更新隊列
// rootFiber 中的updateQueue ,設定一個初始值
fiber.updateQueue = queue; // rootFiber 的updateQueue 設定更新
// 給uninitlialFiber
// 設定rootFiber的更新隊列
}
// markContainerAsRoot 定義(17)
// hostRoot fiberRoot.current node: container
/// 定義markContainerAsRoot
// 代碼位置: src/react/packages/react-dom/src/client/ReactDOMComponentTree.js
// 主題邏輯: 設定container.internalContainerInstanceKey 指向rootFiber
export function markContainerAsRoot(hostRoot: Fiber, node: Container): void {
// container. internalContainerInstanceKey = fiberRoot.current
// 設定指向
node[internalContainerInstanceKey] = hostRoot;
}
// 備注資訊:
const randomKey = Math.random()
.toString(36)
.slice(2);
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
// listenToAllSupportedEvents 定義(18)
// 當enableEagerRootListeners 對container 的元素的綁定
// rootContainerElement: container
// 代碼位置: src/react/packages/react-dom/src/events/DOMPluginEventSystem.js
// 主題邏輯: 将觸發的事件綁定的container , 冒泡階段和觸發分别綁定
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
// ture
if (enableEagerRootListeners) {
// 初始為undefined
if ((rootContainerElement: any)[listeningMarker]) {
return;
}
// 初始false
(rootContainerElement: any)[listeningMarker] = true;
// 各種事件類型
allNativeEvents.forEach(domEventName => {
// 不進行冒泡代理的事件和 必須要綁定在指定元素的事件
if (!nonDelegatedEvents.has(domEventName)) {
// 冒泡階段的事件代理
listenToNativeEvent(
domEventName, // 事件類型
false,
((rootContainerElement: any): Element),
null,
);
}
// 在觸發階段的綁定
listenToNativeEvent(
domEventName,
true,
((rootContainerElement: any): Element),
null,
);
});
}
}
// 備注
export const deferRenderPhaseUpdateToNextBatch = true;
流程圖
總結資訊:
1 通過ReactDom.render() 進行初始化流程, 再次更新的時候也可以通過ReactDom.render() 進行更新。
2 container.reactRootContainer 為一個ReactDOMBlockingRoot執行個體。執行個體的_internalRoot 為fiberRoot ,
3 建立rootFiber 對象, 設定rootFiber 與container 的關系,在遊覽器下設定部分事件挂載在container.
4 初始化queue 添加到rootFiber 的updateQueue上。