Open lz-lee opened 5 years ago
初始渲染
ReactDOM.render 、ReactDOM.hydrate
组件内更新
setState
forceUpdate
replaceState(即将被舍弃)
创建 ReactRoot 包含React整个应用最顶点的对象
创建 FiberRoot 和 RootFiber
创建更新 update,用来更新调度,进入调度后 setState 或 ReactDOM.render 的后续操作都的调度器去管理的
ReactDOM对象有render、hydrate两个方法,render 方法是用在浏览器环境内, hydrate 用于服务端渲染,区别在于调用 legacyRenderSubtreeIntoContainer 方法第四个参数 false 和 true.
render
hydrate
legacyRenderSubtreeIntoContainer
false
true
const ReactDOM = { // ... hydrate(element: React$Node, container: DOMContainer, callback: ?Function) { // TODO: throw or warn if we couldn't hydrate? return legacyRenderSubtreeIntoContainer( null, element, container, true, callback, ); }, render( element: React$Element<any>, // React element container: DOMContainer, // 挂在节点 callback: ?Function, // 渲染结束的callback ) { return legacyRenderSubtreeIntoContainer( null, element, container, false, callback, ); } }
legacyRenderSubtreeIntoContainer 作用主要是生成 ReactRoot 并调用 ReactRoot 实例的 render 方法
ReactRoot
function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>, children: ReactNodeList, container: DOMContainer, forceHydrate: boolean, callback: ?Function, ) { ... // TODO: Without `any` type, Flow says "Property cannot be accessed on any // member of intersection type." Whyyyyyy. let root: Root = (container._reactRootContainer: any); // container 就是首次渲染传入的<div id="root">, 这个dom 肯定不存在 _reactRootContainer 属性 if (!root) { // Initial mount // 初次渲染,创建ReactRoot,并创建FiberRoot root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate, ); // 封装回调 if (typeof callback === 'function') { ... } // Initial mount should not be batched. // 初始渲染不是批量更新 DOMRenderer.unbatchedUpdates(() => { // render, hydrate 传入的 parentComponent 都是 null if (parentComponent != null) { root.legacy_renderSubtreeIntoContainer( parentComponent, children, callback, ); } else { // 调用 ReactRoot 的render方法 root.render(children, callback); } }); } else { // 更新逻辑 if (typeof callback === 'function') { const originalCallback = callback; callback = function() { const instance = DOMRenderer.getPublicRootInstance(root._internalRoot); originalCallback.call(instance); }; } // Update if (parentComponent != null) { root.legacy_renderSubtreeIntoContainer( parentComponent, children, callback, ); } else { root.render(children, callback); } return DOMRenderer.getPublicRootInstance(root._internalRoot); }
legacyCreateRootFromDOMContainer 函数的作用判断是不是 hydrate 渲染,ReactDOM.render 当然为 false,接着会判断 rootElement 有没有 ROOT_ATTRIBUTE_NAME 即 data-reactroot 属性来判断是不是服务端渲染,如果不是则 while 循环将 root 所有子节点全部删掉,返回new ReactRoot(container, isConcurrent, shouldHydrate)
legacyCreateRootFromDOMContainer
ReactDOM.render
rootElement
ROOT_ATTRIBUTE_NAME
data-reactroot
new ReactRoot(container, isConcurrent, shouldHydrate)
function legacyCreateRootFromDOMContainer( container: DOMContainer, // ReactDOM.render 传入的 root forceHydrate: boolean, // ReactDOM.render 为false ): Root { const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content. if (!shouldHydrate) { let warned = false; let rootSibling; while ((rootSibling = container.lastChild)) { if (__DEV__) { ... } // 把 root 的所有子节点删掉 container.removeChild(rootSibling); } } if (__DEV__) { ... } // Legacy roots are not async by default. // root 节点创建时同步的,isConcurrent: false const isConcurrent = false; return new ReactRoot(container, isConcurrent, shouldHydrate); }
接着看ReactRoot构造函数,构造函数里调用了 ReactFiberReconciler 里的 createContainer 方法,这个方法调用 createFiberRoot 生成一个 FiberRoot 对象,最后挂载到实例的 _internalRoot 上,
createContainer
createFiberRoot
FiberRoot
_internalRoot
function ReactRoot( container: Container, isConcurrent: boolean, hydrate: boolean, ) { const root = DOMRenderer.createContainer(container, isConcurrent, hydrate); this._internalRoot = root; } // ReactFiberReconciler.js export function createContainer( containerInfo: Container, isConcurrent: boolean, hydrate: boolean, ): OpaqueRoot { return createFiberRoot(containerInfo, isConcurrent, hydrate); }
legacyRenderSubtreeIntoContainer 方法里 root.render 调用的最终 ReactRoot 构造函数的 protoType 上定义了 render 方法,而它调用的是 ReactFiberReconciler 里的 updateContainer。
root.render
protoType
updateContainer
ReactRoot.prototype.render = function( children: ReactNodeList, callback: ?() => mixed, ): Work { // FiberRoot // FiberRoot 是根据 ReactDOM.render 方法的第二个参数创建出来的,创建的过程中同时创建 RootFiber, RootFiber.stateNode = FiberRoot, FiberRoot.current = RootFiber const root = this._internalRoot; const work = new ReactWork(); callback = callback === undefined ? null : callback; if (__DEV__) { warnOnInvalidCallback(callback, 'render'); } if (callback !== null) { work.then(callback); } DOMRenderer.updateContainer(children, root, null, work._onCommit); return work; };
DOMRenderer.updateContainer 中计算出一个 expirationTime 传入了 updateContainerAtExpirationTime.
DOMRenderer.updateContainer
expirationTime
updateContainerAtExpirationTime
// ReactFiberReconciler.js export function updateContainer( element: ReactNodeList, // <App /> container: OpaqueRoot, // FiberRoot parentComponent: ?React$Component<any, any>, callback: ?Function, ): ExpirationTime { // RootFiber const current = container.current; // 创建一个时间差 const currentTime = requestCurrentTime(); // 计算出一个时间,ConcurrentMode 会用到 const expirationTime = computeExpirationForFiber(currentTime, current); return updateContainerAtExpirationTime( element, container, parentComponent, expirationTime, callback, ); }
FiberRoot 和 RootFiber 接下来会讲到,先看 updateContainerAtExpirationTime 创建更新的过程,调用scheduleRootUpdate 方法将 RootFiber、 element、计算出的expirationTime、 callback 传入,在这个方法里,首先会调用createUpdate 创建一个 update 对象,enqueueUpdate 方法将 update 对象加入到 fiber 对象上的 updateQueue 里,scheduleWork 即开始执行调度。创建更新的过程到此为止。
RootFiber
scheduleRootUpdate
element
callback
createUpdate
update
enqueueUpdate
fiber
updateQueue
scheduleWork
export function updateContainerAtExpirationTime( element: ReactNodeList, // <App /> container: OpaqueRoot, parentComponent: ?React$Component<any, any>, expirationTime: ExpirationTime, callback: ?Function, ) { // TODO: If this is a nested container, this won't be the root. const current = container.current; if (__DEV__) { ... } // 目前版本React拿不到context,因为 parentComponent 为null const context = getContextForSubtree(parentComponent); if (container.context === null) { container.context = context; } else { container.pendingContext = context; } return scheduleRootUpdate(current, element, expirationTime, callback); } function scheduleRootUpdate( current: Fiber, element: ReactNodeList, expirationTime: ExpirationTime, callback: ?Function, ) { if (__DEV__) { ... } // update对象是用来标记React应用中需要更新的节点 const update = createUpdate(expirationTime); // Caution: React DevTools currently depends on this property // being called "element". // 设置update的相关属性 update.payload = {element}; callback = callback === undefined ? null : callback; if (callback !== null) { warningWithoutStack( typeof callback === 'function', 'render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback, ); update.callback = callback; } // 把update加入到 fiber 的updateQueue 里,一个节点上可能会产生很多次更新,需要 batchUpdates (批量更新) enqueueUpdate(current, update); // 有更新产生,告诉 React 开始调度 scheduleWork(current, expirationTime); return expirationTime; }
初次渲染 传入 APP 组件和 getElementById(root) 执行 ReactDOM.render 返回并执行 legacyRenderSubtreeIntoContainer
legacyRenderSubtreeIntoContainer 方法调用 legacyCreateRootFromDOMContainer 方法把 getElementById(root) 里的子节点清空,并把返回值 new ReactRoot 挂载到 getElementById(root) 节点的 _reactRootContainer 属性上
new ReactRoot
ReactRoot 生成实例时调用 react-reconcile 模块的 createContainer 传入 getElementById(root) 执行 createFiberRoot 生成一个 FiberRoot 对象挂载到实例的 _internalRoot
legacyRenderSubtreeIntoContainer 方法里 root.render 方法实际是调用 ReactRoot 原型上的render方法
ReactRoot.prototype.render 把子节点和实例生成的 _internalRoot FiberRoot 对象传入 react-reconcile 模块的 updateContainer 中
在 updateContainer 中 react 计算出一个 expirationTime 传入 updateContainerAtExpirationTime 调用 scheduleRootUpdate 中做三件事
createUpddate 创建update对象来标记 react 需要更新的点
enqueueUpdate 将 update 加入到 RootFiber 的 updateQueue 中
scheduleWork 根据任务的优先级进行调度更新
下一篇
React 创建更新的过程
创建更新的方式
初始渲染
ReactDOM.render 、ReactDOM.hydrate
组件内更新
setState
forceUpdate
replaceState(即将被舍弃)
ReactDom.render
步骤
创建 ReactRoot 包含React整个应用最顶点的对象
创建 FiberRoot 和 RootFiber
创建更新 update,用来更新调度,进入调度后 setState 或 ReactDOM.render 的后续操作都的调度器去管理的
初始渲染
ReactDOM对象有
render
、hydrate
两个方法,render
方法是用在浏览器环境内,hydrate
用于服务端渲染,区别在于调用legacyRenderSubtreeIntoContainer
方法第四个参数false
和true
.legacyRenderSubtreeIntoContainer
作用主要是生成ReactRoot
并调用ReactRoot
实例的render
方法legacyCreateRootFromDOMContainer
函数的作用判断是不是hydrate
渲染,ReactDOM.render
当然为false
,接着会判断rootElement
有没有ROOT_ATTRIBUTE_NAME
即data-reactroot
属性来判断是不是服务端渲染,如果不是则 while 循环将 root 所有子节点全部删掉,返回new ReactRoot(container, isConcurrent, shouldHydrate)
接着看
ReactRoot
构造函数,构造函数里调用了 ReactFiberReconciler 里的createContainer
方法,这个方法调用createFiberRoot
生成一个FiberRoot
对象,最后挂载到实例的_internalRoot
上,legacyRenderSubtreeIntoContainer
方法里root.render
调用的最终ReactRoot
构造函数的protoType
上定义了render
方法,而它调用的是 ReactFiberReconciler 里的updateContainer
。DOMRenderer.updateContainer
中计算出一个expirationTime
传入了updateContainerAtExpirationTime
.FiberRoot
和RootFiber
接下来会讲到,先看updateContainerAtExpirationTime
创建更新的过程,调用scheduleRootUpdate
方法将RootFiber
、element
、计算出的expirationTime
、callback
传入,在这个方法里,首先会调用createUpdate
创建一个update
对象,enqueueUpdate
方法将update
对象加入到fiber
对象上的updateQueue
里,scheduleWork
即开始执行调度。创建更新的过程到此为止。ReactDOM.render 总结
初次渲染 传入 APP 组件和 getElementById(root) 执行
ReactDOM.render
返回并执行legacyRenderSubtreeIntoContainer
legacyRenderSubtreeIntoContainer
方法调用legacyCreateRootFromDOMContainer
方法把 getElementById(root) 里的子节点清空,并把返回值new ReactRoot
挂载到 getElementById(root) 节点的 _reactRootContainer 属性上ReactRoot 生成实例时调用 react-reconcile 模块的 createContainer 传入 getElementById(root) 执行
createFiberRoot
生成一个FiberRoot
对象挂载到实例的 _internalRootlegacyRenderSubtreeIntoContainer 方法里
root.render
方法实际是调用 ReactRoot 原型上的render方法ReactRoot.prototype.render 把子节点和实例生成的 _internalRoot FiberRoot 对象传入 react-reconcile 模块的 updateContainer 中
在 updateContainer 中 react 计算出一个 expirationTime 传入 updateContainerAtExpirationTime 调用 scheduleRootUpdate 中做三件事
createUpddate 创建update对象来标记 react 需要更新的点
enqueueUpdate 将 update 加入到 RootFiber 的 updateQueue 中
scheduleWork 根据任务的优先级进行调度更新
下一篇