一、初次渲染流程圖
二、下載源碼到本地
三、Render 爲 入口
1. 導出 render
packages/react-dom/src/client/ReactDOM.js
import { findDOMNode, render, hydrate, unstable_renderSubtreeIntoContainer, unmountComponentAtNode, } from './ReactDOMLegacy'; export { createPortal, batchedUpdates as unstable_batchedUpdates, flushSync, Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, ReactVersion as version, // Disabled behind disableLegacyReactDOMAPIs findDOMNode, hydrate, render, // 此處導出 unmountComponentAtNode, // exposeConcurrentModeAPIs createRoot, flushControlled as unstable_flushControlled, scheduleHydration as unstable_scheduleHydration, // Disabled behind disableUnstableRenderSubtreeIntoContainer renderSubtreeIntoContainer as unstable_renderSubtreeIntoContainer, // enableCreateEventHandleAPI createEventHandle as unstable_createEventHandle, // TODO: Remove this once callers migrate to alternatives. // This should only be used by React internals. runWithPriority as unstable_runWithPriority, };
2. 當初始化渲染時,刪除 container 內其他額外的元素,並在 root 上掛載 _internalRoot
packages/react-dom/src/client/ReactDOMLegacy.js
import { findHostInstanceWithNoPortals, updateContainer, unbatchedUpdates, getPublicRootInstance, findHostInstance, findHostInstanceWithWarning, } from 'react-reconciler/src/ReactFiberReconciler'; export function render( element: React$Element<any>, container: Container, callback: ?Function, ) { invariant( isValidContainer(container), 'Target container is not a DOM element.', ); if (__DEV__) { const isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined; if (isModernRoot) { console.error( 'You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.createRoot(). This is not supported. ' + 'Did you mean to call root.render(element)?', ); } } return legacyRenderSubtreeIntoContainer( null, element, container, false, callback, ); } function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>, children: ReactNodeList, container: Container, forceHydrate: boolean, callback: ?Function, ) { if (__DEV__) { topLevelUpdateWarnings(container); warnOnInvalidCallback(callback === undefined ? null : callback, 'render'); } let root = container._reactRootContainer; let fiberRoot: FiberRoot; if (!root) { // Initial mount root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate, ); } } /** * 當初始化渲染時,刪除 container 內其他額外的元素 */ function legacyCreateRootFromDOMContainer( container: Container, forceHydrate: boolean, ): RootType { // First clear any existing content. if (!forceHydrate) { let rootSibling; while ((rootSibling = container.lastChild)) { container.removeChild(rootSibling); } } return createLegacyRoot( container, forceHydrate ? { hydrate: true, } : undefined, ); }
3. FirberRoot 對象,RootFirber 對象創建的過程
packages/react-dom/src/client/ReactDOMRoot.js
export function createLegacyRoot( container: Container, options?: RootOptions, ): RootType { return new ReactDOMLegacyRoot(container, options); } function ReactDOMLegacyRoot(container: Container, options: void | RootOptions) { this._internalRoot = createRootImpl(container, LegacyRoot, options); } /** * 創建 FirberRoot 對象 * 並在 FirberRoot 上掛載 rootFirber * 並將其返回到 _internalRoot 對象上,此時 _internalRoot = FirberRoot */ function createRootImpl( container: Container, tag: 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; const isStrictMode = options != null && options.unstable_strictMode === true; let concurrentUpdatesByDefaultOverride = null; if (allowConcurrentByDefault) { concurrentUpdatesByDefaultOverride = options != null && options.unstable_concurrentUpdatesByDefault != null ? options.unstable_concurrentUpdatesByDefault : null; } // 將 id="root" 轉化爲 Firber 對象 const root = createContainer( container, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, ); // 重新 node[__reactContainer] = #idrootFirber markContainerAsRoot(root.current, container); const rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; listenToAllSupportedEvents(rootContainerElement); if (mutableSources) { for (let i = 0; i < mutableSources.length; i++) { const mutableSource = mutableSources[i]; registerMutableSourceForHydration(root, mutableSource); } } return root; }
4. 選擇 創建Firber 的方式?
packages/react-reconciler/src/ReactFiberReconciler.js
import { createContainer as createContainer_old, getCurrentUpdatePriority as getCurrentUpdatePriority_old, } from './ReactFiberReconciler.old'; import { createContainer as createContainer_new, } from './ReactFiberReconciler.new'; export const createContainer = enableNewReconciler ? createContainer_new : createContainer_old;
5. createContainer_old 創建 Firber 的方式( 此處爲重點的重點,包括React的重點流程在此處,可多次反覆查看代碼 )
import { createFiberRoot } from './ReactFiberRoot.old'; export function createContainer( containerInfo: Container, tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, isStrictMode: boolean, concurrentUpdatesByDefaultOverride: null | boolean, ): OpaqueRoot { return createFiberRoot( containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, ); } /** * 1. 創建 FiberRoot * 2. 創建 RootFiber * 3. 在 Fiber.current 上掛載 RootFiber * 4. 在 RootFiber.stateNode 掛載 FiberRoot * 5. 初始化隊列: 在 RootFiber 上掛載 updateQueue */ export function createFiberRoot( containerInfo: any, tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, isStrictMode: boolean, concurrentUpdatesByDefaultOverride: null | boolean, ): FiberRoot { 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, isStrictMode, concurrentUpdatesByDefaultOverride, ); // 在 Fiber.current 上掛載 RootFiber root.current = uninitializedFiber; // 在 RootFiber.stateNode 掛載 FiberRoot uninitializedFiber.stateNode = root; if (enableCache) { const initialCache = new Map(); root.pooledCache = initialCache; const initialState = { element: null, cache: initialCache, }; uninitializedFiber.memoizedState = initialState; } else { const initialState = { element: null, }; uninitializedFiber.memoizedState = initialState; } // 初始化更新隊列 initializeUpdateQueue(uninitializedFiber); return root; }
6. 初次創建 RootFiber
/** * 創建 RootFiber,並返回 Firber * tag = 0 */ export function createHostRootFiber( tag: RootTag, isStrictMode: boolean, concurrentUpdatesByDefaultOverride: null | boolean, ): Fiber { let mode; if (tag === ConcurrentRoot) { mode = ConcurrentMode; if (isStrictMode === true) { mode |= StrictLegacyMode; if (enableStrictEffects) { mode |= StrictEffectsMode; } } else if (enableStrictEffects && createRootStrictEffectsByDefault) { mode |= StrictLegacyMode | StrictEffectsMode; } if ( // We only use this flag for our repo tests to check both behaviors. // TODO: Flip this flag and rename it something like "forceConcurrentByDefaultForTesting" !enableSyncDefaultUpdates || // Only for internal experiments. (allowConcurrentByDefault && concurrentUpdatesByDefaultOverride) ) { mode |= ConcurrentUpdatesByDefaultMode; } } else { mode = NoMode; } if (enableProfilerTimer && isDevToolsPresent) { // Always collect profile timings when DevTools are present. // This enables DevTools to start capturing timing at any point– // Without some nodes in the tree having empty base times. mode |= ProfileMode; } return createFiber(HostRoot, null, null, mode); } // 將 RootFiber 的格式 以實例的形式返回 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 return new FiberNode(tag, pendingProps, key, mode); }; // RootFiber 的格式, // 此處的 pendingProps = null // mode = 0b000000 function FiberNode( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ) { // Instance 實例 // 判斷是否是初次,0=初次,1=不初次 this.tag = tag; // 唯一標識 this.key = key; // dom 的標籤 this.elementType = null; // JSX的類型,Component? Function? String this.type = null; // dom 元素 this.stateNode = null; // Fiber // 父級 this.return = null; // 子級 Firber this.child = null; // 同級 Firber this.sibling = null; this.index = 0; // ref 掛載 this.ref = null; // 暫定 this.pendingProps = pendingProps; this.memoizedProps = null; // 隊列 this.updateQueue = null; this.memoizedState = null; this.dependencies = null; this.mode = mode; // Effects this.flags = NoFlags; this.subtreeFlags = NoFlags; this.deletions = null; this.lanes = NoLanes; this.childLanes = NoLanes; this.alternate = null; if (enableProfilerTimer) { // Note: The following is done to avoid a v8 performance cliff. // // Initializing the fields below to smis and later updating them with // double values will cause Fibers to end up having separate shapes. // This behavior/bug has something to do with Object.preventExtension(). // Fortunately this only impacts DEV builds. // Unfortunately it makes React unusably slow for some applications. // To work around this, initialize the fields below with doubles. // // Learn more about this here: // https://github.com/facebook/react/issues/14365 // https://bugs.chromium.org/p/v8/issues/detail?id=8538 this.actualDuration = Number.NaN; this.actualStartTime = Number.NaN; this.selfBaseDuration = Number.NaN; this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization. // This won't trigger the performance cliff mentioned above, // and it simplifies other profiler code (including DevTools). this.actualDuration = 0; this.actualStartTime = -1; this.selfBaseDuration = 0; this.treeBaseDuration = 0; } if (__DEV__) { // This isn't directly used but is handy for debugging internals: this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugNeedsRemount = false; this._debugHookTypes = null; if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { Object.preventExtensions(this); } } }
7. 初始化隊列,並將其掛載到 RootFiber 上
export function initializeUpdateQueue<State>(fiber: Fiber): void { const queue: UpdateQueue<State> = { baseState: fiber.memoizedState, firstBaseUpdate: null, lastBaseUpdate: null, shared: { pending: null, interleaved: null, lanes: NoLanes, }, effects: null, // 更新事件 }; // 掛載隊列 fiber.updateQueue = queue; }