# react18.2 **Repository Path**: AbelCodeUp/react18.2 ## Basic Information - **Project Name**: react18.2 - **Description**: react18.2源码学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 4 - **Created**: 2024-12-31 - **Last Updated**: 2024-12-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # react usage > ## 源码调试 ```js // 1. 下载代码 git clone https://github.com/facebook/react.git // https://github.com/facebook/react/tree/v18.2.0 // 2. 拷贝文件 - src\react\packages\react - src\react\packages\react-dom - src\react\packages\react-reconciler - src\react\packages\scheduler - src\react\packages\shared // 3. 将flow文件改为js npm install strip-flowtype -g strip-flowtype src/react/packages/**/*.js // 4. 删除 __text__测试目录 // 5. 修改配置 // src\react\packages\react-reconciler\src\ReactFiberHostConfig.js export * from './forks/ReactFiberHostConfig.dom'; // src\react\packages\shared\ReactSharedInternals.js import ReactSharedInternals from '../react/src/ReactSharedInternals'; export default ReactSharedInternals; // src\react\packages\react\src\ReactServerContext.js const ContextRegistry = ReactSharedInternals?.ContextRegistry; ``` ## react 概念 ```js // react是什么 // react是一个用户构建用户界面的js库 组件化开发 // 组件化构建 快速响应的 大型web应用 // react和vue区别 优缺点 // react发展 react15 react16 react18 ``` ## jsx ```js // 什么是jsx 和 template 区别 // jsx会经过babel的转义 https://www.babeljs.cn/repl // 老版的React.createElement() 返回一个 React元素 // react17新版的会变为 jsx()的函数调用 返回的也是 ReactElement 元素 // 1. 新版不再需要 import React from 'react' // 2. 两者对 children 的处理有差异 jsx_dev会将children放在props中 // 3. React.createElement() = jsx_dev() // 1. jsx react元素 虚拟dom fiber 区别 // 1.1 编辑阶段: jsx通过babel编译得到 React.createElement() // 1.2 在浏览器执行代码 React.createElement 函数会执行 返回 React 元素 // 1.3 React 元素就是一个普通的 js 对象 虚拟 dom // 1.4 在react的workLoop的render阶段我们会根据 vdom 创建对应的fiber对象 // 2. babel如果实现的转义 ast语法树 ``` ## createElement ```js // https://github.com/facebook/react/blob/main/packages/react/src/ReactElement.js // 返回一个 ReactElement react元素 ``` ## jsx_dev ```js // https://github.com/facebook/react/blob/main/packages/react/src/jsx/ReactJSXElement.js // react/jsx-dev-runtime // 和createElement是一样的 返回React元素就是我们说的虚拟dom // babel的编译结果是不一样的 children不是一个个的在参数后面了 // children没单独处理 是在 props 中的 ``` ## render ```js // react有两种渲染模式 // 在react18中默认是使用并发模式的 // 1. ReactDOM.render() // 2. ReactDOM.createRoot().render() ``` ## legacy ```js // https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMLegacy.js // render 函数 function render(element, container) { return legacyRenderSubtreeIntoContainer(element, container); } // 区分初次挂载和更新 function legacyRenderSubtreeIntoContainer(children, container) { const maybeRoot = container._reactRootContainer; let root; if (!maybeRoot) { // 初始化挂载 root = legacyCreateRootFromDOMContainer(container, children); } else { root = updateContainer(children, root, parentComponent, callback); } // return getPublicRootInstance(root); return root; } ``` ### Initial mount ```js function legacyCreateRootFromDOMContainer(container, children) { // 1. 创建 rootFiber和fiberRoot const root = createContainer(); // 2. 事件系统 // listenToAllSupportedEvents(); // 3. 初始化挂载 同步 // ReactFiberWorkLoop flushSync设置优先级 执行fn flushSync(updateContainer()); return root; } ``` #### crateContainer ```js // 创建rootFiber和fiberRoot 初始化更新队列 updateQueue(单向循环链表) function createContainer(container) { return createFiberRoot(containerInfo, tag, initialChildren); } // tag用来区分不同的模式 function createFiberRoot(containerInfo, tag) { // 1. 创建 fiberRoot const root = new FiberRootNode(); // 2. 创建 rootFiber 很多属性 const uninitializedFiber = createHostRootFiber(); // 3. 相互指向 root.current = uninitializedFiber; uninitializedFiber.stateNode = root; uninitializedFiber.memoizedState = initialState; // 4. 初始化更新队列 // fiber.updateQueue = queue = {shared: {pending: null}} initializeUpdateQueue(); return root; } ``` #### updateContainer ```js // 创建更新 加入到更新队列 然后从根节点开始调度 function updateContainer(element, container) { const current = element.current; // rootFiber // 1. 计算lane const eventTime = requestEventTime(); const lane = requestUpdateLane(current); // 2. 创建一个update更新 {payload: null, next: null} const update = createUpdate(eventTime, lane); update.payload = { element }; // 3. 更新入对 按照批次加入到 concurrentQueues 数组中 return node.stateNode // 两种不同的模式在updateQueue的处理上是有区别的 const root = enqueueUpdate(current, update, lane); // 4. 从根节点开始调度 scheduleUpdateOnFiber(root, current, lane, eventTime); return lane; } ``` #### flushSync ```js // 初次渲染在执行 flushSync(updateContainer())的时候 // 执行callback就是执行 updateContainer() 回调 function flushSync(fn) { try { if (fn) return fn(); } finally { // 初次渲染会执行这个flush 直接清空 if ((executionContext & (RenderContext | CommitContext)) === NoContext) { flushSyncCallbacks(); } } } // 清空 syncQueue 依次执行 function flushSyncCallbacks() { const queue = syncQueue; for (let i = 0; i < queue.length; i++) { const callback = queue[i]; // 执行callback do { callback = callback(isSync); } while (callback !== null); } } ``` ### schedule ```js // 在执行updateContainer的时候 将更新加入到updateQueue中然后从根节点开始调度 // ReactFiberWorkLoop function scheduleUpdateOnFiber(root, fiber, lane, eventTime) { // 1. 将root标记为更新 root.pendingLanes |= updateLane; 1 markRootUpdated(root, lane, eventTime); // 2. 调度root // 2.1 lane优先级 newCallbackPriority // 2.2 将callback添加到 syncQueue 中 scheduleSyncCallback(performSyncWorkOnRoot添加到queue中) // 2.3 在 microtask 中清空任务 queueMicrotask(flushSyncCallbacks) // 2.3.1 设置优先级 // 2.3.2 依次执行 syncQueue中的回调 callback = callback(isSync); // 2.3.3 执行 performSyncWorkOnRoot ensureRootIsScheduled(root, eventTime); } // 要处理优先级问题 先跳过 function markRootUpdated() {} function ensureRootIsScheduled(root, eventTime) { // 1. 将cb加到如syncQueue中 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root)); // 2. 清空队列 flushSyncCallbacks就是取出 cb执行 // 初次渲染是在 flush的 finally 中直接就清空了 // 就是执行 performSyncWorkOnRoot scheduleMicrotask(flushSyncCallbacks); } // 将cb加入到 syncQueue 中 let syncQueue = null; function scheduleLegacySyncCallback(cb) { if (syncQueue === null) syncQueue = [cb]; else syncQueue.push(cb); } ``` ### performSyncWorkOnRoot ```js // workLoop 分为两个阶段 // 1. render阶段 异步可中断 计算副作用 // 2. commit提交阶段 同步的 提交副作用 function performSyncWorkOnRoot(root) { // 1. render const exitStatus = renderRootSync(root, lanes); // 2. commit commitRoot(root); return null; } ``` ### renderRootSync ```js // render阶段 开始 workLoop() // 1. beginWork 根据虚拟dom节点判断tag递归的创建子fiber // reconcileChildren dom-diff 创建fiber节点 // 2. completeWork 当child为null表示节点完成 根据不同的tag创建真实的dom节点 stateNode // bubbleProperties 归并副作用 以前是收集effectList副作用链条 // 以fiber为工作单元的 异步可中断的操作 function renderRootSync() { // 1. 创建 workProgress fiberRoot 双缓存 prepareFreshStack(); // 2. 开始工作循环 do { workLoopSync(); } while (true); return 0; } ``` #### workLoopSync ```js function performUnitOfWork() { while (workInProgress !== null) { // 开始工作单元 performUnitOfWork(workInProgress); } } // 开始一个工作单元 function performUnitOfWork(unitOfWork) { // fiber开始工作 返回第一个儿子 let next = beginWork(current, unitOfWork, renderLanes); unitOfWork.memoizedProps = unitOfWork.pendingProps; // 当root节点 beginWork之后 返回第一个儿子 儿子开始beginWork(文本节点) // 文本节点return null 就完成工作 if (next === null) { // 如果没有了就表示自己完成了 // completeWork completeUnitOfWork(unitOfWork); } else { // 执行下一个 workInProgress = next; } } ``` #### beginWork ```js // ReactFiberBeginWork function beginWork(current, workInProgress, renderLanes) { // 根据不同的tag执行对应的方法 switch ( workInProgress.tag case HostRoot: return updateHostRoot() case HostText: return updateHostText() ) { } } // 第一个根节点 beginWork的是 hostRoot function updateHostRoot() { // 1. 处理更新队列 将循环链表拆开 得到 newState processUpdateQueue(workInProgress, nextProps, null, renderLanes); // 2. 协调children 递归创建子fiber reconcileChildren() // 3. 返回大儿子 return workInProgress.child; } // 处理文本 直接返回 不创建fiber function updateHostText() { return null; } ``` #### reconcileChildren ```js // 这个方法很重要 // dom-diff function reconcileChildren(current) { if (current === null) { // 初次挂载 workInProgress.child = mountChildFibers(); } else { // 根节点是存在current的 workInProgress.child = reconcileChildFibers(); } } // ReactChildFiber // 1. 文本的情况 function reconcileChildFibers() { if (typeof newChild === "string" || typeof newChild === "number") { // 1.创建fiber // 2.增加flags return placeSingleChild( reconcileSingleTextNode( returnFiber, currentFirstChild, "" + newChild, lanes ) ); } } // 创建 TextNode的fiber 维护fiber链条的关系 function reconcileSingleTextNode() { const created = createFiberFromText(); created.return = returnFiber; return created; } function placeSingleChild() { newFiber.flags |= Placement; } ``` #### completeUnitOfWork ```js // 当 beginWork返回的是null表示这个节点工作完成了 function completeUnitOfWork(unitOfWork) { let completedWork = unitOfWork; do { const current = completedWork.alternate; const returnFiber = completedWork.return; let next = completeWork(current, completedWork); // 如果有next就继续工作 if (next !== null) { workInProgress = next; return; } const siblingFiber = completedWork.sibling; if (siblingFiber !== null) { workInProgress = siblingFiber; return; } // 如果弟弟也完成了 就表示让parent完成 同时找叔叔开始工作 completedWork = returnFiber; workInProgress = completedWork; } while (completedWork !== null); } ``` #### completeWork ```js // ReactFiberCompleteWork // beginWork是根据虚拟dom创建fiber链 // completeWork是根据fiber创建真实的dom节点 向上冒泡副作用(收集effectList副作用) // 和beginWork一样需要判断tag做不同的处理 创建真实的dom节点 function completeWork() { switch (workInProgress.tag) { case HostText: { // 先完成文本节点 document.createTextNode(text) workInProgress.stateNode = createTextInstance(newText); // 向上冒泡副作用 取代了之前的 effectList副作用链条 bubbleProperties(workInProgress); return null; } case HostRoot: { // 完成 root之后就全部完成了 执行 commitRoot的操作应用副作用 bubbleProperties(workInProgress); } } } ``` #### bubbleProperties ```js // subtreeFlags function bubbleProperties(completedWork) { let newChildLanes = NoLanes; let subtreeFlags = NoFlags; let child = completedWork.child; while (child !== null) { newChildLanes = mergeLanes( newChildLanes, mergeLanes(child.lanes, child.childLanes) ); subtreeFlags |= child.subtreeFlags; subtreeFlags |= child.flags; child = child.sibling; } completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; // return didBailout; } ``` ### commitRoot ```js // 提交阶段 ReactFiberWorkLoop function commitRoot() { if (subtreeHasEffects || rootHasEffect) { // 1. before // const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects() // 2. 提交副作用 commitMutationEffects(root, finishedWork, lanes); } } ``` #### commitMutationEffects ```js // 还是判断不同的tag做不同的处理 这里又需要大量的递归 function commitMutationEffects(finishedWork) { switch (finishedWork.tag) { case HostRoot: // 递归提交 recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork); return; case HostText: { recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork); return; } default: break; } } // 递归 function recursivelyTraverseMutationEffects() { let child = parentFiber.child; while (child !== null) { commitMutationEffectsOnFiber(child, root, lanes); child = child.sibling; } } // 提交 根据不同的flags执行不同的操作 function commitReconciliationEffects() { if (flags & Placement) { // 执行insert插入操作 commitPlacement(finishedWork); finishedWork.flags &= ~Placement; } } ``` ## createRoot ```js // https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOM.js // react本身就是一个视图渲染器 将虚拟dom变成真实的dom const root = createRoot(document.getElementById("root")); root.render(); function createRoot(container) { // 1. 创建root容器 createFiberRoot // 模式是不一样的 fiber上的mode属性 // containerInfo:{div#root} // rootFiber.stateNode = fiberRoot // fiberRoot.current = rootFiber const root = createContainer(container, ConcurrentMode); // 2. node[internalContainerInstanceKey] = hostRoot; // markContainerAsRoot(root.current, container); // 3. 事件系统 // listenToAllSupportedEvents(rootContainerElement); // 4. 返回react的根 // this._internalRoot = internalRoot; // ReactDOMRoot.prototype.render = function(children) {} return new ReactDOMRoot(root); } function ReactDOMRoot(internalRoot) { this._internalRoot = internalRoot; } // render执行的也是 updateContainer ReactDOMRoot.prototype.render = function (children) { const root = this._internalRoot; // root updateContainer(children, root, null, null); }; ``` ### createContainer [FiberRootNode](img/1.FiberRootNode.jpeg) ```js // 创建fiberRoot function createContainer() { return createFiberRoot(containerInfo, tag; } function createFiberRoot() { // fiberRoot div#root const root = new FiberRootNode(containerInfo, tag); // rootFiber 创建一个未初始化的 fiber const uninitializedFiber = createHostRootFiber(tag); // 相互指向 root.current = uninitializedFiber; uninitializedFiber.stateNode = root; uninitializedFiber.memoizedState = initialState; // 初始化更新队列 更新队列是一个循环链表 // fiber.updateQueue = queue = {shared: {pending: null}} initializeUpdateQueue(uninitializedFiber); return root; } ``` ### fiber [renderFiber](img/2.renderFiber.jpeg) ```js // 1. fiber是一个执行单元 // 作用单元是以fiber为基本的 // 2. fiber也是一种数据结构 链表 react中大量使用了链表 单向链表 双向链表 循环链表(更新队列) // return child sibling // react为什么要引入fiber // 1. react15的架构是 Reconciler(协调器) + Renderer(渲染器) // 1.1 reconciler 找出变化的组件 // 递归更新子组件 无法中断 // js执行过长会形成卡顿 // 1.2 renderer 将变化的组件渲染到页面上 // 2. react16 // 2.1 Scheduler(调度器)找到高优任务 // react和浏览器是合作调度方式; // 优先级 expirationTime => lane模型 // 不使用 requestIdleCallback() react内部使用 MessageChannel 自己实现 // 1. 浏览器兼容性问题 // 2. 时间不可控 react每帧申请5ms // 2.2 Reconciler(协调器) 找出变化的组件 // Reconciler与Renderer不再是交替工作 Reconciler将变化的节点加上flags标记 // 之前是递归处理 => 变成异步可中断的 // 引入fiber 浏览器及时响应用户的交互 // 2.3 Renderer(渲染器) 渲染页面 // fiber 时间切片 // js执行时间过长会操作卡顿的问题 出现性能问题 // 将一个大任务拆分成很多个小任务 分布在每一帧中 ``` ### root updateContainer ```js // ReactDOM.render => flushSync(updateContainer()); // createContainer() => root.render() => updateContainer() // => workLoop() // => render阶段 // => beginWork根据虚拟dom构建fiber tree dom-diff // => completer阶段 创建真实的dom节点 向上冒泡副作用 // => commit阶段 提交副作用 不可中断的 // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberReconciler.js function updateContainer(element, container) { // 并发模式和传统的不一样 // 同步渲染返回的lane是 SyncLane // 并发渲染 const updateLane = getCurrentEventPriority() 默认事件的优先级 16 const lane = requestUpdateLane(current); // 1. 创建更新 const update = createUpdate(eventTime, lane); update.payload = { element }; // 2. 将更新对象添加到current的更新队列上 updateQueue // 构建循环链表 更新lanes 返回root根节点 // concurrent的模式是先添加到数组中 const root = enqueueUpdate(current, update, lane); // 3. 开始调度 scheduleUpdateOnFiber(root, current, lane, eventTime); return lane; } // ReactFiberClassUpdateQueue function enqueueUpdate() {} ``` ### scheduleUpdateOnFiber ```js // 从根节点开始调度 并发模式和传统模式的区别 两者的lane不一样 // 同步的是Sync 并发的是 default 16 => 32 (加入了 SyncHydrationLane的lane) function scheduleUpdateOnFiber() { ensureRootIsScheduled(root, eventTime); } function ensureRootIsScheduled(root, currentTime) { // const nextLanes = getNextLanes(); // root.pendingLanes const newCallbackPriority = getHighestPriorityLane(nextLanes); let newCallbackNode; if (includesSyncLane(newCallbackPriority)) { // 同步渲染的模式 执行的是 performSyncWorkOnRoot scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root)); scheduleMicrotask(flushSyncCallbacks()); newCallbackNode = null; } else { // 并发渲染 执行的是 performConcurrentWorkOnRoot let schedulerPriorityLevel; // lane对应调度优先级 switch (lanesToEventPriority(nextLanes)) { } // scheduleCallback 调度模块 newCallbackNode = scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root) ); } } // 调度模块 优先队列 function scheduleCallback(cb) { // 暂时先使用 requestIdleCallback requestIdleCallback(callback); } ``` ### performConcurrentWorkOnRoot ```js // 开始工作循环 function performConcurrentWorkOnRoot(root) { let lanes = getNextLanes(); // 初次渲染的时候肯定是同步的方式的 let exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes); // 根据退出的状态进行判断 其实就是执行了 commitRoot() finishConcurrentRender(); } // 传统模式执行的也是 renderRootSync function renderRootSync(root, lanes) { // 1. 根据老fiber和属性创建新的fiber // current和workInProgress prepareFreshStack(root, lanes); workLoopSync(); } function finishConcurrentRender(root, exitStatus) { switch (exitStatus) { // 在 completeUnitOfWork 阶段完成之后会改为 RootCompleted case RootCompleted: { // 提交 commitRoot(root); break; } default: { throw new Error("Unknown root exit status."); } } } // 根据来的fiber和属性创建新的fiber // current是老的 workInProgress是新的 正在工作的 function prepareFreshStack() { const rootWorkInProgress = createWorkInProgress(root.current, null); // 并发模式下的render阶段的退出状态 workInProgressRootExitStatus = RootInProgress; // enqueueConcurrentClassUpdate 中的 enqueueUpdate将更新先一批批的添加到concurrentQueues数组中 // 在 prepareFreshStack 的时候在构建更新的循环链表 finishQueueingConcurrentUpdates(); return rootWorkInProgress; } // 开始工作循环 function workLoopSync() { while (workInProgress !== null) { performUnitOfWork(workInProgress); } } // 以fiber为工作单元 function performUnitOfWork(initOfWork) { const current = unitOfWork.alternate; // 开始工作 const next = beginWork(current, unitOfWork); unitOfWork.memoizedProps = unitOfWork.pendingProps; if (next === null) { // 没有子节点了就完成这个单元 completeUnitOfWork(unitOfWork); } else { workInProgress = next; } } ``` [fiberTree](img/3.fiberTree.jpeg) ```js // 1. 第一次beginWork => updateHostRoot // 1.1 处理更新队列 拿出 {element} // 1.2 协调children reconcileChildren root是肯定有current的 // 1.2.1 element是一个对象

