首先 我这里 有一份配置好的 react源码配置 地址如下
仓库 地址
git地址:git@github.com:544076724/react16-source.git
下载下来间接 yarn start启动就能够了
前言:react我的项目是基于Monorepo 构造开发的,后续文章中所呈现的地址都是咱们工程的
src/react/packages/ 这一级来找的具体包
接下来咱们简略做下剖析
咱们这篇外面临时不波及太多的优先级工作,react源码里的优先级工作 是通过计算一个过期工夫来计算的 他们本人实现的一套算法 这里临时不介绍这个算法 大家晓得这个优先级算法是requestIdleCallback的欠缺版 就能够
咱们晓得后面说了fiber构造 而fiber的构造是由vnode 转换过去的,JSX 被 Babel 编译为 React.createElement 办法的调用,createElement 办法在调用后返回的就是 ReactElement,就是 virtualDOM。
createElement
文件地位:packages/react/src/ReactElement.js
/** * 创立 React Element * type 元素类型 * config 配置属性 * children 子元素 * 1. 拆散 props 属性和非凡属性 * 2. 将子元素挂载到 props.children 中 * 3. 为 props 属性赋默认值 (defaultProps) * 4. 创立并返回 ReactElement */export function createElement(type, config, children) { /** * propName -> 属性名称 * 用于前面的 for 循环 */ let propName; /** * 存储 React Element 中的一般元素属性 即不蕴含 key ref self source */ const props = {}; /** * 待提取属性 * React 外部为了实现某些性能而存在的属性 */ let key = null; let ref = null; let self = null; let source = null; // 如果 config 不为 null if (config != null) { // 如果 config 对象中有非法的 ref 属性 if (hasValidRef(config)) { // 将 config.ref 属性提取到 ref 变量中 ref = config.ref; // 在开发环境中 if (__DEV__) { // 如果 ref 属性的值被设置成了字符串模式就报一个提醒 // 阐明此用法在未来的版本中会被删除 warnIfStringRefCannotBeAutoConverted(config); } } // 如果在 config 对象中领有非法的 key 属性 if (hasValidKey(config)) { // 将 config.key 属性中的值提取到 key 变量中 key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // 遍历 config 对象 for (propName in config) { // 如果以后遍历到的属性是对象本身属性 // 并且在 RESERVED_PROPS 对象中不存在该属性 if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { // 将满足条件的属性增加到 props 对象中 (一般属性) props[propName] = config[propName]; } } } /** * 将第三个及之后的参数挂载到 props.children 属性中 * 如果子元素是多个 props.children 是数组 * 如果子元素是一个 props.children 是对象 */ // 因为从第三个参数开始及当前都示意子元素 // 所以减去前两个参数的后果就是子元素的数量 const childrenLength = arguments.length - 2; // 如果子元素的数量是 1 if (childrenLength === 1) { // 间接将子元素挂载到到 props.children 属性上 // 此时 children 是对象类型 props.children = children; // 如果子元素的数量大于 1 } else if (childrenLength > 1) { // 创立数组, 数组中元素的数量等于子元素的数量 const childArray = Array(childrenLength); // 开启循环 循环次匹配子元素的数量 for (let i = 0; i < childrenLength; i++) { // 将子元素增加到 childArray 数组中 // i + 2 的起因是实参汇合的前两个参数不是子元素 childArray[i] = arguments[i + 2]; } // 如果是开发环境 if (__DEV__) { // 如果 Object 对象中存在 freeze 办法 if (Object.freeze) { // 调用 freeze 办法 解冻 childArray 数组 // 避免 React 外围对象被批改 解冻对象进步性能 Object.freeze(childArray); } } // 将子元素数组挂载到 props.children 属性中 props.children = childArray; } /** * 如果以后解决是组件 * 看组件身上是否有 defaultProps 属性 * 这个属性中存储的是 props 对象中属性的默认值 * 遍历 defaultProps 对象 查看对应的 props 属性的值是否为 undefined * 如果为undefined 就将默认值赋值给对应的 props 属性值 */ // 将 type 属性值视为函数 查看其中是否具备 defaultProps 属性 if (type && type.defaultProps) { // 将 type 函数下的 defaultProps 属性赋值给 defaultProps 变量 const defaultProps = type.defaultProps; // 遍历 defaultProps 对象中的属性 将属性名称赋值给 propName 变量 for (propName in defaultProps) { // 如果 props 对象中的该属性的值为 undefined if (props[propName] === undefined) { // 将 defaultProps 对象中的对应属性的值赋值给 props 对象中的对应属性的值 props[propName] = defaultProps[propName]; } } } /** * 在开发环境中 如果元素的 key 属性 或者 ref 属性存在 * 监测开发者是否在组件外部通过 props 对象获取了 key 属性或者 ref 属性 * 如果获取了 就报错 */ // 如果处于开发环境 if (__DEV__) { // 元素具备 key 属性或者 ref 属性 if (key || ref) { // 看一下 type 属性中存储的是否是函数 如果是函数就示意以后元素是组件 // 如果元素不是组件 就间接返回元素类型字符串 // displayName 用于在报错过程中显示是哪一个组件报错了 // 如果开发者显式定义了 displayName 属性 就显示开发者定义的 // 否者就显示组件名称 如果组件也没有名称 就显示 'Unknown' const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; // 如果 key 属性存在 if (key) { // 为 props 对象增加key 属性 // 并指定当通过 props 对象获取 key 属性时报错 defineKeyPropWarningGetter(props, displayName); } // 如果 ref 属性存在 if (ref) { // 为 props 对象增加 ref 属性 // 并指定当通过 props 对象获取 ref 属性时报错 defineRefPropWarningGetter(props, displayName); } } } // 返回 ReactElement return ReactElement( type, key, ref, self, source, // 在 Virtual DOM 中用于辨认自定义组件 ReactCurrentOwner.current, props, );}
ReactElement
/** * 接管参数 返回 ReactElement */const ReactElement = function (type, key, ref, self, source, owner, props) { const element = { /** * 组件的类型, 十六进制数值或者 Symbol 值 * React 在最终在渲染 DOM 的时候, 须要确保元素的类型是 REACT_ELEMENT_TYPE * 须要此属性作为判断的根据 */ $$typeof: REACT_ELEMENT_TYPE, /** * 元素具体的类型值 如果是元素节点 type 属性中存储的就是 div span 等等 * 如果元素是组件 type 属性中存储的就是组件的构造函数 */ type: type, /** * 元素的惟一标识 * 用作外部 vdom 比对 晋升 DOM 操作性能 */ key: key, /** * 存储元素 DOM 对象或者组件 实例对象 */ ref: ref, /** * 存储向组件外部传递的数据 */ props: props, /** * 记录以后元素所属组件 (记录以后元素是哪个组件创立的) */ _owner: owner, }; // 返回 ReactElement return element;};
该办法波及到的 一些辅助函数 也都在这个文件中 大家能够下载下来源代码 而后看一下
虚构dom 解析进去大略是这样
能够参考我之前写的解析进去的构造 是相似的
而后就是咱们转换进去的fiber节点的构造
type Fiber = { /************************ DOM 实例相干 *****************************/ // 标记不同的组件类型, 值详见 WorkTag tag: WorkTag, // 组件类型 div、span、组件构造函数 type: any, // 实例对象, 如类组件的实例、原生 dom 实例, 而 function 组件没有实例, 因而该属性是空 stateNode: any, /************************ 构建 Fiber 树相干 ***************************/ // 指向本人的父级 Fiber 对象 return: Fiber | null, // 指向本人的第一个子级 Fiber 对象 child: Fiber | null, // 指向本人的下一个兄弟 iber 对象 sibling: Fiber | null, // 在 Fiber 树更新的过程中,每个 Fiber 都会有一个跟其对应的 Fiber // 咱们称他为 current <==> workInProgress // 在渲染实现之后他们会替换地位 // alternate 指向以后 Fiber 在 workInProgress 树中的对应 Fiber alternate: Fiber | null, /************************ 状态数据相干 ********************************/ // 行将更新的 props pendingProps: any, // 旧的 props memoizedProps: any, // 旧的 state memoizedState: any, /************************ 副作用相干 ******************************/ // 该 Fiber 对应的组件产生的状态更新会寄存在这个队列外面 组件的状态 updateQueue: UpdateQueue<any> | null, // 用来记录以后 Fiber 要执行的 DOM 操作 effectTag: SideEffectTag, // 存储要执行的 DOM 操作 firstEffect: Fiber | null, // 单链表用来疾速查找下一个 side effect nextEffect: Fiber | null, // 存储 DOM 操作完后的副租用 比方调用生命周期函数或者钩子函数的调用 lastEffect: Fiber | null, // 工作的过期工夫 用于优先级 计算 expirationTime: ExpirationTime, // 以后组件及子组件处于何种渲染模式 详见 TypeOfMode mode: TypeOfMode,};
接来下 就该正式进入了,首先咱们react的app 个别都是以 ReactDOM.render挂载办法作为入口
咱们先来讲下首次渲染的状况
接下来咱们找到render函数
render
文件地位:packages/react-dom/src/client/ReactDOMLegacy.js
/** * 渲染入口 * element 要进行渲染的 ReactElement, createElement 办法的返回值 * container 渲染容器 <div id="root"></div> * callback 渲染实现后执行的回调函数 */export function render( element: React$Element<any>, container: Container, callback: ?Function,) { // 检测 container 是否是符合要求的渲染容器 // 即检测 container 是否是实在的DOM对象 // 如果不符合要求就报错 invariant( isValidContainer(container), 'Target container is not a DOM element.', ); return legacyRenderSubtreeIntoContainer( // 父组件 初始渲染没有父组件 传递 null 占位 null, element, container, // 是否为服务器端渲染 false 不是服务器端渲染 true 是服务器端渲染 false, callback, );}
因为咱们是串通主流程 所以间接看legacyRenderSubtreeIntoContainer办法
legacyRenderSubtreeIntoContainer
这个办法用来生成FiberRoot,这个外面会创立Root 而后它上面_internalRoot会贮存fiberRoot。
文件地位: packages/react-dom/src/client/ReactDOMLegacy.js
/** * 将子树渲染到容器中 (初始化 Fiber 数据结构: 创立 fiberRoot 及 rootFiber) * parentComponent: 父组件, 初始渲染传入了 null * children: render 办法中的第一个参数, 要渲染的 ReactElement * container: 渲染容器 * forceHydrate: true 为服务端渲染, false 为客户端渲染 * callback: 组件渲染实现后须要执行的回调函数 **/function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>, children: ReactNodeList, container: Container, forceHydrate: boolean, callback: ?Function,) { /** * 检测 container 是否曾经是初始化过的渲染容器 * react 在初始渲染时会为最外层容器增加 _reactRootContainer 属性 * react 会依据此属性进行不同的渲染形式 * root 不存在 示意初始渲染 * root 存在 示意更新 */ // 获取 container 容器对象下是否有 _reactRootContainer 属性 let root: RootType = (container._reactRootContainer: any); // 行将存储根 Fiber 对象 let fiberRoot; if (!root) { // 初始渲染 // 初始化根 Fiber 数据结构 // 为 container 容器增加 _reactRootContainer 属性 // 在 _reactRootContainer 对象中有一个属性叫做 _internalRoot // _internalRoot 属性值即为 FiberRoot 示意根节点 Fiber 数据结构 // legacyCreateRootFromDOMContainer // createLegacyRoot // new ReactDOMBlockingRoot -> this._internalRoot // createRootImpl root = container._reactRootContainer = legacyCreateRootFromDOMContainer( container, forceHydrate, ); // 获取 Fiber Root 对象 fiberRoot = root._internalRoot; /** * 扭转 callback 函数中的 this 指向 * 使其指向 render 办法第一个参数的实在 DOM 对象 */ // 如果 callback 参数是函数类型 if (typeof callback === 'function') { // 应用 originalCallback 存储 callback 函数 const originalCallback = callback; // 为 callback 参数从新赋值 callback = function () { // 获取 render 办法第一个参数的实在 DOM 对象 // 实际上就是 id="root" 的 div 的子元素 // rootFiber.child.stateNode // rootFiber 就是 id="root" 的 div const instance = getPublicRootInstance(fiberRoot); // 调用 callback 函数并扭转函数外部 this 指向 originalCallback.call(instance); }; } // 初始化渲染不执行批量更新 // 因为批量更新是异步的是能够被打断的, 然而初始化渲染应该尽快实现不能被打断 // 所以不执行批量更新 unbatchedUpdates(() => { updateContainer(children, fiberRoot, parentComponent, callback); }); } else { // 非初始化渲染 即更新 fiberRoot = root._internalRoot; if (typeof callback === 'function') { const originalCallback = callback; callback = function () { const instance = getPublicRootInstance(fiberRoot); originalCallback.call(instance); }; } // Update updateContainer(children, fiberRoot, parentComponent, callback); } // 返回 render 办法第一个参数的实在 DOM 对象作为 render 办法的返回值 // 就是说渲染谁 返回谁的实在 DOM 对象 return getPublicRootInstance(fiberRoot);}
legacyCreateRootFromDOMContainer 该会办法会清空 容器 的元素(占位元素) 并创立fiberRoot
看主流程 这个办法里最终是调用的unbatchedUpdates 这个办法 初始执行的时候 会间接调用回调函数 也就是会间接调用 updateContainer 办法。
updateContainer
这个办法将工作(Update)寄存于工作队列(updateQueue)中 串并进行串联
并且 调度和更新 current 对象 (render阶段构建workInProgress树 比照生成dom构造或筛选有更新的fiber节点 commit阶段开始更新)
文件地位: packages/react-reconciler/src/ReactFiberReconciler.js
/** * 计算工作的过期工夫 * 再依据工作过期工夫创立 Update 工作 */export function updateContainer( // element 要渲染的 ReactElement element: ReactNodeList, // container Fiber Root 对象 container: OpaqueRoot, // parentComponent 父组件 初始渲染为 null parentComponent: ?React$Component<any, any>, // ReactElement 渲染实现执行的回调函数 callback: ?Function,): ExpirationTime { // container 获取 rootFiber const current = container.current; // 获取以后间隔 react 利用初始化的工夫 1073741805 const currentTime = requestCurrentTimeForUpdate(); // 异步加载设置 const suspenseConfig = requestCurrentSuspenseConfig(); // 计算过期工夫 // 为避免工作因为优先级的起因始终被打断而未能执行 // react 会设置一个过期工夫, 当工夫到了过期工夫的时候 // 如果工作还未执行的话, react 将会强制执行该工作 // 初始化渲染时, 工作同步执行不波及被打断的问题 1073741823 const expirationTime = computeExpirationForFiber( currentTime, current, suspenseConfig, ); // 设置FiberRoot.context, 首次执行返回一个emptyContext, 是一个 {} const context = getContextForSubtree(parentComponent); // 初始渲染时 Fiber Root 对象中的 context 属性值为 null // 所以会进入到 if 中 if (container.context === null) { // 初始渲染时将 context 属性值设置为 {} container.context = context; } else { container.pendingContext = context; } // 创立一个待执行工作 const update = createUpdate(expirationTime, suspenseConfig); // 将要更新的内容挂载到更新对象中的 payload 中 // 将要更新的组件存储在 payload 对象中, 不便前期获取 update.payload = {element}; // 判断 callback 是否存在 callback = callback === undefined ? null : callback; // 如果 callback 存在 if (callback !== null) { // 将 callback 挂载到 update 对象中 // 其实就是一层层传递 不便 ReactElement 元素渲染实现调用 // 回调函数执行实现后会被革除 能够在代码的前面加上 return 进行验证 update.callback = callback; } // 将 update 对象退出到以后 Fiber 的更新队列当中 (updateQueue) enqueueUpdate(current, update); // 调度和更新 current 对象 scheduleWork(current, expirationTime); // 返回过期工夫 return expirationTime;}
enqueueUpdate
文件地位: packages/react-reconciler/src/ReactUpdateQueue.js
// 将工作(Update)寄存于工作队列(updateQueue)中// 创立单向链表构造寄存 update, next 用来串联 updateexport function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) { // 获取以后 Fiber 的 更新队列 const updateQueue = fiber.updateQueue; // 如果更新队列不存在 就返回 null if (updateQueue === null) { // 仅产生在 fiber 曾经被卸载 return; } // 获取待执行的 Update 工作 // 初始渲染时没有待执行的工作 const sharedQueue = updateQueue.shared; const pending = sharedQueue.pending; // 如果没有待执行的 Update 工作 if (pending === null) { // 这是第一次更新, 创立一个循环列表. update.next = update; } else { update.next = pending.next; pending.next = update; } // 将 Update 工作存储在 pending 属性中 sharedQueue.pending = update;}
scheduleWork as scheduleUpdateOnFiber
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
/** * 判断工作是否为同步 调用同步工作入口 */export function scheduleUpdateOnFiber( // rootFiber fiber: Fiber, expirationTime: ExpirationTime,) { /** * fiber: 初始化渲染时为 rootFiber, 即 <div id="root"></div> 对应的 Fiber 对象 * expirationTime: 工作过期工夫 =>1073741823 */ /** * 判断是否是有限循环的 update 如果是就报错 * 在 componentWillUpdate 或者 componentDidUpdate 生命周期函数中反复调用 * setState 办法时, 可能会产生这种状况, React 限度了嵌套更新的数量以避免有限循环 * 限度的嵌套更新数量为 50, 可通过 NESTED_UPDATE_LIMIT 全局变量获取 */ checkForNestedUpdates(); // 判断工作是否是同步工作 Sync的值为: 1073741823 if (expirationTime === Sync) { if ( // 查看是否处于非批量更新模式 (executionContext & LegacyUnbatchedContext) !== NoContext && // 查看是否没有处于正在进行渲染的工作 (executionContext & (RenderContext | CommitContext)) === NoContext ) { // 同步工作入口点 performSyncWorkOnRoot(root); } // 疏忽了一些初始化渲染不会失去执行的代码}
首次加载因为要让用户尽快看到界面 所以是一个同步工作
会走performSyncWorkOnRoot 这个同步工作入口
performSyncWorkOnRoot
正式进入render阶段 开始构建workInProgress 树
// 进入 render 阶段, 构建 workInProgress Fiber 树function performSyncWorkOnRoot(root) { // 参数 root 为 fiberRoot 对象 // 查看是否有过期的工作 // 如果没有过期的工作 值为 0 // 初始化渲染没有过期的工作待执行 const lastExpiredTime = root.lastExpiredTime; // NoWork 值为 0 // 如果有过期的工作 将过期工夫设置为 lastExpiredTime 否则将过期工夫设置为 Sync // 初始渲染过期工夫被设置成了 Sync const expirationTime = lastExpiredTime !== NoWork ? lastExpiredTime : Sync; // 如果 root 和 workInProgressRoot 不相等 // 阐明 workInProgressRoot 不存在, 阐明还没有构建 workInProgress Fiber 树 // workInProgressRoot 为全局变量 默认值为 null, 初始渲染时值为 null // expirationTime => 1073741823 // renderExpirationTime => 0 // true if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) { // 构建 workInProgressFiber 树及rootFiber prepareFreshStack(root, expirationTime); } // workInProgress 如果不为 null if (workInProgress !== null) { do { try { // 以同步的形式开始构建 Fiber 对象 workLoopSync(); // 跳出 while 循环 break; } catch (thrownValue) { handleError(root, thrownValue); } } while (true); if (workInProgress !== null) { // 这是一个同步渲染, 所以咱们应该实现整棵树. // 无奈提交不残缺的 root, 此谬误可能是因为React中的谬误所致. 请提出问题. invariant( false, 'Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.', ); } else { // 将构建好的新 Fiber 对象存储在 finishedWork 属性中 // 提交阶段应用 root.finishedWork = (root.current.alternate: any); root.finishedExpirationTime = expirationTime; // 完结 render 阶段 // 进入 commit 阶段 finishSyncRender(root); } }}
prepareFreshStack
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
构建 workInProgress Fiber 树中的 rootFiber
/** * 依据 currentFiber 树中的 rootFiber * 构建 workInProgressFiber 树中的 rootFiber */function prepareFreshStack(root, expirationTime) { // 为 FiberRoot 对象增加 finishedWork 属性 // finishedWork 示意 render 阶段执行实现后构建的待提交的 Fiber 对象 root.finishedWork = null; // 初始化 finishedExpirationTime 值为 0 root.finishedExpirationTime = NoWork; // 建构 workInProgress Fiber 树的 Fiber 对象 workInProgressRoot = root; // 构建 workInProgress Fiber 树中的 rootFiber workInProgress = createWorkInProgress(root.current, null); renderExpirationTime = expirationTime; workInProgressRootExitStatus = RootIncomplete;}
createWorkInProgress
文件地位: packages/react-reconciler/src/ReactFiber.js
// 构建 workInProgress Fiber 树中的 rootFiber// 构建实现后会替换 current fiber// 初始渲染 pendingProps 为 nullexport function createWorkInProgress(current: Fiber, pendingProps: any): Fiber { // current: current Fiber 中的 rootFiber // 获取 current Fiber 中对应的 workInProgress Fiber let workInProgress = current.alternate; // 如果 workInProgress 不存在 if (workInProgress === null) { // 创立 fiber 对象 workInProgress = createFiber( current.tag, pendingProps, current.key, current.mode, ); // 属性复用 workInProgress.elementType = current.elementType; workInProgress.type = current.type; workInProgress.stateNode = current.stateNode; // 应用 alternate 存储 current workInProgress.alternate = current; // 应用 alternate 存储 workInProgress current.alternate = workInProgress; } workInProgress.childExpirationTime = current.childExpirationTime; workInProgress.expirationTime = current.expirationTime; workInProgress.child = current.child; workInProgress.memoizedProps = current.memoizedProps; workInProgress.memoizedState = current.memoizedState; workInProgress.updateQueue = current.updateQueue; workInProgress.sibling = current.sibling; workInProgress.index = current.index; workInProgress.ref = current.ref; // 返回创立好的 workInProgress Fiber 对象 return workInProgress;}
workLoopSync
依据rootFiber 开始构建 逐级构建 整颗 fiber树 循环构建
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
// 它的值不为 null 意味着该 fiber 对象上依然有更新要执行 while (workInProgress !== null) { workInProgress = performUnitOfWork(workInProgress); }}
performUnitOfWork
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
这里是 承接 递的过程从父到子 构建(child和兄弟) 以及 归的过程 从子到父
function performUnitOfWork(unitOfWork: Fiber): Fiber | null { // unitOfWork => workInProgress Fiber 树中的 rootFiber // current => currentFiber 树中的 rootFiber const current = unitOfWork.alternate; // 存储下一个要构建的子级 Fiber 对象 let next; // false if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) { // 初始渲染 不执行 } else { // beginWork: 从父到子, 构建 Fiber 节点对象 // 返回值 next 为以后节点的子节点 next = beginWork(current, unitOfWork, renderExpirationTime); } // 为旧的 props 属性赋值 // 此次更新后 pendingProps 变为 memoizedProps unitOfWork.memoizedProps = unitOfWork.pendingProps; // 如果子节点不存在阐明以后节点向下遍历子节点曾经到底了 // 持续向上返回 遇到兄弟节点 构建兄弟节点的子 Fiber 对象 直到返回到根 Fiber 对象 if (next === null) { // 从子到父, 构建其余节点 Fiber 对象 next = completeUnitOfWork(unitOfWork); } return next;}
beginWork
递 从父到子
文件地位: packages/react-reconciler/src/ReactFiberBeginWork.js
// 从父到子, 构建 Fiber 节点对象function beginWork( current: Fiber | null, workInProgress: Fiber, renderExpirationTime: ExpirationTime,): Fiber | null { // NoWork 常量 值为0 清空过期工夫 workInProgress.expirationTime = NoWork; // 依据以后 Fiber 的类型决定如何构建起子级 Fiber 对象 // 文件地位: shared/ReactWorkTags.js switch (workInProgress.tag) { // 2 // 函数型组件在第一次渲染组件时应用 case IndeterminateComponent: { return mountIndeterminateComponent( // 旧 Fiber current, // 新 Fiber workInProgress, // 新 Fiber 的 type 值 初始渲染时是App组件函数 workInProgress.type, // 同步 整数最大值 1073741823 renderExpirationTime, ); } // 0 case FunctionComponent: { const Component = workInProgress.type; const unresolvedProps = workInProgress.pendingProps; const resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps); return updateFunctionComponent( current, workInProgress, Component, resolvedProps, renderExpirationTime, ); } // 1 case ClassComponent: { const Component = workInProgress.type; const unresolvedProps = workInProgress.pendingProps; const resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps); return updateClassComponent( current, workInProgress, Component, resolvedProps, renderExpirationTime, ); } // 3 case HostRoot: return updateHostRoot(current, workInProgress, renderExpirationTime); // 5 case HostComponent: return updateHostComponent(current, workInProgress, renderExpirationTime); // 6 case HostText: return updateHostText(current, workInProgress); // 组件类型未知 报错 invariant( false, 'Unknown unit of work tag (%s). This error is likely caused by a bug in ' + 'React. Please file an issue.', workInProgress.tag, );}
updateHostRoot
首次加载会匹配HostRoot 而后会执行updateHostRoot函数
文件地位: packages/react-reconciler/src/ReactFiberBeginWork.js
// HostRoot => <div id="root"></div> 对应的 Fiber 对象// 找出 HostRoot 的子 ReactElement 并为其构建 Fiber 对象function updateHostRoot(current, workInProgress, renderExpirationTime) { // 获取更新队列 const updateQueue = workInProgress.updateQueue; // 获取新的 props 对象 null const nextProps = workInProgress.pendingProps; // 获取上一次渲染应用的 state null const prevState = workInProgress.memoizedState; // 获取上一次渲染应用的 children null const prevChildren = prevState !== null ? prevState.element : null; // 浅复制更新队列, 避免援用属性相互影响 // workInProgress.updateQueue 浅拷贝 current.updateQueue cloneUpdateQueue(current, workInProgress); // 获取 updateQueue.payload 并赋值到 workInProgress.memoizedState // 要更新的内容就是 element 就是 rootFiber 的子元素 processUpdateQueue(workInProgress, nextProps, null, renderExpirationTime); // 获取 element 所在对象 const nextState = workInProgress.memoizedState; // 从对象中获取 element const nextChildren = nextState.element; // 获取 fiberRoot 对象 const root: FiberRoot = workInProgress.stateNode; // 服务器端渲染走 if if (root.hydrate && enterHydrationState(workInProgress)) { // 疏忽 } else { // 客户端渲染走 else // 构建子节点 fiber 对象 reconcileChildren( current, workInProgress, nextChildren, renderExpirationTime, ); } // 返回子节点 fiber 对象 return workInProgress.child;}
能够看到该办法会调用reconcileChildren 构建子级和兄弟节点(和咱们mini-fiber相似)
最初返回子级
这个实现逻辑 https://segmentfault.com/a/11...
就是我这篇 mini-fiber的的构建过程
找到没有子级之后,返回到父级 而后找父级的兄弟节点 再持续反复这个构建过程
reconcileChildren
文件地位: packages/react-reconciler/src/ReactFiberBeginWork.js
export function reconcileChildren( // 旧 Fiber current: Fiber | null, // 父级 Fiber workInProgress: Fiber, // 子级 vdom 对象 nextChildren: any, // 初始渲染 整型最大值 代表同步工作 renderExpirationTime: ExpirationTime,) { /** * 为什么要传递 current ? * 如果不是初始渲染的状况, 要进行新旧 Fiber 比照 * 初始渲染时则用不到 current */ // 如果就 Fiber 为 null 示意初始渲染 if (current === null) { // 为以后构建的 Fiber 对象增加子级 Fiber 对象 workInProgress.child = mountChildFibers( workInProgress, null, nextChildren, renderExpirationTime, ); } // 疏忽了 else 的状况}
mountChildFibers
文件地位: packages/react-reconciler/src/ReactChildFiber.js
/** * shouldTrackSideEffects 标识, 是否为 Fiber 对象增加 effectTag * true 增加 false 不增加 * 对于初始渲染来说, 只有根组件须要增加, 其余元素不须要增加, 避免过多的 DOM 操作 */// 用于初始渲染export const mountChildFibers = ChildReconciler(false);function ChildReconciler(shouldTrackSideEffects) { function placeChild( newFiber: Fiber, lastPlacedIndex: number, newIndex: number, ): number { newFiber.index = newIndex; if (!shouldTrackSideEffects) { return lastPlacedIndex; } // 疏忽了一部分初始化渲染不执行的代码 } function placeSingleChild(newFiber: Fiber): Fiber { // 如果是初始渲染 会在根组件(App)上设置 effectTag 属性为 Placement 值为 1 // 其余子级节点具备默认值为 0 避免在 commit 阶段重复操作实在DOM // 初始渲染时如果以后解决的是根组件 true 其余组件 false if (shouldTrackSideEffects && newFiber.alternate === null) { // Placement 示意新创建的节点 newFiber.effectTag = Placement; } return newFiber; } // 解决子元素是数组的状况 function reconcileChildrenArray( // 父级 Fiber returnFiber: Fiber, currentFirstChild: Fiber | null, // 子级 vdom 数组 newChildren: Array<*>, expirationTime: ExpirationTime, ): Fiber | null { /** * 存储第一个子节点 Fiber 对象 * 办法返回的也是第一个子节点 Fiber 对象 * 因为其余子节点 Fiber 对象都存储在上一个子 Fiber 节点对象的 sibling 属性中 */ let resultingFirstChild: Fiber | null = null; // 上一次创立的 Fiber 对象 let previousNewFiber: Fiber | null = null; // 初始渲染没有旧的子级 所以为 null let oldFiber = currentFirstChild; let lastPlacedIndex = 0; let newIdx = 0; let nextOldFiber = null; // oldFiber 为空 阐明是初始渲染 if (oldFiber === null) { // 遍历子 vdom 对象 for (; newIdx < newChildren.length; newIdx++) { // 创立子 vdom 对应的 fiber 对象 const newFiber = createChild( returnFiber, newChildren[newIdx], expirationTime, ); // 如果 newFiber 为 null if (newFiber === null) { // 进入下次循环 continue; } // 初始渲染时只为 newFiber 增加了 index 属性, // 其余事没干. lastPlacedIndex 被一成不变的返回了 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); // 为以后节点设置下一个兄弟节点 if (previousNewFiber === null) { // 存储第一个子 Fiber 产生在第一次循环时 resultingFirstChild = newFiber; } else { // 为节点设置下一个兄弟 Fiber previousNewFiber.sibling = newFiber; } // 在循环的过程中更新上一个创立的Fiber 对象 previousNewFiber = newFiber; } // 返回创立好的子 Fiber // 其余 Fiber 都作为 sibling 存在 return resultingFirstChild; } // 返回第一个子元素 Fiber 对象 return resultingFirstChild; } // 解决子元素是文本或者数值的状况 function reconcileSingleTextNode( returnFiber: Fiber, currentFirstChild: Fiber | null, textContent: string, expirationTime: ExpirationTime, ): Fiber { // 初始渲染不执行 if (currentFirstChild !== null && currentFirstChild.tag === HostText) { // We already have an existing node so let's just update it and delete // the rest. deleteRemainingChildren(returnFiber, currentFirstChild.sibling); const existing = useFiber(currentFirstChild, textContent); existing.return = returnFiber; return existing; } // 现有的第一个子节点不是文本节点,因而咱们须要创立一个并删除现有的. // 初始渲染不执行 deleteRemainingChildren(returnFiber, currentFirstChild); // 依据文本创立 Fiber 对象 const created = createFiberFromText( textContent, returnFiber.mode, expirationTime, ); // 设置父 Fiber 对象 created.return = returnFiber; // 返回创立好的 Fiber 对象 return created; } // 解决子元素是单个对象的状况 function reconcileSingleElement( // 父 Fiber 对象 returnFiber: Fiber, // 备份子 fiber currentFirstChild: Fiber | null, // 子 vdom 对象 element: ReactElement, expirationTime: ExpirationTime, ): Fiber { // 查看子 vdom 对象是否示意 fragment if (element.type === REACT_FRAGMENT_TYPE) { // false } else { // 依据 React Element 创立 Fiber 对象 // 返回创立好的 Fiber 对象 const created = createFiberFromElement( element, // 用来示意以后组件下的所有子组件要用处于何种渲染模式 // 文件地位: ./ReactTypeOfMode.js // 0 同步渲染模式 // 100 异步渲染模式 returnFiber.mode, expirationTime, ); // 增加 ref 属性 { current: DOM } created.ref = coerceRef(returnFiber, currentFirstChild, element); // 增加父级 Fiber 对象 created.return = returnFiber; // 返回创立好的子 Fiber return created; } } function reconcileChildFibers( // 父 Fiber 对象 returnFiber: Fiber, // 旧的第一个子 Fiber 初始渲染 null currentFirstChild: Fiber | null, // 新的子 vdom 对象 newChild: any, // 初始渲染 整型最大值 代表同步工作 expirationTime: ExpirationTime, ): Fiber | null { // 这是入口办法, 依据 newChild 类型进行对应解决 // 判断新的子 vdom 是否为占位组件 比方 <></> // false const isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null; // 如果 newChild 为占位符, 应用 占位符组件的子元素作为 newChild if (isUnkeyedTopLevelFragment) { newChild = newChild.props.children; } // 检测 newChild 是否为对象类型 const isObject = typeof newChild === 'object' && newChild !== null; // newChild 是单个对象的状况 if (isObject) { // 匹配子元素的类型 switch (newChild.$$typeof) { // 子元素为 ReactElement case REACT_ELEMENT_TYPE: // 为 Fiber 对象设置 effectTag 属性 // 返回创立好的子 Fiber return placeSingleChild( // 解决单个 React Element 的状况 // 外部会调用其余办法创立对应的 Fiber 对象 reconcileSingleElement( returnFiber, currentFirstChild, newChild, expirationTime, ), ); } } // 解决 children 为文本和数值的状况 return "App works" if (typeof newChild === 'string' || typeof newChild === 'number') { return placeSingleChild( reconcileSingleTextNode( returnFiber, currentFirstChild, // 如果 newChild 是数值, 转换为字符串 '' + newChild, expirationTime, ), ); } // children 是数组的状况 if (isArray(newChild)) { // 返回创立好的子 Fiber return reconcileChildrenArray( returnFiber, currentFirstChild, newChild, expirationTime, ); } }}
completeUnitOfWork
下一步是从子到父
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
/** * * 从下至上挪动到该节点的兄弟节点, 如果始终往上没有兄弟节点就返回父节点, 最终会达到 root 节点 * 1. 创立其余节点的 Fiber 对象 * 2. 创立每一个节点的实在 DOM 对象并将其增加到 stateNode 属性中 * 3. 构建 effect 链表构造 */function completeUnitOfWork(unitOfWork: Fiber): Fiber | null { // 为 workInProgress 全局变量从新赋值 workInProgress = unitOfWork; do { // 获取备份节点 // 初始化渲染 非根 Fiber 对象没有备份节点 所以 current 为 null const current = workInProgress.alternate; // 父级 Fiber 对象, 非根 Fiber 对象都有父级 const returnFiber = workInProgress.return; // 判断传入的 Fiber 对象是否构建实现, 任务调度相干 // & 是示意位的与运算, 把左右两边的数字转化为二进制 // 而后每一位别离进行比拟, 如果相等就为1, 不相等即为0 // 此处利用"位与"运算符的目标是"清零" // true if ((workInProgress.effectTag & Incomplete) === NoEffect) { let next; // 如果不能应用分析器的 timer, 间接执行 completeWork // enableProfilerTimer => true // 但此处无论条件是否成立都会执行 completeWork if ( !enableProfilerTimer || (workInProgress.mode & ProfileMode) === NoMode ) { // 重点代码(二) // 创立节点实在 DOM 对象并将其增加到 stateNode 属性中 next = completeWork(current, workInProgress, renderExpirationTime); } else { // 创立节点实在 DOM 对象并将其增加到 stateNode 属性中 next = completeWork(current, workInProgress, renderExpirationTime); } // 重点代码(一) // 如果子级存在 if (next !== null) { // 返回子级 始终返回到 workLoopSync // 再从新执行 performUnitOfWork 构建子级 Fiber 节点对象 return next; } // 构建 effect 链表构造 // 如果不是根 Fiber 就是 true 否则就是 false // 将子树和此 Fiber 的所有 effect 附加到父级的 effect 列表中 if ( // 如果父 Fiber 存在 并且 returnFiber !== null && // 父 Fiber 对象中的 effectTag 为 0 (returnFiber.effectTag & Incomplete) === NoEffect ) { // 将子树和此 Fiber 的所有副作用附加到父级的 effect 列表上 // 以下两个判断的作用是收集子 Fiber的 effect 到父 Fiber if (returnFiber.firstEffect === null) { // first returnFiber.firstEffect = workInProgress.firstEffect; } if (workInProgress.lastEffect !== null) { if (returnFiber.lastEffect !== null) { // next returnFiber.lastEffect.nextEffect = workInProgress.firstEffect; } // last returnFiber.lastEffect = workInProgress.lastEffect; } // 获取副作用标记 // 初始渲染时除[根组件]以外的 Fiber, effectTag 值都为 0, 即不须要执行任何实在DOM操作 // 根组件的 effectTag 值为 3, 即须要将此节点对应的实在DOM对象增加到页面中 const effectTag = workInProgress.effectTag; // 创立 effect 列表时跳过 NoWork(0) 和 PerformedWork(1) 标记 // PerformedWork 由 React DevTools 读取, 不提交 // 初始渲染时 只有遍历到了根组件 判断条件能力成立, 将 effect 链表增加到 rootFiber // 初始渲染 FiberRoot 对象中的 firstEffect 和 lastEffect 都是 App 组件 // 因为当所有节点在内存中构建实现后, 只须要一次将所有 DOM 增加到页面中 if (effectTag > PerformedWork) { // false if (returnFiber.lastEffect !== null) { returnFiber.lastEffect.nextEffect = workInProgress; } else { // 为 fiberRoot 增加 firstEffect returnFiber.firstEffect = workInProgress; } // 为 fiberRoot 增加 lastEffect returnFiber.lastEffect = workInProgress; } } } else { // 疏忽了初始渲染不执行的代码 } // 获取下一个同级 Fiber 对象 const siblingFiber = workInProgress.sibling; // 如果下一个同级 Fiber 对象存在 if (siblingFiber !== null) { // 返回下一个同级 Fiber 对象 return siblingFiber; } // 否则退回父级 workInProgress = returnFiber; } while (workInProgress !== null); // 当执行到这里的时候, 阐明遍历到了 root 节点, 已实现遍历 // 更新 workInProgressRootExitStatus 的状态为 已实现 if (workInProgressRootExitStatus === RootIncomplete) { workInProgressRootExitStatus = RootCompleted; } return null;}
completeWork
文件地位: packages/react-reconciler/src/ReactFiberCompleteWork.js
首次加载 归的时候 会把子级dom构造追加到父级中
function completeWork( current: Fiber | null, workInProgress: Fiber, renderExpirationTime: ExpirationTime,): Fiber | null { // 获取待更新 props const newProps = workInProgress.pendingProps; switch (workInProgress.tag) { // 0 case FunctionComponent: return null; // 3 case HostRoot: { updateHostContainer(workInProgress); return null; } // 5 case HostComponent: { // 获取 rootDOM 节点 <div id="root"></div> const rootContainerInstance = getRootHostContainer(); // 节点的具体的类型 div span ... const type = workInProgress.type; // false current = null if (current !== null && workInProgress.stateNode != null) { // 初始渲染不执行 } else { // false if (wasHydrated) { // 初始渲染不执行 } else { // 创立节点实例对象 <div></div> <span></span> let instance = createInstance( type, newProps, rootContainerInstance, currentHostContext, workInProgress, ); /** * 将所有的子级追加到父级中 * instance 为父级 * workInProgress.child 为子级 */ appendAllChildren(instance, workInProgress, false, false); // 为 Fiber 对象增加 stateNode 属性 workInProgress.stateNode = instance; } // 解决 ref DOM 援用 if (workInProgress.ref !== null) { markRef(workInProgress); } } return null; } }}
appendAllChildren
文件地位: packages/react-reconciler/src/ReactFiberCompleteWork.js
appendAllChildren = function ( parent: Instance, workInProgress: Fiber, needsVisibilityToggle: boolean, isHidden: boolean, ) { // 获取子级 let node = workInProgress.child; // 如果子级不为空 执行循环 while (node !== null) { // 如果 node 是一般 ReactElement 或者为文本 if (node.tag === HostComponent || node.tag === HostText) { // 将子级追加到父级中 appendInitialChild(parent, node.stateNode); } else if (node.child !== null) { // 如果 node 不是一般 ReactElement 又不是文本 // 将 node 视为组件, 组件自身不能转换为实在 DOM 元素 // 获取到组件的第一个子元素, 继续执行循环 node.child.return = node; node = node.child; continue; } // 如果 node 和 workInProgress 是同一个节点 // 阐明 node 曾经退回到父级 终止循环 // 阐明此时所有子级都曾经追加到父级中了 if (node === workInProgress) { return; } // 解决子级节点的兄弟节点 while (node.sibling === null) { // 如果节点没有父级或者节点的父级是本人, 退出循环 // 阐明此时所有子级都曾经追加到父级中了 if (node.return === null || node.return === workInProgress) { return; } // 更新 node node = node.return; } // 更新父级 不便回退 node.sibling.return = node.return; // 将 node 更新为下一个兄弟节点 node = node.sibling; } };
归的过程完了之后 整体的dom构造 会被全副 收集全 到顶级的fiber中
当初到这里workInProgress 树构建完了 也就是说render阶段 曾经完结了 接下来就是进入commit提交阶段了,上面
finishSyncRender
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
function finishSyncRender(root) { // 销毁 workInProgress Fiber 树 // 因为待提交 Fiber 对象曾经被存储在了 root.finishedWork 中 workInProgressRoot = null; // 进入 commit 阶段 commitRoot(root);}
commitRoot
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
开始正式提交commit
function commitRoot(root) { // 获取工作优先级 97 => 一般优先级 const renderPriorityLevel = getCurrentPriorityLevel(); // 应用最高优先级执行当前任务, 因为 commit 阶段不能够被打断 // ImmediatePriority, 优先级为 99, 最高优先级 runWithPriority( ImmediatePriority, commitRootImpl.bind(null, root, renderPriorityLevel), ); return null;}
commitRootImpl
这里开始执行commitRootImpl办法
commit 阶段能够分为三个子阶段:
- before mutation 阶段(执行 DOM 操作前)
- mutation 阶段(执行 DOM 操作)
- layout 阶段(执行 DOM 操作后)
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
function commitRootImpl(root, renderPriorityLevel) { // 获取待提交 Fiber 对象 rootFiber const finishedWork = root.finishedWork; // 如果没有工作要执行 if (finishedWork === null) { // 阻止程序持续向下执行 return null; } // 重置为默认值 root.finishedWork = null; root.callbackNode = null; root.callbackExpirationTime = NoWork; root.callbackPriority = NoPriority; root.nextKnownPendingLevel = NoWork; // 获取要执行 DOM 操作的副作用列表 let firstEffect = finishedWork.firstEffect; // true if (firstEffect !== null) { // commit 第一个子阶段 nextEffect = firstEffect; // 解决类组件的 getSnapShotBeforeUpdate 生命周期函数 do { invokeGuardedCallback(null, commitBeforeMutationEffects, null); } while (nextEffect !== null); // commit 第二个子阶段 nextEffect = firstEffect; do { invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel); } while (nextEffect !== null); // 将 workInProgress Fiber 树变成 current Fiber 树 root.current = finishedWork; // commit 第三个子阶段 nextEffect = firstEffect; do { invokeGuardedCallback(null, commitLayoutEffects, null, root,expirationTime); } while (nextEffect !== null); // 重置 nextEffect nextEffect = null; }}
第一子阶段
commitBeforeMutationEffects
件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
// commit 阶段的第一个子阶段// 调用类组件的 getSnapshotBeforeUpdate 生命周期函数function commitBeforeMutationEffects() { // 循环 effect 链 while (nextEffect !== null) { // nextEffect 是 effect 链上从 firstEffect 到 lastEffec 的每一个须要commit的 fiber 对象 // 初始化渲染第一个 nextEffect 为 App 组件 // effectTag => 3 const effectTag = nextEffect.effectTag; // console.log(effectTag); // nextEffect = null; // return; // 如果 fiber 对象中里有 Snapshot 这个 effectTag 的话 // Snapshot 和更新有关系 初始化渲染 不执行 // false if ((effectTag & Snapshot) !== NoEffect) { // 获取以后 fiber 节点 const current = nextEffect.alternate; // 当 nextEffect 上有 Snapshot 这个 effectTag 时 // 执行以下办法, 次要是类组件调用 getSnapshotBeforeUpdate 生命周期函数 commitBeforeMutationEffectOnFiber(current, nextEffect); } // 更新循环条件 nextEffect = nextEffect.nextEffect; }}
commitBeforeMutationEffectOnFiber as commitBeforeMutationLifeCycles
文件地位: packages/react-reconciler/src/ReactFiberCommitWork.js
function commitBeforeMutationLifeCycles( current: Fiber | null, finishedWork: Fiber,): void { switch (finishedWork.tag) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: case Block: { return; } // 如果该 fiber 类型是 ClassComponent case ClassComponent: { if (finishedWork.effectTag & Snapshot) { if (current !== null) { // 旧的 props const prevProps = current.memoizedProps; // 旧的 state const prevState = current.memoizedState; // 获取 classComponent 组件的实例对象 const instance = finishedWork.stateNode; // 执行 getSnapshotBeforeUpdate 生命周期函数 // 在组件更新前捕捉一些 DOM 信息 // 返回自定义的值或 null, 统称为 snapshot const snapshot = instance.getSnapshotBeforeUpdate( finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState, ); } } return; } case HostRoot: case HostComponent: case HostText: case HostPortal: case IncompleteClassComponent: // Nothing to do for these component types return; }}
第二子阶段
commitMutationEffects
文件地位: packages/react-reconciler/src/ReactFiberWorkLoop.js
// commit 阶段的第二个子阶段// 依据 effectTag 执行 DOM 操作function commitMutationEffects(root: FiberRoot, renderPriorityLevel) { // 循环 effect 链 while (nextEffect !== null) { // 获取 effectTag // 初始渲染第一次循环为 App 组件 // 行将根组件及外部所有内容一次性增加到页面中 const effectTag = nextEffect.effectTag; // 依据 effectTag 别离解决 let primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating); // 匹配 effectTag // 初始渲染 primaryEffectTag 为 2 匹配到 Placement switch (primaryEffectTag) { // 针对该节点及子节点进行插入操作 case Placement: { commitPlacement(nextEffect); // effectTag 从 3 变为 1 // 从 effect 标签中革除 "placement" 重置 effectTag 值 // 以便咱们晓得在调用诸如componentDidMount之类的任何生命周期之前已将其插入。 nextEffect.effectTag &= ~Placement; break; } // 插入并更新 DOM case PlacementAndUpdate: { // 插入 commitPlacement(nextEffect); nextEffect.effectTag &= ~Placement; // 更新 const current = nextEffect.alternate; commitWork(current, nextEffect); break; } // 服务器端渲染 case Hydrating: { nextEffect.effectTag &= ~Hydrating; break; } // 服务器端渲染 case HydratingAndUpdate: { nextEffect.effectTag &= ~Hydrating; // Update const current = nextEffect.alternate; commitWork(current, nextEffect); break; } // 更新 DOM case Update: { const current = nextEffect.alternate; commitWork(current, nextEffect); break; } // 删除 DOM case Deletion: { commitDeletion(root, nextEffect, renderPriorityLevel); break; } } nextEffect = nextEffect.nextEffect; }}