React源码——总体流程

455 阅读3分钟

架构分层

  1. scheduler 调度
  2. reconciler 协调
  3. render 渲染

scheduler:event loops —— 宏任务方式执行任务

  1. scheduleCallback:方法加入到调度队列中,每次会对队列重新排序
  2. requestHostTimeout(setTimeout)-> advanceTimers:将 timer 中过期任务放到 task 中
  3. requestHostCallback(MessageChannel 或 setTimeout) -> flushWork -> workLoop: 执行 taskQueue 中优先级最高的任务

reconciler

  1. beginWork —— “递”过程: 首先从 rootFiber 开始向下深度优先遍历。为遍历到的每个 Fiber 节点调用 beginWork 方法。 该方法会根据传入的 Fiber 节点创建子 Fiber 节点,并将这两个 Fiber 节点连接起来。 当遍历到叶子节点(即没有子组件的组件)时就会进入“归”阶段。
  2. completeWork —— “归”过程: 在“归”阶段会调用 completeWork 处理 Fiber 节点。 当某个 Fiber 节点执行完 completeWork,如果其存在兄弟 Fiber 节点(即 fiber.sibling !== null),会进入其兄弟 Fiber 的“递”阶段。 如果不存在兄弟 Fiber,会进入父级 Fiber 的“归”阶段。 “递”和“归”阶段会交错执行直到“归”到 rootFiber。至此,render 阶段的工作就结束了。

beginWork

  1. reconcileChildren —— diff 算法

completeUnitOfWork

  1. completeWork
    • function 组件:不处理
    • class 组件
    • HostComponent:创建 dom 节点,将子节点插入到该 dom 中;
  2. effect 链表:将要更新的 fiber 连成链表;rootFiber.firstEffect = 指向第一个要更新的 fiber

render:React 中 commitRoot 方法

commitBeforeMutationEffects

  1. commitBeforeMutationLifeCycles
    • class:执行 getSnaphotBeforeUpdate 生命周期方法;获取当前 DOM 信息,提供给 componentDidUpdate 使用
    • HostRoot:清空节点内容
  2. 判断当前 Fiber 是否存在 useEffect;存在,开启 useEffect 调度队列任务

commitMutationEffects:针对真实 dom 进行操作

  1. commitDetachRef:ref = null 重置
  2. commitPlacement:添加元素
  3. commitWork
    • function:执行 useLayoutEffect 的 destroy 方法
    • HostComponent:commitUpdate —— 对 html 标签添加 value、attribute、style

commitLayoutEffects

  1. commitLifeCycle
    • function:执行 useLayoutEffect 的 createfangf;将 useEffect 放到数组中,等待执行
    • class:执行 componentDidMount 或 componentDidUpdate
  2. commitAttachRef:ref = dom 实例

其他方法

  1. ensureRootIsScheduled:将满足条件的方法放到调度队列中
  2. flushSyncCallbackQueue: 执行 syncQueue 中方法
  3. flushPassiveEffects:当新的调度任务开始时,是否上次的 useEffect 方法还没有执行,先执行掉
  4. processUpdateQueue:将 pending 的值放到 baseState 上,firstBaseUpdate/lastBaseUpdate
  5. lanes 优先级;低优先级的占到位数越多

react: setState, hooks -> dispatchAction

setState 是同步的,调用 scheduleUpdateOnFiber -> ensureRootIsScheduled,判断当前 调度队列中存在了,就不会再次入栈;最后调用 processUpdateQueue 得到新的 state 和 memoizedState;

class 生命周期

  1. render
  2. constructor
  3. componentDidMount
  4. componentDidUpdate
  5. componentWillUnmount
  6. shouldComponentUpdate
  7. getDerivedStateFromProps: pure function,返回参数;返回 null,不更新 state 中数据
  8. getSnapshotBeforeUpdate: 在组件更新之前,捕获 DOM 信息

hooks

  • Fiber.memorized: Function Component 对应 Fiber 保存的 hooks 链表;指向第一个 hook 地址
  • hook.memorized: Hooks 链表中保存单一 hook 对应的数据
  • 不同类型 hook 的 memoizedState 保存不同类型数据,具体如下:
    1. useState:对于 const [state, updateState] = useState(initialState),memoizedState 保存 state 的值
    2. useReducer:对于 const [state, dispatch] = useReducer(reducer, {}),memoizedState 保存 state 的值
    3. useEffect:memoizedState 保存包含 useEffect 回调函数、依赖项等的链表数据结构 effect,你可以在这里 (opens new window)看到 effect 的创建过程。effect 链表同时会保存在 fiber.updateQueue 中
    4. useRef:对于 useRef(1),memoizedState 保存{current: 1}
    5. useMemo:对于 useMemo(callback, [depA]),memoizedState 保存[callback(), depA]
    6. useCallback:对于 useCallback(callback, [depA]),memoizedState 保存[callback, depA]。与 useMemo 的区别是,useCallback 保存的是 callback 函数本身,而 useMemo 保存的是 callback 函数的执行结果
    7. 有些 hook 是没有 memoizedState 的,比如:useContext
  • updateWorkInProgressHook: 找到对应的 hook,根据 update 计算该 hook 的新 state 并返回
function dispatchAction(fiber, queue, action) {
  ...
  // 执行 nextChildren = Component(props, secondArg); 时,触发了useState方法
  if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {

    didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
  } else {  // 当前Fiber处理NoLanes状态,此时触发useState方法,发现更新值与之前值一样时,取消此次更新
    if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
      ...
         if (objectIs(eagerState, currentState)) {

         return;
         }
      ...
    }
    ...
    scheduleUpdateOnFiber(fiber, lane, eventTime);
  }
}

setState流程图01 setState流程图02

参考资料

  1. React 进阶实践指南
  2. React技术揭秘