placeSingleChild(reconcileSingleElement) 创建fiber对象返回 // reconcileSingleElement => createFiberFromElement => createFiber() // 1.3 返回刚创建的fiber workInProgress = next还有值 继续 beginWork // 2. 第二次的时候是 h1的原生dom标签 => updateHostComponent() // 2.1 如果是纯文本的话 nextChildren = null; 不创建fiber节点 // 2.2 继续reconcileChildren子fiber 这个时候current是null的 // 2.2.1 这个时候的children是一个数组 reconcileChildrenArray() // 2.2.2 遍历创建fiber对象 createChild() 根据不同类型创建不同的fiber // createFiberFromElement createFiberFromText // 2.2.3 构建fiber链条 return sibling // 2.3 返回 workInProgress.child; // 3. 第三次返回的是一个文本的fiber对象 updateHostText // 3.1 直接 return null 完成这个fiber // 4. 文本节点完成之后执行 completeUnitOfWork => completeWork() // 4.1 完成阶段也是判断tag执行不同的分支 // 4.2 文本节点创建文本 workInProgress.stateNode = createTextInstance // 4.3 bubbleProperties 向上冒泡副作用 // 4.4 return null // 4.5 存在sibling节点 workInProgress = siblingFiber; // 5. 开始工作 span的fiber节点 beginWork // 5.1 同理也是 updateHostComponent() // 5.2 但是这个时候的child是个纯文本 所以不创建子的fiber节点了 nextChildren = null // 5.3 reconcileChildren() 这个时候的children是null 返回null // 5.4 然后span这个fiber完成 // 6. span的fiber完成 completeWork() // 6.1 原生dom标签完成 HostComponent // 6.2 createInstance创建真实的dom节点 document.createElement(type) // 6.3 appendAllChildren 挂载children到自己的身上 初次挂载的时候是不会添加flags的 // parent.appendChild(child); // 6.4 处理dom属性 finalizeInitialChildren // 6.5 bubbleProperties(workInProgress)上交副作用 return null // 6.6 span完成之后 没有弟弟了 父fiber就完成了 完成h1 // 7. 完成h1的标签 也是一个 HostComponent // 完成h1的fiber // 8. completedWork为null // commitRoot() 提交副作用 // 8.1 判断是否有变更 subtreeHasEffects rootHasEffect // 8.2 commitMutationEffects // 8.3 又是判断tag标签 先是 HostRoot // 8.3.1 recursivelyTraverseMutationEffects 先递归处理children commitMutationEffectsOnFiber // 8.3.2 commitReconciliationEffects 在处理自己的 根据不同的副作用执行操作 // 8.4 初次挂载都是Placement的 commitPlacement // 8.4.1 找到真实的dom节点插入 完成渲染 可能不是真实的dom节点的 ``` #### beginWork 开始工作 ```js function beginWork(current, workInProgress, renderLanes) { switch (workInProgress.tag) { case HostRoot: { return updateHostRoot(current, workInProgress, renderLanes); } case HostComponent: { return updateHostComponent(current, workInProgress, renderLanes); } case HostText: { return updateHostText(current, workInProgress); } default: break; } } // 第一个就是root节点 function updateHostRoot() { // 取出update {payload: element} processUpdateQueue(workInProgress, nextProps, null, renderLanes); reconcileChildren(current, workInProgress, nextChildren, renderLanes); // 返回

的虚拟dom return workInProgress.child; } // 第二次的时候是
dom原生标签 function updateHostComponent(current, workInProgress) { const type = workInProgress.type; // div const nextProps = workInProgress.pendingProps; let nextChildren = nextProps.children; // props.children 拿到虚拟dom节点 const isDirectTextChild = shouldSetTextContent(type, nextProps); // 如果孩子是一个文本就不创建fiber节点了 if (isDirectTextChild) nextChildren = null; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } // 第三次 hello 文本节点 function updateHostText() { // 不做处理 直接完成 return null; } function reconcileChildren() { if (current) { reconcileChildFibers(); } else { mountChildFibers(); } } ``` #### reconcileChildFibers ```js // 协调children // dom-diff function reconcileChildFibers(returnFiber, currentFirstChild, newChild) { // 协调孩子 if (typeof newChild === "object" && newChild !== null) { // 1. 第一次root节点进来的时候 children是一个对象 element: {{$$typeof: REACT_ELEMENT_TYPE}} 对应的虚拟dom节点 // return了新创建的fiber节点 switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: { // react元素 return placeSingleChild( reconcileSingleElement( returnFiber, currentFirstChild, newChild, lanes ) ); } } // 2. 第二次的children是一个数组 ['hello', world] if (isArray(newChild)) { return reconcileChildrenArray( returnFiber, currentFirstChild, newChild, lanes ); } } // 3. 第三次 'hello' if (typeof newChild === "string") { // 创建fiber的子节点 createFiberFromText return reconcileSingleTextNode(reconcileSingleTextNode()); } // 4. 第四次 span的children是纯文本 child = null return null; } ``` ##### reconcileSingleElement ```js // 协调单个元素 创建fiber节点 function reconcileSingleElement(returnFiber, currentFirstChild, element) { // 创建fiber返回 const created = createFiberFromElement(element, returnFiber.mode, lanes); created.return = returnFiber; return created; } ``` ##### createFiberFromElement ```js // tag的处理 function createFiberFromElement() { const fiber = createFiberFromTypeAndProps( type, key, pendingProps, mode, lanes ); return fiber; } function createFiberFromTypeAndProps(type, key, pendingProps, mode, lanes) { // 开始是不知道类型的 let fiberTag = IndeterminateComponent; if (typeof type === "function") { } else if (typeof type === "string") { // div span 原生组件 fiberTag = HostComponent; } const fiber = createFiber(fiberTag, pendingProps, key, mode); return fiber; } ``` ##### placeSingleChild ```js // 放置child function placeSingleChild(newFiber) { // 根节点的root的current是存在的 增加了flags if (shouldTrackSideEffects && newFiber.alternate === null) { // 给fiber增加新增的flags newFiber.flags |= Placement; } return newFiber; } ``` ##### reconcileChildrenArray ```js // dom-diff 初次渲染就是创建fiber 构建fiber链条 return sibling function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren) { let resultingFirstChild = null; let newIdx = 0; for (; newIdx < newChildren.length; newIdx++) { // 1. 创建fiber const newFiber = createChild(returnFiber, newChildren[newIdx], lanes); // 2. 放置fiber newFiber.index = newIndex; placeChild(newFiber, lastPlacedIndex, newIdx); // 3. 构建fiber链表 if (previousNewFiber === null) { // 大儿子 resultingFirstChild = newFiber; } else { // 弟弟 构建fiber的链条 previousNewFiber.sibling = newFiber; } // 父fiber previousNewFiber = newFiber; } return resultingFirstChild; } ``` ##### createChild ```js // 创建child的fiber function createChild() { // 文本 const created = createFiberFromText(); // 对象 const created = createFiberFromElement(); created.return = returnFiber; return created; } ``` ##### placeChild ```js function placeChild() { newFiber.index = newIndex; } ``` #### completeUnitOfWork 完成工作 ```js function completeUnitOfWork(unitOfWork) { let completedWork = unitOfWork; do { const current = completedWork.alternate; const returnFiber = completedWork.return; let next; // 完成了 执行fiber的完成工作 // 如果是原生的就创建真实的dom节点 // next = completeWork(current, completedWork, renderLanes); next = completeWork(current, completedWork); if (next !== null) { workInProgress = next; return; } // 如果这个节点return null 找弟弟 // 上一个文本节点处理完成了 处理弟弟 对应的fiber节点 const siblingFiber = completedWork.sibling; if (siblingFiber !== null) { workInProgress = siblingFiber; return; } // 如果弟弟也完成了 就表示让parent完成 同时找叔叔开始工作 completedWork = returnFiber; // 这个很重要 workInProgress = completedWork; } while (completedWork !== null); // 更改状态 并发模型下的退出状态 if (workInProgressRootExitStatus === RootInProgress) { workInProgressRootExitStatus = RootCompleted; // 完成 commitRoot() } } ``` #### completeWork 完成 fiber ```js // ReactFiberCompleteWork // 创建真实的dom节点 // 和beginWork一样也是判断tag function completeWork(current, workInProgress) { switch (workInProgress.tag) { case HostText: { // 第一个完成的是文本节点 创建真实的dom节点 // document.createTextNode(text) workInProgress.stateNode = createTextInstance(); // 向上冒泡副作用 以前是收集副作用链条 effectList // 将儿子的副作用和自己的副作用全部上交 全部冒泡到根节点 统一处理 bubbleProperties(workInProgress); return null; } case HostComponent: { // 原生dom节点 // 1. 创建真实的dom节点 document.createElement(type) const instance = createInstance(); // 2. 将自己的孩子全部挂到自己的身上 parent.appendChild(child); appendAllChildren(instance, workInProgress, false, false); // 3. 处理dom属性 finalizeInitialChildren(); // 4. 上交副作用 bubbleProperties(workInProgress); return null; } case HostRoot: { // 根节点的完成 bubbleProperties(workInProgress); return null; } } } ``` ##### bubbleProperties 上交副作用 ```js function bubbleProperties(completedWork) { let subtreeFlags = NoFlags; // 拿到完成的fiber的儿子 let child = completedWork.child; // 遍历当前fiber的所有子节点 while (child !== null) { // 将child的副作用合并到上面 subtreeFlags |= child.subtreeFlags; // child的child的副作用上交 subtreeFlags |= child.flags; // child的flag child = child.sibling; } // 全部收集 completedWork.subtreeFlags |= subtreeFlags; } ``` #### commitRoot 提交阶段 ```js function commitRoot() { // 如果有副作用 if (subtreeHasEffects || rootHasEffect) { // before mutation const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(); // mutation阶段 commitMutationEffects(root, finishedWork, lanes); } } function commitMutationEffects(root, finishedWork) { commitMutationEffectsOnFiber(finishedWork, root); } // 又是根据不同的tag做处理 function commitMutationEffectsOnFiber(finishedWork, root) { switch (finishedWork.tag) { case HostRoot: // 递归提交 先处理child recursivelyTraverseMutationEffects(root, finishedWork, lanes); // 在处理自己的 commitReconciliationEffects(finishedWork); return; case HostText: { recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork); return; } default: break; } } // 先递归处理children function recursivelyTraverseMutationEffects(root, parentFiber) { if (parentFiber.subtreeFlags & MutationMask) { let child = parentFiber.child; while (child !== null) { // 递归处理children commitMutationEffectsOnFiber(child, root, lanes); child = child.sibling; } } } // 在处理自己的 function commitReconciliationEffects(finishedWork) { const flags = finishedWork.flags; // 不同的副作用执行不同的操作 if (flags & Placement) { // 插入操作 初次渲染的是插入操作 commitPlacement(finishedWork); finishedWork.flags &= ~Placement; } } ``` ##### commitPlacement ```js function commitPlacement(finishedWork) { // 找到host的fiber 找到真实的dom节点 const parentFiber = getHostParentFiber(finishedWork); switch (parentFiber.tag) { case HostComponent: { break; } case HostRoot: { // div#root const parent = parentFiber.stateNode.containerInfo; // 获取最近的弟弟dom节点 const before = getHostSibling(finishedWork); // 插入或者追加一个 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent); break; } default: break; } } // 向上找到真实的dom节点 function getHostParentFiber(fiber) { let parent = fiber.return; while (parent !== null) { // hostComponent hostRoot if (isHostParent(parent)) { return parent; } parent = parent.return; } } function insertOrAppendPlacementNodeIntoContainer(node, before, parent) { const { tag } = node; const isHost = tag === HostComponent || tag === HostText; if (isHost) { const stateNode = node.stateNode; if (before) { // insertBefore insertInContainerBefore(parent, stateNode, before); } else { // appendChild appendChildToContainer(parent, stateNode); } } } ``` ##### getHostSibling [getHostSibling](img/4.getHostSibling.jpeg) ```js // 找到参考点 // 需要是真实存在的dom节点 // 1. 找弟弟 如果弟弟有不是新增的(可以作为参考点) // 2. 没有弟弟就找 return (parent) 找return的弟弟 // 3. 不能是新插入的节点 在真实的dom中不存在 不能作为插入点 function getHostSibling(fiber) { let node = fiber; // 给while循环取名 siblings siblings: while (true) { while (node.sibling === null) { if (node.return === null || isHostParent(node.return)) { // 没有before的插入点 return null; } // 没有弟弟就找return node = node.return; } node.sibling.return = node.return; // 找弟弟 node = node.sibling; // 如果不是原生节点 while (node.tag !== HostComponent && node.tag !== HostRoot) { // 如果是一个新的需要插入的就找弟弟 插入的不是真实存在的dom if (node.flags & Placement) { continue siblings; } if (node.child === null) { continue siblings; } else { node.child.return = node; node = node.child; } } // 不是插入的就表示是真实存在的节点 if (!(node.flags & Placement)) { return node.stateNode; } } } ``` ## 组件 ```js // 函数组件 类组件(很少使用) // 在beginWork中如果类型是函数 // renderWithHooks ``` ### 函数组件 ```js // FunctionComponent // 1. 在root节点beginWork之后返回的是一个函数组件 // 2. 在beginWork的时候判断tag的类型为 未知的组件 IndeterminateComponent进行挂载 function mountIndeterminateComponent() { const props = workInProgress.pendingProps; // return Component() 就是执行函数组件返回 value = renderWithHook(); if (isClassComponent) { workInProgress.tag = ClassComponent; } else { // 函数组件 workInProgress.tag = FunctionComponent; reconcileChildren(null, workInProgress, value, renderLanes); return workInProgress.child; } } function renderWithHooks() { // TODO: 执行hooks的逻辑 let children = Component(props, secondArg); return children; } // 3. 执行函数组件拿到 jsx 虚拟dom节点之后 继续 reconcileChildren // 4. 完成节点 当函数组件的fiber节点都完成之后 函数组件也要完成 bubbleProperties(workInProgress); // 5. 提交阶段 recursivelyTraverseMutationEffects(root, finishedWork); commitReconciliationEffects(finishedWork); // 6. 在函数组件提交自己的副作用的时候 placeSingleChild的时候有一个flags 执行 commitPlacement 操作 // 执行插入操作 function insertOrAppendPlacementNodeIntoContainer() { if (isHost) { } else { // 组件 找到真实的dom节点插入 const child = node.child; if (child !== null) { insertOrAppendPlacementNodeIntoContainer(child, before, parent); let sibling = child.sibling; while (sibling !== null) { insertOrAppendPlacementNodeIntoContainer(sibling, before, parent); sibling = sibling.sibling; } } } } ``` ### 类组件 ```js // 类组件使用已经很少了 不考虑 setState的更新过程了 // 只考虑初次渲染的逻辑 // 1. 实现 React.Component() // ReactBaseClasses function Component(props, context, updater) { this.props = props; this.context = context; this.refs = {}; // 更新器 this.updater = updater; } // 区分函数组件 Component.prototype.isReactComponent = {}; // setState方法 Component.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState, callback, "setState"); }; // 纯组件 pureComponentPrototype.isPureReactComponent = true; // 2. beginWork阶段 在root处理children的时候 function createFiberFromTypeAndProps() { if (isClassComponent) { // 函数组件的时候修改了类型 fiberTag = ClassComponent; } } // 3. 组件在beginWork的时候执行的就是 function updateClassComponent() { const instance = workInProgress.stateNode; if (instance === null) { // 这个很重要 给updater赋值classComponentUpdater constructClassInstance(); mountClassInstance(); } else { // 更新 // updateClassInstance() } // 主要就是执行render函数得到child 然后 reconcile const nextUnitOfWork = finishClassComponent(); return nextUnitOfWork; } function constructClassInstance() { let instance = new ctor(props, context); adoptClassInstance(workInProgress, instance); return instance; } // 更新器 function adoptClassInstance() { instance.updater = classComponentUpdater; workInProgress.stateNode = instance; } // 4. 挂载类组件 // ReactFiberClassComponent.js function mountClassInstance() { // 更新队列 initializeUpdateQueue(workInProgress); // 处理content // 处理生命周期函数 } // 5. 返回unitOfWork() function finishClassComponent() { const instance = workInProgress.stateNode; let nextChildren; // 执行render函数得到返回值的虚拟dom节点 然后递归处理孩子 nextChildren = instance.render(); reconcileChildren(current, workInProgress, nextChildren, renderLanes); workInProgress.memoizedState = instance.state; return workInProgress.child; } // 6. 完成阶段 bubbleProperties(workInProgress); // 7. 提交阶段 recursivelyTraverseMutationEffects(root, finishedWork); commitReconciliationEffects(finishedWork); ``` #### setState ```js // 1. Component上的方法 调用的是更新器上的方法 Component.prototype.setState = function (partialState, callback) { this.updater.enqueueSetState(this, partialState, callback, "setState"); }; // 2. 在mount的过程中 // updateClassComponent => constructClassInstance => adoptClassInstance function adoptClassInstance() { instance.updater = classComponentUpdater; } // 3. 更新器 const classComponentUpdater = { enqueueSetState() { // 1. 创建更新 const update = createUpdate(eventTime, lane); update.payload = payload; // 2. 入队 const root = enqueueUpdate(fiber, update, lane); // 3. 调度 setState最后也是执行的 schedule scheduleUpdateOnFiber(root, fiber, lane, eventTime); }, }; // ReactFiberClassUpdateQueue function enqueueUpdate() { if (isUnsafeClassRenderPhaseUpdate()) { // 构建更新的环形链表 return unsafe_markUpdateLaneFromFiberToRoot(); } else { // 现在都是 concurrent的 先添加到数组中在preStack的时候在构建成循环链表 return enqueueConcurrentClassUpdate(); } } ``` ## 事件系统 ```js // 更新依赖事件系统 先了解react的事件系统 // 1. 注册 // 2. 监听 addEventListener // 3. 触发 dispatchEvent 合成事件 事件优先级 // react的事件是基于插件来实现的 // 合成事件 为什么需要合成 性能高 处理浏览器兼容性 SyntheticEvent // 插件系统 new Set() 5个插件往里面加事件 // react17对事件进行很大的改动 ``` ### 事件流 [eventUse](img/5.eventuse.jpeg) ```js // 事件流 捕获 => 目标 => 冒泡 // 事件委托 事件代理 利用时间冒泡实现 // 事件注册 通过插件实现 代理到目标容器上 之前是代理在 document 文档对象上的 // 事件绑定 element.addEventListener(event, function, useCapture) // 事件触发 dispatchEvent ``` ### 事件注册 ```js // 在 createRoot的过程中会进行事件的注册 // 监听只有一次的 function createRoot(container) { listenToAllSupportedEvents(container); } // 1. 通过插件来进行注册 // 1.1 记录click和onClick的映射关系 // 1.2 给allNativeEvents添加事件 SimpleEventPlugin.registerEvents(); // events事件相关的 // react-dom-bindings/src/events/DOMPluginEventSystem function listenToAllSupportedEvents(container) { // 遍历事件进行注册 通过插件系统给 allNativeEvents 赋值 allNativeEvents.forEach((domEventName) => { // 2. 进行事件的监听 listenToNativeEvent(domEventName, false, rootContainerElement); listenToNativeEvent(domEventName, true, rootContainerElement); }); } // 2 监听事件 拿到事件去监听 function listenToNativeEvent() { // addTrappedEventListener() // 2.1 拿到listener 事件是有优先级的概念的 // click对应的是离散事件 dispatchDiscreteEvent let listener = createEventListenerWrapperWithPriority(); // 2.2 绑定监听 捕获和冒泡阶段 就是 target.addEventListener addEventCaptureListener(); addEventBubbleListener(); } ``` ### 插件系统 ```js // react的事件是基于插件来实现的 一共5个事件插件 // ChangeEventPlugin SelectEventPlugin EnterLeaveEventPlugin BeforeInputEventPlugin // SimpleEventPlugin click等事件 // 插件就是往allNativeEvents中添加数据 然后进行监听 const allNativeEvents = new Set(); ``` #### 简单事件插件 ```js SimpleEventPlugin.registerEvents(); // registerSimpleEvents // 定义一些列的简单事件 将这些添加到allNativeEvents中 const simpleEventPluginEvents = ["click"]; // 用来记录原生click和react事件onClick的映射关系 const topLevelEventsToReactNames = new Map(); // 用来记录onClick和[click]的关系 一个react事件可能是由多个事件的 比如onChange就对应多个事件 const registrationNameDependencies = {}; // DOMEventProperties function registerSimpleEvents() { for (let i = 0; i < simpleEventPluginEvents.length; i++) { const eventName = simpleEventPluginEvents[i]; // click const domEventName = eventName.toLowerCase(); // click const capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1); // Click // 注册两个阶段的处理函数 // registerSimpleEvent(domEventName, "on" + capitalizedEvent); // click => onClick // 1. 使用一个map来记录原生事件和react事件的映射关系 // 触发了click事件我们要拿到对应的props.onClick事件执行 topLevelEventsToReactNames.set(domEventName, reactName); // 2. 注册两阶段的事件 注册捕获和冒泡的事件 就是往 allNativeEvents 中添加数据 // registerTwoPhaseEvent(reactName, [domEventName]); // dependencies: [domEventName] registrationNameDependencies[registrationName] = dependencies; for (let i = 0; i < dependencies.length; i++) { allNativeEvents.add(dependencies[i]); } } } ``` ### 事件派发 ```js // dispatchEvent触发事件就是拿到对应的listener然后去执行 // 当createRoot的时候会初始化事件系统 然后不同的插件注册不同的事件 // 然后将事件都注册在target容器上(之前是委托给document) target.addEventListener() // 不同的事件有不同的优先级 暂时先不考虑 // click对应的是离散 dispatchDiscreteEvent 当我们触发这些事件的时候就会进行事件的派发 // 不同的事件对应不同的事件优先级 也对应不同的处理函数 // 不同的事件是优先级不同 最后还是执行 dispatchEvent函数 function dispatchDiscreteEvent() { setCurrentUpdatePriority(); // 设置当前的优先级 dispatchEvent(); // 执行完之后还原上次的优先级 } function dispatchEvent() { // dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay() // 就是找到最近的fiber节点 return_targetInst = getClosestInstanceFromNode(nativeEventTarget); const blockedOn = findInstanceBlockingEvent(); // 为插件系统派发事件 dispatchEventForPluginEventSystem( domEventName, eventSystemFlags, nativeEvent, return_targetInst, // 作为参数传递 targetContainer ); } function dispatchEventForPluginEventSystem() { // 合成事件中的事件都是批量执行的 // executionContext |= BatchedContext; batchedUpdates(() => dispatchEventsForPlugins( domEventName, eventSystemFlags, nativeEvent, ancestorInst, targetContainer ); ); } ``` #### dispatchEventsForPlugins ```js // 最后还是回到了插件系统上 // 为插件系统派发事件 function dispatchEventsForPlugins() { const dispatchQueue = []; // 1. 提取事件 给dispatchQueue 赋值 // 插件去实现的 SimpleEventPlugin.extractEvents extractEvents(); // 2. 处理事件 拿到事件按顺序执行 捕获和冒泡顺序是不一样的 processDispatchQueue(); } ``` ##### extractEvents ```js // 提取事件 function extractEvents() { // 不同的事件对应不同的合成事件 let SyntheticEventCtor = SyntheticEvent; // click对应的是鼠标合成事件 SyntheticEventCtor = SyntheticMouseEvent; // 累加单个阶段的监听 const listeners = accumulateSinglePhaseListeners(); if (listeners.length > 0) { const event = new SyntheticMouseEvent(); // 如果有监听函数就添加到派发队列中 dispatchQueue.push({ event, listeners }); } } function accumulateSinglePhaseListeners() { let instance = targetFiber; let listeners = []; // 递归向上去找处理函数 只能是原生的节点 while (instance !== null) { // props.click 如果找到这个属性 const listener = getListener(instance, reactEventName); listeners.push( createDispatchListener(instance, listener, lastHostComponent) ); instance = instance.return; } } // 1. 虚拟dom节点 element.props // 2. 在创建fiber的时候 fiber.memProps = props // 3. 在完成阶段 createInstance的时候 updateFiberProps(domElement, props) // node["__reactProps$" + randomKey] = props // 4. 这里拿到的就是虚拟dom节点上的props 里面就可以拿到我们定义的 onClick回调函数了 function getListener() { // node["__reactProps$" + randomKey] = props // 同理在完成阶段的 createInstance的时候会进行赋值 const props = getFiberCurrentPropsFromNode(stateNode); // props.onClick const listener = props[registrationName]; // 处理disabled的 // shouldPreventMouseEvent() return listener; } ``` ##### processDispatchQueue ```js // 处理事件 遍历去执行 function processDispatchQueue() { for (let i = dispatchListeners.length - 1; i >= 0; i--) { const { instance, currentTarget, listener } = dispatchListeners[i]; if (instance !== previousInstance && event.isPropagationStopped()) { return; } // listener(event); executeDispatch(event, listener, currentTarget); previousInstance = instance; } } // 合成事件实例的 currentTarget 是在不断变化的 // event nativeTarget 是原始的事件元是不回变化的 // currentTarget 是当前的事件元 是不断变化的 指向当前的 target function executeDispatch(event, listener, currentTarget) { event.currentTarget = currentTarget; // invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event); listener && listener(event); event.currentTarget = null; } ``` #### findInstanceBlockingEvent ```js function findInstanceBlockingEvent() { let targetInst = getClosestInstanceFromNode(nativeEventTarget); return_targetInst = targetInst; } const internalInstanceKey = "__reactFiber$" + randomKey; function getClosestInstanceFromNode() { return targetNode[internalInstanceKey]; } // 合成加上的这个属性 在完成阶段 createInstance 的时候 会提前缓存fiber节点到真实dom上 function createInstance() { // node["__reactFiber$" + randomKey] = hostInst; precacheFiberNode(internalInstanceHandle, domElement); // 还会更新fiber的props 之后找props.onClick就可以快速的找到 // node["__reactProps$" + randomKey] = props updateFiberProps(domElement, props); } ``` #### 合成事件 ```js // SyntheticEvent // 我们在回调中获取到的event不是原生的event对象是react合成的对象 // 为什么需要合成事件 性能高 屏蔽浏览器差异 // 不同事件对应不同的事件对象 // 不同的合成事件对应的接口属性不一样 const MouseEventInterface = { ...UIEventInterface, clientX: 0, clientY: 0, }; // 通过不同的接口创建不同的合成事件 click对应的是鼠标合成事件 const SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface); function createSyntheticEvent(Interface) { // 基类 拷贝属性 function SyntheticBaseEvent() { for (const propName in Interface) { this[propName] = nativeEvent[propName]; } return this; } Object.assign(SyntheticBaseEvent.prototype, { preventDefault() {} stopPropagation() {} }) return SyntheticBaseEvent } ``` ## 类组件状态变更 setState ```js // 1. 点击button触发事件系统执行回调函数 // 2. 调用this.setState函数 // 3. 执行Component原型上的方法 Component.prototype.setState = function () { this.updater.enqueueSetState(); }; // 4. 执行更新器的方法 // update是在何时赋值的? 组件在beginWork的过程中 如果是类组件 function updateClassComponent() { constructClassInstance(); // instance.updater = classComponentUpdater; } // 5. 跟新器 const classComponentUpdater = { enqueueSetState() { // 5.1 创建更新 createUpdate(); // 5.2 更新入队 enqueueUpdate(); // 5.3 开始调度更新 scheduleUpdateOnFiber(); }, }; // 6. 调度 最后的最后都要回到这个调度来 // click事件的优先级是 1 SyncLane scheduleSyncCallback(performSyncWorkOnRoot); // 然后执行 workLoop performUnitOfWork 这个时候current就不是null了 function beginWork(current, workInProgress, renderLanes) { if (current !== null) { } // 判断tag // 类组件 updateClassComponent(); } // 7. 更新类组件 function updateClassComponent() { if (instance === null) { // 初次挂载 mountClassInstance(); } else { // 更新 更新state状态 updateClassInstance(); } const nextUnitOfWork = finishClassComponent(); return nextUnitOfWork; } // ReactFiberClassComponent function updateClassInstance() { cloneUpdateQueue(current, workInProgress); // 计算新的state processUpdateQueue(workInProgress, newProps, instance, renderLanes); // context上下文 // 还有一些生命周期 instance.props = newProps; instance.state = newState; } function finishClassComponent() { // 执行render得到新的虚拟dom 然后协调children nextChildren = instance.render(); reconcileChildren(); } // 8. 类组件协调新的child 开心下一个unit的工作 // updateHostComponent 更新原生dom节点 // reconcileChildren 执行的就是 reconcileChildFibers了 初次挂载是mountChildFibers // 更新阶段就需要加到副作用标识了 // 更新 dom-diff // 1. beginWork阶段的处理 // 1.1 第一个更新的是root节点 返回child就是我们定义的class类组件 cloneChildFibers(current, workInProgress); // 1.2 classComponent开始工作 function updateClassComponent() { if (instance === null) { // 初次挂载 initializeUpdateQueue 初始化更新队列 mountClassInstance(); } else { // 更新 // cloneUpdateQueue // processUpdateQueue 处理更新队列 更新state this.setState添加的update updateClassInstance(); } // 执行render函数返回虚拟dom节点 reconcileChildren return finishClassComponent(); } // 1.3 classComponent协调子节点的时候是div原生dom节点 返回这个fiber节点 placeSingleChild(reconcileSingleElement()); function reconcileSingleElement() { let child = currentFirstChild; if (child) { // 更新阶段child是存在的 // const clone = createWorkInProgress(fiber, pendingProps); const existing = useFiber(child); existing.return = returnFiber; return existing; } // 初次挂载阶段创建一个fiber返回 const created = createFiberFromElement(element, returnFiber.mode, lanes); created.return = returnFiber; return created; } // 设计到dom-diff的比较 位置的移动 需要判断index function placeSingleChild() { const current = newFiber.alternate; if (current !== null) { // 判断位置 如果是移动的节点 newFiber.flags |= Placement; } else { newFiber.flags |= Placement; } } // 1.4 div节点 原生dom节点 协调子节点 function updateHostComponent() { reconcileChildren(); } // 1.5 children是一个数组 [p, button] // dom-diff就在这里 function reconcileChildrenArray() { let oldFiber = currentFirstChild; // lodFiber存在 更新的阶段 和初次挂载的基本是一样的 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { const newFiber = updateSlot(); placeChild(); if (previousNewFiber === null) { resultingFirstChild = newFiber; } else { previousNewFiber.sibling = newFiber; } previousNewFiber = newFiber; } // 初次挂载就是循环创建fiber然后构建fiber链条即可 if (oldFiber === null) { for (; newIdx < newChildren.length; newIdx++) { const newFiber = createChild(); // 创建 placeChild(); // 放置 // 构建fiber链条 if (previousNewFiber === null) { // 大儿子 resultingFirstChild = newFiber; } else { // 弟弟 构建fiber的链条 previousNewFiber.sibling = newFiber; } // 父fiber previousNewFiber = newFiber; } } // 返回大儿子 p return resultingFirstChild; } // 更新 function updateSlot() { if (typeof newChild === "string" || typeof newChild === "number") { // 更新文本 return updateTextNode(); } if (typeof newChild === "object" && newChild !== null) { // 更新元素 return updateElement(); } } function updateTextNode() { // 新增阶段 if (current === null) { createFiberFromText(); // createFiberFromElement() } else { // 更新 const existing = useFiber(); } } // 1.6 返回 p的节点继续工作 还是原生节点 但是child是一个文本 nextChildren = null // 开始完成这个节点 function updateHostComponent() { const isDirectTextChild = shouldSetTextContent(); if (isDirectTextChild) nextChildren = null; reconcileChildren(); } // 1.7 p节点完成后就返回 sibling button // 然后完成button // button完成之后完成div节点 // 在完成classComponent // 再完成root节点 // 都完成直接就进行commit提交 // 2. completed阶段的处理 // 2.1 在p节点工作的时候 child是一个文本children为null 然后完整这个 p节点 function completeWork() { switch (workInProgress.tag) { // p阶段完成的时候是原生dom节点 case HostComponent: { if (current !== null) { // 更新操作 workInProgress.updateQueue = updatePayload; // diffProperties 比对属性 // 如果有更新就标记为update markUpdate workInProgress.flags |= Update; updateHostComponent(current, workInProgress, type, newProps); } else { // 初次挂载 const instance = createInstance(); appendAllChildren(); // 初始化dom属性 finalizeInitialChildren(); } // 上下冒泡副作用 bubbleProperties(workInProgress); return null; } // 原生dom节点是判断是否有updatePayload 如果是文本节点就判断新旧文本是否相等 case HostText: { if (current !== null) { // if(oldText !== newText) markUpdate(workInProgress) updateHostText(); } } } } // 有更新就增加了副作用标识 function updateHostComponent() { // diffProperties 收集 updatePayload 这里是没有更新的 const updatePayload = prepareUpdate(); workInProgress.updateQueue = updatePayload; // 如果有更新就标记为 更新 if (updatePayload) { // workInProgress.flags |= Update; markUpdate(workInProgress); } } // 2.2 在完成阶段将副作用上交给 div // 2.3 div完成的时候将副作用上交给classComponent // 2.4 类组件再将自己的副作用和div上交的副作用一起都给root // root节点完成之后就开始提交 // 3. 提交阶段的处理 // workLoop function commitRoot() { // 有副作用 if (subtreeHasEffects || rootHasEffect) { commitMutationEffects(root, finishedWork, lanes); } } // 3.1 提交阶段 开始是root节点 // 有两个副作用标识的 // 一个是针对children的 subtreeFlags // 一个是自己的 flags // ReactFiberCommitWork 又是各种递归操作 function commitMutationEffects() { // commitMutationEffectsOnFiber() switch (finishedWork.tag) { case HostRoot: { // 先递归处理child的 subtreeFlags recursivelyTraverseMutationEffects(root, finishedWork, lanes); // 在处理自己的 flags 好像只处理的 Placement commitReconciliationEffects(finishedWork); // 处理更新的 if (flags & Update) { } } } } // 递归处理child 就是上交上来的 subtreeFlags 不在收集effectList副作用链表了 function recursivelyTraverseMutationEffects() { // 处理的是 subtreeFlags if (parentFiber.subtreeFlags & MutationMask) { let child = parentFiber.child; while (child !== null) { // 递归处理children commitMutationEffectsOnFiber(child, root, lanes); child = child.sibling; } } } // 处理自己的 function commitReconciliationEffects(finishedWork) { const flags = finishedWork.flags; if (flags & Placement) { commitPlacement(finishedWork); // 插入操作 finishedWork.flags &= ~Placement; } } // 3.2 root节点递归处理classComponent的副作用 // 也是递归处理 到div HostComponent 同理也是先递归处理child // 先处理p然后再是span(没有副作用的不处理) // 处理p // p的副作用标识为: flags 4 subTreeFlags 0 function commitMutationEffectsOnFiber() { switch (finishedWork.tag) { case HostComponent: { recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork); // 处理更新 if (flags & Update) { // 在 完成阶段 updateHostComponent 收集的更新 const updatePayload = finishedWork.updateQueue; // 提交更新 commitUpdate(); } } } } // 3.3 提交更新 function commitUpdate() { // 更新dom属性 updateDOMProperties() // children要单独处理文本的问题 setTextContent updateProperties(domElement, updatePayload, type, oldProps, newProps); // 更新fiber的属性 updateFiberProps(domElement, newProps); } ``` ## hooks [renderWithHooks](img/7.renderWithHooks.png) ```js // react通过一个全局变量来管理 ReactCurrentDispatcher // React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; const ReactCurrentDispatcher = { current: null }; const dispatcher = ReactCurrentDispatcher.current; // react包中的ReactHooks文件导出相应的方法 function useState() { // hook通过 ReactCurrentDispatcher 来赋值 return dispatcher.useState(); } function useReducer() { return dispatcher.useReducer(); } // 在beginWork的过程中 函数组件我们使用的是renderWithHooks // 具体的逻辑是在 react-reconciler 包中的 ReactFiberHooks function renderWithHooks() { // hooks的逻辑就是在这里 // 给dispatch赋值 ReactCurrentDispatcher.current = current === null || current.memoizedState === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate; } // 挂载的时候 const HooksDispatcherOnMount = { useState: mountState, useReducer: mountReducer, }; // 更新的时候 const HooksDispatcherOnUpdate = { useState: updateState, useReducer: updateReducer, }; ``` ### useReducer #### mountReducer [mountReducer](img/8.mountReducer.jpeg) ```js // useState是useReducer的语法糖 function renderWithHooks() { // 给hooks赋值 ReactCurrentDispatcher.current = current === null || current.memoizedState === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate; // 在 renderWithHooks 的过程中会执行Component 会调用函数组件执行 // 会执行 React.useReducer() 函数 let children = Component(props, secondArg); } // mount挂载阶段 支持第三个参数的 function mountReducer(reducer, initialArg, init) { // 构建hook的单向链表 const hook = mountWorkInProgressHook(); hook.memoizedState = hook.baseState = initialArg; // hook的状态 hook.queue = {}; // hook的更新队列 // 返回的更新函数 const dispatch = (queue.dispatch = dispatchReducerAction); return [hook.memoizedState, dispatch]; } // dispatch派发函数 function dispatchReducerAction(fiber, queue, action) { console.log("dispatchReducerAction"); } ``` #### updateReducer [updateReducer](img/9.updateReducer.png) ```js // 点击按钮的时候就是执行绑定的dispatch方法 function dispatchReducerAction() { // 更新入队 const root = enqueueConcurrentHookUpdate(); // 开始调度 都是回归到了这个方法 scheduleUpdateOnFiber(); } // 在beginWork阶段更新函数组件 重新执行 Component() function updateFunctionComponent() { // 执行renderWithHooks nextChildren = renderWithHooks(); reconcileChildren(); } // 再次执行 React.useReducer() 更新阶段了 function updateReducer(reducer, initialArg, init) { // 获取新的hook const hook = updateWorkInProgressHook(); // 更新队列 const queue = hook.queue; const pendingQueue = queue.pending; // 获取老的hook const current = currentHook; let newState = current.memoizedState; if (pendingQueue !== null) { queue.pending = null; // 第一个更新 const first = pendingQueue.next; let update = first; do { // 计算新的状态 const action = update.action; newState = reducer(newState, action); update = update.next; } while (update !== null && update !== first); } hook.memoizedState = newState; return [hook.memoizedState, hook.dispatch]; } // 在进行dom-diff比较 更新页面 // commitRoot() 提交变更 ``` #### update ```js // 更新阶段 // root => functionComponent => div // 1. beginWork阶段 // reconcileSingleElement 进行 dom-diff的比对 // 然后函数组件 执行 renderWithHooks 重新执行函数 然后重新执行 useReducer() function updateFunctionComponent() {} // updateReducer function updateReducer() {} // 2. completeWork 完成阶段 if (current !== null && workInProgress.stateNode != null) { // 更新阶段 updateHostComponent(); } function updateHostComponent() { // 1. 收集payload ['id', 'newId', 'children': '2'] const updatePayload = prepareUpdate(); // 2. 标记为更新 workInProgress.flags |= Update; } // 3. 提交阶段 // 3.1 递归处理children // 3.2 处理自己的placement // 3.3 Update 提交上面的payload if (flags & Update) { commitUpdate(); // // 更新dom属性 文本children的单独处理 // updateProperties(domElement, updatePayload, type, oldProps, newProps); // // 更新fiber的属性 // updateFiberProps(domElement, newProps); } ``` ### useState ```js // useState是useReducer的语法糖 // 内置了reducer的useReducer // 多个的setState应该是合并的 批量更新的 function mountState() { const hook = mountWorkInProgressHook; const queue = {}; const dispatch = (queue.dispatch = dispatchSetState.bind()); return [hook.memoizedState, dispatch]; } // 派发函数 function dispatchSetState(fiber, queue, action) { const update = {}; const root = enqueueConcurrentHookUpdate(); // 调度 scheduleUpdateOnFiber(root, fiber); } // 更新 调用 updateReducer function basicStateReducer(state, action) { return typeof action === "function" ? action(state) : action; } function updateState(initialState) { return updateReducer(basicStateReducer, initialState); } ``` ### useEffect [eventLoop](img/13.eventLoop.jpeg) [useEffect](img/14.useEffect.jpeg) ```js // 副作用操作 dom操作 接口调用 // 初次挂载 function mountEffect(create, deps) { // 和useLayout的逻辑是一样的 区别在于执行的时间不一样 对应的是fiber的flags不一样 // PassiveEffect Passive hook的标识是Passive 8 return mountEffectImpl(PassiveEffect, HookPassive, create, deps); } function mountEffectImpl() { const hook = updateWorkInProgressHook(); // 依赖 const nextDeps = deps === undefined ? null : deps; let destroy = undefined; currentlyRenderingFiber.flags |= fiberFlags; // fiber的 memoizedState 指向的是hook的单向链表 // useState的 memoizedState 指向的是 内部的状态 // hook的 memoizedState 指向一个 effect的循环链表 // fiber的updateQueue的componentUpdateQueue的lastEffect指向effect的state的循环链表的最后一个effect // effect为什么要单独创建一个循环链表??? 遍历 effect更方便 不需要遍历单向的effect链表 hook.memoizedState = pushEffect( HookHasEffect | hookFlags, create, undefined, nextDeps ); } // 构建更新的循环链表 function pushEffect() { const effect = {}; let componentUpdateQueue = currentlyRenderingFiber.updateQueue; if (componentUpdateQueue === null) { // 创建一个函数组件的更新队列 {lastEffect: null} componentUpdateQueue = createFunctionComponentUpdateQueue(); } else { } return effect; } // 在提交阶段 执行副作用 function commitRoot() { // 如果自己有 Passive 或者child有Passive 表示有需要执行的 // 在下一次的调度中 执行 effect的钩子 if ( (finishedWork.subtreeFlags & PassiveMask) !== NoFlags || (finishedWork.flags & PassiveMask) !== NoFlags ) { if (!rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects = true; // 添加到调度中 NormalSchedulerPriority 优先级 scheduleCallback(NormalSchedulerPriority, () => { flushPassiveEffects(); return null; }); } } if (subtreeHasEffects || rootHasEffect) { // 1. before mutation dom变更前 // const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(); // 2. 提交阶段 commitMutationEffects(root, finishedWork, lanes); // 3. dom变更之后 commitLayoutEffects(finishedWork, root, lanes); } } // effect的执行 function flushPassiveEffects() { // 先执行 unMount commitPassiveUnmountEffects(); // 再执行mount commitPassiveMountEffects(); } // 两个阶段的处理是一样的 都是先递归然后处理执行的 // 处理函数也是拿到循环的更新队列去执行 function commitPassiveMountEffects() { // commitPassiveMountOnFiber // 判断类型 switch (finishedWork.tag) { // 函数组件 case FunctionComponent: { // 先递归 recursivelyTraversePassiveMountEffects(); if (flags & Passive) { commitHookPassiveMountEffects(); } } } } function recursivelyTraversePassiveMountEffects() { while (child !== null) { commitPassiveMountOnFiber( root, child, committedLanes, committedTransitions ); child = child.sibling; } } // 执行hook // commitHookEffectListMount function commitHookEffectListMount(flags, finishedWork) { const updateQueue = finishedWork.updateQueue; const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; if (lastEffect !== null) { // 循环链表 const firstEffect = lastEffect.next; let effect = firstEffect; do { if ((effect.tag & flags) === flags) { // Mount const create = effect.create; // 在unmount的时候就是执行 destroy函数 effect.destroy = create(); } effect = effect.next; } while (effect !== null && effect !== firstEffect); } } // 更新阶段 function updateEffect(create, deps) { return updateEffectImpl(PassiveEffect, HookPassive, create, deps); } function updateEffectImpl(fiberFlags, hookFlags, create, deps) { const hook = updateWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; let destroy = undefined; // 如果依赖没变就不需要执行 直接return if (currentHook !== null) { const prevEffect = currentHook.memoizedState; destroy = prevEffect.destroy; if (nextDeps !== null) { const prevDeps = prevEffect.deps; if (areHookInputsEqual(nextDeps, prevDeps)) { // 还是要push到里面 维持链表 hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps); return; } } } // 如果依赖没变就不添加 currentlyRenderingFiber.flags |= fiberFlags; hook.memoizedState = pushEffect( HookHasEffect | hookFlags, create, destroy, nextDeps ); } ``` ### useLayoutEffect ```js // 和useEffect类型 执行的时机不一样 function mountLayoutEffect(create, deps) { // Update hook的标识是 Layout 4 return mountEffectImpl(UpdateEffect, HookLayout, create, deps); } // 更新 function updateLayoutEffect(create, deps) { return updateEffectImpl(UpdateEffect, HookLayout, create, deps); } // 在 commitLayoutEffects阶段 判断类型 如果是函数组件 // commitMutationEffectsOnFiber function commitMutationEffectsOnFiber() { switch (finishedWork.tag) { case FunctionComponent: { recursivelyTraverseMutationEffects(root, finishedWork); commitReconciliationEffects(finishedWork); if (flags & Update) { // 执行 Unmount 的钩子 commitHookEffectListUnmount(); } } } } // 之后在提交effect和useEffect的是类似的 // commitLayoutEffectOnFiber function commitLayoutEffectOnFiber() { // 也是判断tag switch (finishedWork.tag) { case FunctionComponent: { // 也是先递归处理 recursivelyTraverseLayoutEffects( finishedRoot, finishedWork, committedLanes ); if (flags & Update) { // 在提交自己的 commitHookEffectListMount(); commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect); } break; } } } // 递归的处理 function recursivelyTraverseLayoutEffects() { while (child !== null) { const current = child.alternate; commitLayoutEffectOnFiber(root, current, child, lanes); child = child.sibling; } } ``` ### use ```js ``` ### useRef ```js ``` ### useContext ```js ``` ## dom-diff ### 单节点的 [dom-diff](img/10.dom-diff.jpeg) ```js // 1. 单节点的处理 function reconcileSingleElement() { // (1) 判断是否有老的fiber if (child !== null) { // (2) 判断key if (child.key === element.key) { // (3) 判断type if (child.elementType === element.type) { // 都相同就表示复用 const existing = useFiber(); } else { // type不一样 deleteRemainingChildren() break } } else { // 否则就删除 deleteChild(); } } // 然后创建新的 createFiberFromElement(); } // 递归删掉 function deleteRemainingChildren() { while (childToDelete !== null) { deleteChild(returnFiber, childToDelete); childToDelete = childToDelete.sibling; } } // react18中会用一个 deletions 来存储要删除的 // 加上一个flags returnFiber.flags |= ChildDeletion function deleteChild() { returnFiber.deletions = [childToDelete]; // 有子节点要删除 在提交阶段要删掉 returnFiber.flags |= ChildDeletion; } // 在提交阶段 function recursivelyTraverseMutationEffects() { for (let i = 0; i < deletions.length; i++) { commitDeletionEffects() } } function commitDeletionEffects() { // 递归删除 recursivelyTraverseDeletionEffects() // parentInstance.removeChild(child); removeChildFromContainer() } // 2. 以前有多个的 ``` ### 多个节点的 [dom-diff](img/11.dom-diff.jpeg) [dom-diff](img/12.dom-dif.jpeg) ```js // dom-diff三个规则 // 1. 只对同级元素进行比较 不同层级的不比较 // 2. 不同的类型对应不同的元素 // 3. 通过key来标识同一个节点 // 三轮遍历 // (1) 第一轮遍历 // 如果key不同就直接结束 // newChildren 或 oldFiber 遍历完,结束本轮循环 // key相同type不同标记为删除 继续遍历 // key相同type相同就复用 继续遍历 // (2) 第二轮遍历 // newChildren 遍历完而 oldFiber 还有 就删除剩余的 // oldFiber完了 newChildren还有 就标记为插入 // 都遍历完成了 diff结束 // 都没有完成 就要进行 节点的移动 // (3) 第三轮遍历 // 处理节点移动的情况 // 1. 将剩下的老的fiber放入一个map existingChildren // 2. 声明一个 lastIndex用来记录最后一个不需要移动的来节点 // 3. 循环剩下的虚拟dom节点 在map中找key和type相同的节点 // 4. 如果能找到就复用将老的fiber从map中删除 判断lastIndex和index的关系 // 如果index < lastIndex 不需要移动老的fiber // 否则需要移动老的fiber 更新lastIndex变量 // 5. 找不到就创建洗你的fiber节点 // 6. 如果虚拟dom循环借宿了 map中还有就将剩下的全部标记为删除 function reconcileChildrenArray() { // 1. 第一轮循环 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { // 试图去更新 const newFiber = updateSlot(); // 放置child lastPlacedIndex = placeChild(); // 构建链表关系 } // 2. 第二轮遍历 // 2.1 如果老的有多的就需要删除 if (newIdx === newChildren.length) { deleteRemainingChildren(); } // 2.2 如果新的有多的就需要插入操作 // 初次挂载的逻辑也是在这里的 if (oldFiber === null) { for (; newIdx < newChildren.length; newIdx++) { const newFiber = createChild(); placeChild(); } } // 3. 第三轮遍历 需要处理移动的情况 // 3.1 老的维护为一个map const existingChildren = mapRemainingChildren(returnFiber, oldFiber); // 3.2 遍历在里面找 for (; newIdx < newChildren.length; newIdx++) { const newFiber = updateFromMap(); lastPlacedIndex = placeChild(); } // 3.3 如果老的还有就删除 if (shouldTrackSideEffects) { existingChildren.forEach((child) => deleteChild(returnFiber, child)); } } ``` ## schedule 调度 ```js // schedule是一个单独的模块 // lanes 模型的优先级是 react的概念 // lanes优先级 变成事件优先级 // 然后都要变成调度的优先级 给 schedule模块进行调度 ``` ### 最小堆 SchedulerMinHeap [minHeap](img/15.minHeadp.jpeg) ```js // https://github.com/facebook/react/blob/main/packages/scheduler/src/SchedulerMinHeap.js // 使用数组实现最小堆 找到最高优先级的任务 // peek() 查看堆的顶点 // pop() 弹出堆的定点后需要调用siftDown函数向下调整堆 // push() 添加新节点后需要调用siftUp函数向上调整堆 // siftDown() 向下调整堆结构, 保证最小堆 // siftUp() 需要向上调整堆结构, 保证最小堆 // 左子节点索引=(父节点索引+1)2-1 (0+1)2-1=1 // 右子节点索引=左子节点索引+1 // 父节点索引=(子节点索引-1)/2 (1-1)/2=0 ``` ### scheduleCallback [messageChannel](img/16.messageChannel.jpeg) [scheduleCallback](img/17.scheduleCallback.jpeg) [scheduleCallback](img/18.scheduleCallback.jpeg) ```js // 在 react-reconciler中我们会调用 scheduler包中的调度函数 scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root) ); // 之前使用 requestIdleCallback 简单的实现 function unstable_scheduleCallback(callback) { requestIdleCallback(callback); } // 但是存在兼容性问题 而且时间是不确定的 // react和浏览器是合作式的调度 每帧申请5ms的时间片 // react中使用 MessageChannel来模拟 function scheduleCallback(priorityLevel, callback, options) { // 1. 任务的开始时间 let currentTime = getCurrentTime(); // 2. 超时时间 不同的优先级对应不同的超时时间 let timeout; switch (priorityLevel) { default: timeout = NORMAL_PRIORITY_TIMEOUT; break; } // 3. 过期时间 作为优先级的排序依据 let expirationTime = startTime + timeout; const newTask = { id: taskIdCounter++, callback, sortIndex: expirationTime }; // 4. 将任务添加到最小堆数组中 push(taskQueue, newTask); // 5. 开始调度任务 schedulePerformWorkUntilDeadline requestHostCallback(flushWork); } // 用 MessageChannel const channel = new MessageChannel(); const port = channel.port2; channel.port1.onmessage = performWorkUntilDeadline; // 发送消息 执行 flushWork function requestHostCallback() { port.postMessage(null); } // 开始执行任务队列中的任务 function flushWork() { // return workLoop() // 1. 取出堆顶元素 currentTask = peek(taskQueue); while (currentTask !== null) { const callback = currentTask.callback; // 2. 执行任务函数 如果返回了一个函数就需要继续执行 // 这里的cb就是 performConcurrentWorkOnRoot fiber树的构建是比较耗时的 const continuationCallback = callback(didUserCallbackTimeout); if (typeof continuationCallback === "function") { currentTask.callback = continuationCallback; return true; } else { // 任务完成了就弹出这个任务 pop(taskQueue); } } } ``` ### lane 优先级 [lane](img/19.lane.jpeg) ```js // 31个车道 16个优先级 车道就是优先级 是react的概念 // 会转为调度模块中的优先级 // react之前有四个优先级 // 事件 任务 更新 schedule // 现在是三个优先级 // 1.1 react里面的lane优先级 16个优先级 ReactFiberLane react中有很多个任务对应不同的车道 // 1.2. 事件优先级 lanesToEventPriority 将lane变成事件优先级 ReactEventPriorities // 一共4种 事件优先级也是车道 做了归并 // 车道有31个通过 lanesToEventPriority变成event的优先级 const DiscreteEventPriority = SyncLane; // 离散 const ContinuousEventPriority = InputContinuousLane; // 连续 const DefaultEventPriority = DefaultLane; // 默认事件优先级 const IdleEventPriority = IdleLane; // 空闲 // 2. scheduler模块中的 SchedulerPriorities 五个优先级 const ImmediatePriority = 1; const UserBlockingPriority = 2; const NormalPriority = 3; const LowPriority = 4; const IdlePriority = 5; // 优先级是相互独立的 可以相互转化的 // 将lane转成scheduler中的优先级(存在对应关系的) // 调度模块5个优先级 每个优先对应不同的超时时间 ``` ### 更新优先级 ```js // 每个更新会有一个优先级 每个任务存在优先级 一些低优先级的任务可能会被跳过 // 1. 在处理化更新队列的时候 在queue中添加一些属性来记录 function initializeUpdateQueue(fiber) { const queue = { baseState: fiber.memoizedState, // 跳过前的state firstBaseUpdate: null, // 单向链表 第一个被跳过的更新 lastBaseUpdate: null, // 跳过的链表尾 之后后将两个链表做合并 shared: { // shared是一个循环链表 pending: null, lanes: NoLanes, }, }; fiber.updateQueue = queue; } // 2. 在处理更新的时候会判断优先级 function processUpdateQueue() { // 2.1 要将 pending的循环链表拆开和上次跳过的链表合并 const queue = workInProgress.updateQueue; // 上次跳过的老的更新链表头 let firstBaseUpdate = queue.firstBaseUpdate; let lastBaseUpdate = queue.lastBaseUpdate; // 新的更新的循环链表 let pendingQueue = queue.shared.pending; // 如果有新的跟新 if (pendingQueue !== null) { queue.shared.pending = null; const lastPendingUpdate = pendingQueue; // 第一个更新 const firstPendingUpdate = lastPendingUpdate.next; // 变成单向链表 lastPendingUpdate.next = null; // 将两个链表合并 if (lastBaseUpdate === null) { // 没有老链表 firstBaseUpdate = firstPendingUpdate; } else { // 有老的链表就将老的链表尾指向新的链表头 两个链表就变成了一个 lastBaseUpdate.next = firstPendingUpdate; } // 指向新的链表尾 两个链表就合并为一个了 lastBaseUpdate = lastPendingUpdate; } // 2.2 链表不为空 有更新 进行跟新 if (firstBaseUpdate !== null) { // 上次跳过跟新的state状态 let newState = queue.baseState; let newFirstBaseUpdate = null; let newLastBaseUpdate = null; let update = firstBaseUpdate; do { // 2.3 判断是否要跳过更新 if (!isSubsetOfLanes(renderLanes, update.lane)) { // 2.3.1 优先级不够就要跳过更新 const clone = {}; if (newLastBaseUpdate === null) { // 都指向第一次跳过的更新 newFirstBaseUpdate = newLastBaseUpdate = clone; // 跳过的时候的状态值 newBaseState = newState; } else { newLastBaseUpdate = newLastBaseUpdate.next = clone; } } else { // 2.3.2 优先级够的时候就要计算新的状态 // 但是如果已经有跳过的更新了 还是要加到链表上的 保证执行的顺序 if (newLastBaseUpdate !== null) { const clone = {}; newLastBaseUpdate = newLastBaseUpdate.next = clone; } // 2.4 计算新的状态 newState = getStateFromUpdate(); } update = update.next; if (update === null) { break; } } while (true); // 赋值 if (newLastBaseUpdate === null) newBaseState = newState; workInProgress.memoizedState = newState; } } ``` ### 渲染 [scheduleUpdateOnFiber](img/20.scheduleUpdateOnFiber.jpeg) ```js function updateContainer() { // 1. 在 updateContainer 的时候获取lane const lane = requestUpdateLane(current); // 2. 在创建更新的时候加入lane const update = { lane, payload: null, next: null } const update = createUpdate(lane); // 3. 入队更新 使用并发的模式 添加到concurrentQueues 数组中 const root = enqueueUpdate(current, update, lane); // 4. 在调度的时候也要加入lane scheduleUpdateOnFiber(root, current, lane, eventTime); } function markRootUpdated(root, lane, eventTime) { // 1. 标记根节点上的更新 根节点上有一个更新 // root.pendingLanes |= updateLane; markRootUpdated(root, lane, eventTime); ensureRootIsScheduled(); } function ensureRootIsScheduled() { // 2. 回去最高优先级的 const nextLanes = getNextLanes(); const newCallbackPriority = getHighestPriorityLane(nextLanes); let newCallbackNode; if (includesSyncLane()) { // 同步 更新的时候就到了这里 // 1. 添加到同步队列中 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root)); // 2. flushSyncCallbacks 刷新清空同步队列 Scheduler_scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks); } else { // let schedulerPriorityLevel; // 3. 将lane变成事件优先级 let eventPriority = lanesToEventPriority(nextLanes); switch (eventPriority) { // 4. 将事件优先级变成调度优先级 case DefaultEventPriority: schedulerPriorityLevel = NormalSchedulerPriority; break; } // 开始调度 返回的是一个newTask newCallbackNode = Scheduler_scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root) ); } root.callbackNode = newCallbackNode; } ``` #### 并发渲染 ```js // 在跟上执行并发渲染 function performConcurrentWorkOnRoot() { // 获取根节点上的任务 在 ensureRootIsScheduled中赋值的 const originalCallbackNode = root.callbackNode; // 拿到最高lanes let lanes = getNextLanes(); // 判断是否要时间分片 let exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes); // 说明任务还没有完成 提交阶段会清空 if (root.callbackNode === originalCallbackNode) { // 下次继续干 return performConcurrentWorkOnRoot.bind(null, root); } } // 初次渲染是同步的 renderRootSync // 在 createRoot 的时候我们传递一个参数 const root = createRoot(document.getElementById("root"), { // 可以传递参数 这是一个开关 unstable_concurrentUpdatesByDefault: true, }); // 这样 fiber.mode |= ConcurrentUpdatesByDefaultMode; // 是否需要时间分片 const shouldTimeSlice = // 没有阻塞的车道 默认的任务是阻塞的 可以通过开关来控制 !includesBlockingLane(root, lanes) && // 不包含过期的 !includesExpiredLane(root, lanes) && // disableSchedulerTimeoutInWorkLoop = false (disableSchedulerTimeoutInWorkLoop || !didTimeout); // 并发 function renderRootConcurrent(root, lane) { workLoopConcurrent(); // 还没有完成表示要继续工作 if (workInProgress !== null) { return RootInProgress; } else { // 在complete阶段完成之后修改为5 表示完成了 return workInProgressRootExitStatus; } } function workLoopConcurrent() { while (workInProgress !== null && !shouldYield()) { // 测试并发渲染 sleep(6); performUnitOfWork(workInProgress); } } // 完成阶段 function completeUnitOfWork() { // do {}while // 这里就是fiber树构建完成了 修改状态 workInProgressRootExitStatus = RootCompleted; } // 在提交阶段 function commitRoot() { root.callbackNode = null; } ``` #### 同步渲染 ```js function performSyncWorkOnRoot() { let lanes = getNextLanes(root, NoLanes); renderRootSync(root, lanes); } function renderRootSync(root, lanes) { // 1. 渲染优先级不一样就准备fiber栈 // 本次渲染的lanes workInProgressRootRenderLanes = renderLanes = lanes; if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { prepareFreshStack(root, lanes); } workLoop(); } // lanes传下去 在处理更新队列的时候就可以获取到 优先级了 // workLoop => performUnitOfWork => beginWork // => updateHost() => processUpdateQueue() ``` #### 更新阶段 ```js // 点击按钮触发更新 在合成事件中 会派发 dispatchEvent function dispatchDiscreteEvent() { const previousPriority = getCurrentUpdatePriority(); // 设置当前的优先级 setCurrentUpdatePriority(DiscreteEventPriority); dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent); setCurrentUpdatePriority(previousPriority); } // 这个时候就是执行同步的操作 // 1. 添加到同步队列中 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root)); // 2. flushSyncCallbacks 刷新清空同步队列 Scheduler_scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks); ``` ### 批量更新 ```js // 如果任务的优先级一样就直接return function ensureRootIsScheduled() { const newCallbackPriority = getHighestPriorityLane(nextLanes); const existingCallbackPriority = root.callbackPriority; // 优先级一样就直接return了 if (existingCallbackPriority === newCallbackPriority) { return; } root.callbackPriority = newCallbackPriority; } ``` ### 高优任务打断低优先级任务 ```js function ensureRootIsScheduled() { if (existingCallbackNode !== null) { // 取消任务 高优先级的任务会打断低优先级的任务 但是这个任务还是需要执行的 Scheduler_cancelCallback(existingCallbackNode); } } ``` ### 饥饿问题 ```js // 在react中处理的 scheduler模块是不负责处理的 // 低优任务一只被打断 到过期了 异步马上变成同步的 // markStarvedLanesAsExpired() ```