react源码解读

79 阅读28分钟
import React, { useState, useEffect } from 'react';
import * as ReactDOM from 'react-dom/client';

const Counter = () => {
  const [count, setCount] = useState(0);
  const handleIncrement = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    console.log("effect...start", count);
    return () => {
      console.log("effect...end", count);
    };
  }, [count]);

  return (
    <button onClick={handleIncrement}>{count}</button>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);

createRoot

该函数只做了一件事:创建两个fiber:FiberRootNode(整个应用的根)FiberNode(root对应的fiber)

调用createFiberRoot创建root,该fiber的构造函数为FiberRootNode

function FiberRootNode(
  containerInfo,           // 容器信息,例如 DOM 节点
  tag,                     // 标签,表示根节点类型,如 ConcurrentRoot 或 LegacyRoot
  hydrate,                 // 是否是服务端渲染的 hydrate 模式
  identifierPrefix,        // 标识符前缀,主要用于生成 DOM 元素的唯一标识
  onRecoverableError       // 当遇到可恢复错误时的回调函数
) {
  this.tag = tag;                             // 存储根节点类型, **ConcurrentRoot: 1**
  this.containerInfo = containerInfo;         // 存储与根节点关联的容器信息(DOM 或其他容器)
  this.pendingChildren = null;                // 用于存储挂起的子节点
  this.current = null;                        // 指向当前的 Fiber 树(workInProgress Fiber)
  this.pingCache = null;                      // 用于存储在 ping 时恢复的工作
  this.finishedWork = null;                   // 存储render事情的Fiber树,在commit阶段会用到
  this.timeoutHandle = noTimeout;             // 超时处理的句柄,默认值为 noTimeout
  this.context = null;                        // 存储与根节点关联的上下文信息
  this.pendingContext = null;                 // 挂起的上下文信息
  this.callbackNode = null;                   // 调度中的回调节点
  this.callbackPriority = NoLane;             // 当前回调的优先级,默认为 NoLane
  this.eventTimes = createLaneMap(NoLanes);   // 存储每条车道的事件时间
  this.expirationTimes = createLaneMap(NoTimestamp); // 每条车道的过期时间

  // 调度相关的 lane 状态,用于跟踪 Fiber 树的更新优先级
  this.pendingLanes = NoLanes;                // 挂起的 lanes,表示哪些工作正在等待执行
  this.suspendedLanes = NoLanes;              // 被挂起的 lanes
  this.pingedLanes = NoLanes;                 // 已被 ping 的 lanes,表示需要被唤醒的任务
  this.expiredLanes = NoLanes;                // 已过期的 lanes,需要尽快执行
  this.mutableReadLanes = NoLanes;            // 进行可变读取的 lanes
  this.finishedLanes = NoLanes;               // 已完成的 lanes,表示已处理完的更新

  this.entangledLanes = NoLanes;              // 表示 lanes 被纠缠(entangled),影响优先级调度
  this.entanglements = createLaneMap(NoLanes); // 用于记录 lanes 的纠缠关系
  ...
}

调用createHostRootFiber创建根fiber,该fiber的构造函数是FiberNode

// 定义 FiberNode 类的构造函数,用于创建 React 的 Fiber 节点
function FiberNode(
  tag: WorkTag,           // 表示 Fiber 节点的类型(如 FunctionComponent、ClassComponent)
  pendingProps: mixed,    // 此 Fiber 对应组件传入的最新属性(props)
  key: null | string,     // 用于列表渲染中标识唯一性的 key
  mode: TypeOfMode        // 当前 Fiber 节点的模式,如 ConcurrentMode 或 NoMode
) {
  // 组件实例相关的字段
  this.tag = tag;                     // 节点的类型(如 HostComponent, ClassComponent 等)
  this.key = key;                     // key 属性,用于优化列表渲染
  this.elementType = null;            // JSX 元素的类型(如函数组件的函数本身)
  this.type = null;                   // 组件的具体类型,可能是类或函数(与 elementType 类似)
  this.stateNode = null;              // 与此 Fiber 节点关联的实际 DOM 或类实例

  // Fiber 树结构中的关系
  this.return = null;                 // 指向父 Fiber
  this.child = null;                  // 指向子 Fiber
  this.sibling = null;                // 指向兄弟 Fiber
  this.index = 0;                     // 该 Fiber 在父节点中的位置索引

  this.ref = null;                    // 保存对 DOM 或类实例的引用,便于 ref 回调函数

  // 与更新相关的字段
  this.pendingProps = pendingProps;   // 最新的 props
  this.memoizedProps = null;          // 已保存的上一次渲染的 props
  this.updateQueue = null;            // 更新队列,包含待处理的更新
  this.memoizedState = null;          // 已保存的上一次渲染的 state
  this.dependencies = null;           // 与此 Fiber 相关的上下文依赖

  this.mode = mode;                   // 当前 Fiber 所处的模式,如同步或异步模式

  // 副作用相关的字段
  this.flags = NoFlags;               // 当前节点的副作用标志位,指示哪些操作需要执行(如更新、删除)
  this.subtreeFlags = NoFlags;        // 子树的副作用标志位,指示整个子树中是否有需要处理的副作用
  this.deletions = null;              // 需要删除的 Fiber 子节点数组

  // 优先级调度相关的字段
  this.lanes = NoLanes;               // 当前 Fiber 节点的更新优先级
  this.childLanes = NoLanes;          // 子树的更新优先级

  // 双缓存(alternate)相关
  this.alternate = null;              // 用于指向当前 Fiber 的替代版本(即双缓存机制中的另一份 Fiber)

这两个fiber互有联系,以下称FiberRootNode为root,FiberNode为rootFiber。

root.current = rootFiber;
rootFiber.stateNode = root;

image.png

// root的dom上有属性指向其对应的rootFiber
export function markContainerAsRoot(hostRoot: Fiber, node: Container): void {
  node[internalContainerInstanceKey] = hostRoot;
}

image.png

render

updateContainer

image.png element是传给render函数的组件根节点,是个Symbol(react.element),将创建的更新对象添加到根fiber的updateQueue中。

ensureRootIsScheduled

// 使用确定的调度优先级调度回调任务
// `scheduleCallback` 是一个函数,用于根据优先级调度回调任务
// `performConcurrentWorkOnRoot` 是实际要执行的工作,绑定了根节点 `root`,在任务调度时会调用
newCallbackNode = scheduleCallback(
  schedulerPriorityLevel,
  performConcurrentWorkOnRoot.bind(null, root),
);

以上操作就是注册调度任务

Scheduler_scheduleCallback(priorityLevel, callback)

Scheduler

最小堆

调度器内部存储任务的两个队列,是个数组。逻辑上是最小堆,也就是二叉树。

// Tasks are stored on a min heap
var taskQueue = [];
var timerQueue = [];

最小堆,是一种经过排序的完全二叉树

        1
       / \
      3   5
     / \  /
    6  8 7

每个父节点比子节点都要小,在此对应着父节点的过期时间比子节点都小,所以说第一个节点任务,过期时间最小,每次从该任务队列中取出第一个,就是取出过期时间最紧急的任务

// 消息队列的实现结构,类似这样
[1, 3, 5, 6, 8, 7]
Parent(i) = Math.floor((i - 1) / 2)
LeftChild(i) = 2 * i + 1
RightChild(i) = 2 * i + 2

unstable_scheduleCallback

  1. 创建任务,任务主要包括过期时间和回调,根据时间判断是同步任务还是异步任务,分别将其推入taskQueue、timerQueue
  2. 判断是否有任务阻塞,没有就启动任务刷新flushWork
function unstable_scheduleCallback(priorityLevel, callback, options) {
  // 获取当前时间
  var currentTime = getCurrentTime();

  var startTime;
  if (typeof options === 'object' && options !== null) {
    // 从 options 中提取延迟时间
    var delay = options.delay;
    if (typeof delay === 'number' && delay > 0) {
      // 如果延迟时间是正数,则设置任务的开始时间为当前时间加上延迟时间
      startTime = currentTime + delay;
    } else {
      // 否则,任务的开始时间为当前时间
      startTime = currentTime;
    }
  } else {
    // 如果 options 不是对象或为 null,任务的开始时间为当前时间
    startTime = currentTime;
  }

  var timeout;
  // 根据优先级选择任务超时时间
  switch (priorityLevel) {
    case ImmediatePriority:
      timeout = IMMEDIATE_PRIORITY_TIMEOUT;
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT;
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT;
      break;
    case NormalPriority:
    default:
      timeout = NORMAL_PRIORITY_TIMEOUT;
      break;
  }

  // 计算任务的过期时间
  var expirationTime = startTime + timeout;

  // 创建新的任务对象
  var newTask = {
    id: taskIdCounter++, // 任务的唯一标识符
    callback, // 任务执行的回调函数
    priorityLevel, // 任务的优先级
    startTime, // 任务的开始时间
    expirationTime, // 任务的过期时间
    sortIndex: -1, // 用于排序的索引
  };

  if (enableProfiling) {
    newTask.isQueued = false; // 如果启用了性能分析器,将任务标记为未排队
  }

  if (startTime > currentTime) {
    // 如果任务是延迟任务
    newTask.sortIndex = startTime; // 设置排序索引为开始时间
    push(timerQueue, newTask); // 将任务推入定时队列

    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      // 如果任务队列为空,且这是定时队列中最早的任务
      if (isHostTimeoutScheduled) {
        // 如果已经安排了超时,取消现有的超时
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      }
      // 安排超时
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    // 如果任务不是延迟任务
    newTask.sortIndex = expirationTime; // 设置排序索引为过期时间
    push(taskQueue, newTask); // 将任务推入任务队列

    if (enableProfiling) {
      markTaskStart(newTask, currentTime); // 如果启用了性能分析器,标记任务开始时间
      newTask.isQueued = true; // 标记任务为已排队
    }

    // 如果需要,安排主机回调
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback(flushWork);
    }
  }

  // 返回新创建的任务
  return newTask;
}

注意以上操作,最终会执行回调 flushWork

requestHostCallback

function requestHostCallback(callback) {
  // 把传入的回调函数存储到全局变量 `scheduledHostCallback` 中
  scheduledHostCallback = callback;

  // 如果消息循环还没有运行
  if (!isMessageLoopRunning) {
    // 设置 `isMessageLoopRunning` 为 `true`,表示消息循环正在运行
    isMessageLoopRunning = true;

    // 调度任务的执行,使用之前定义的 `schedulePerformWorkUntilDeadline`
    // 该函数会根据不同的环境选择最优的方式来执行任务
    schedulePerformWorkUntilDeadline();
  }
}

scheduledHostCallback:这是一个全局变量,用于存储传入的回调函数。这个回调函数将在后续的调度过程中被调用

schedulePerformWorkUntilDeadline

触发该函数,会在下一个事件循环中执行回调

let schedulePerformWorkUntilDeadline; // 定义调度工作执行的函数

// 检查是否存在 `localSetImmediate`,这是 Node.js 和老版本的 IE 中的 API
if (typeof localSetImmediate === 'function') {
  // Node.js 和旧版 IE
  // 这里有几个原因我们优先使用 setImmediate。
  // 1. 与 MessageChannel 不同,setImmediate 不会阻止 Node.js 进程退出。
  // 即使这是 Scheduler 的 DOM 版本,你可能会在 Node.js 15+ 版本和 jsdom 的混合环境中运行。
  // 相关 issue: https://github.com/facebook/react/issues/20756
  // 
  // 2. setImmediate 会比 MessageChannel 更早运行任务,这是我们需要的语义。
  // 如果其他浏览器实现了 setImmediate,使用它会更好。
  // 3. 但是无论如何,这两者都不如原生的调度 API 优秀(如果原生调度 API 存在的话)。
  
  // 定义 `schedulePerformWorkUntilDeadline`,使用 `localSetImmediate` 调度任务
  schedulePerformWorkUntilDeadline = () => {
    localSetImmediate(performWorkUntilDeadline); // 在下一次事件循环中执行 `performWorkUntilDeadline`
  };
  
// 如果 `MessageChannel` 存在
} else if (typeof MessageChannel !== 'undefined') {
  // DOM 和 Worker 环境下
  // 我们更喜欢使用 MessageChannel,因为 setTimeout 有 4ms 的延迟(称为 "clamping")。
  
  const channel = new MessageChannel(); // 创建一个新的 MessageChannel 对象
  const port = channel.port2; // 使用 port2 作为发送端口
  channel.port1.onmessage = performWorkUntilDeadline; // 当 port1 接收到消息时,执行 `performWorkUntilDeadline`
  
  // 定义 `schedulePerformWorkUntilDeadline`,使用 `port.postMessage` 来调度任务
  schedulePerformWorkUntilDeadline = () => {
    console.log("port.postMessage..."); // 输出调试信息
    port.postMessage(null); // 使用 `postMessage` 触发消息事件,进而触发 `performWorkUntilDeadline`
  };
  
// 如果没有 `setImmediate` 或 `MessageChannel`
} else {
  // 这种情况通常只在非浏览器环境下会发生。
  // 定义 `schedulePerformWorkUntilDeadline`,使用 `localSetTimeout` 调度任务
  schedulePerformWorkUntilDeadline = () => {
    localSetTimeout(performWorkUntilDeadline, 0); // 使用 `setTimeout` 来尽快执行任务,延迟为 0ms
  };
}

performWorkUntilDeadline

该函数会检查scheduledHostCallback是否有返回值,如果有说明taskQueue不为空,只是时间切片满了,余下的taskQueue任务会在下一次事件循环执行,因为触发了 schedulePerformWorkUntilDeadline

// 执行调度的任务直到时间截止
const performWorkUntilDeadline = () => {
  // 如果存在已调度的任务回调,则开始执行
  if (scheduledHostCallback !== null) {
    // 获取当前时间
    const currentTime = getCurrentTime();
    // 记录任务开始执行的时间
    startTime = currentTime;
    // 假设当前有足够的时间来执行任务
    const hasTimeRemaining = true;

    // 定义一个标志,判断是否还有更多工作需要执行
    let hasMoreWork = true;

    // 尝试执行调度的任务回调
    try {
      // 执行回调函数并判断是否还有剩余任务需要执行
      hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
    } finally {
      // 如果还有剩余工作需要执行
      if (hasMoreWork) {
        // 调度下一次任务执行
        schedulePerformWorkUntilDeadline();
      } else {
        // 如果没有剩余任务需要执行,停止消息循环并清空调度回调
        isMessageLoopRunning = false;
        scheduledHostCallback = null;
      }
    }
  } else {
    // 如果没有调度的任务回调,停止消息循环
    isMessageLoopRunning = false;
  }

  // 重置 needsPaint 标志,表示浏览器不需要立即执行绘制操作
  needsPaint = false;
};

flushWork

每次执行的scheduledHostCallback,其实就是在requestHostCallback中注册的flushWork,该函数用来执行 workLoop

function flushWork(hasTimeRemaining, initialTime) {
  // 如果启用了性能分析
  if (enableProfiling) {
    // 记录调度器未被挂起的时间
    markSchedulerUnsuspended(initialTime);
  }

  // 我们需要在下一次工作被调度时一个主机回调
  isHostCallbackScheduled = false;
  if (isHostTimeoutScheduled) {
    // 如果之前调度了一个超时但现在不再需要,取消它
    isHostTimeoutScheduled = false;
    cancelHostTimeout();
  }

  // 标记开始执行工作
  isPerformingWork = true;
  // 保存当前的优先级级别
  const previousPriorityLevel = currentPriorityLevel;

  try {
    if (enableProfiling) {
      try {
        // 执行工作循环
        return workLoop(hasTimeRemaining, initialTime);
      } catch (error) {
        // 如果工作循环抛出错误
        if (currentTask !== null) {
          const currentTime = getCurrentTime();
          // 记录任务出错的时间
          markTaskErrored(currentTask, currentTime);
          currentTask.isQueued = false; // 标记任务未排队
        }
        // 重新抛出错误
        throw error;
      }
    } else {
      // 在生产环境中没有错误捕获
      return workLoop(hasTimeRemaining, initialTime);
    }
  } finally {
    // 清理状态
    currentTask = null; // 重置当前任务
    currentPriorityLevel = previousPriorityLevel; // 恢复之前的优先级级别
    isPerformingWork = false; // 标记工作执行完毕

    if (enableProfiling) {
      const currentTime = getCurrentTime();
      // 记录调度器被挂起的时间
      markSchedulerSuspended(currentTime);
    }
  }
}

workLoop

该函数主要用来执行任务队列中的回调,这里涉及到 时间切片,主要计算从performWorkUntilDeadline到现在的时间差,是否超过了 frameInterval,有个计算公式,根据浏览器的帧频率fps计算,若fps不存在默认 5ms

function workLoop(hasTimeRemaining, initialTime) {
  // 初始化当前时间为传入的时间
  let currentTime = initialTime;
  
  // 更新定时器(处理那些到期的定时器任务)
  advanceTimers(currentTime);
  
  // 获取任务队列中的第一个任务
  currentTask = peek(taskQueue);
  
  // 进入任务循环,循环条件:
  // - 任务队列中存在任务
  // - 调试模式下,调度器未被暂停
  while (
    currentTask !== null &&
    !(enableSchedulerDebugging && isSchedulerPaused)
  ) {
    // 判断当前任务是否超过了它的到期时间,并且是否应该放弃继续执行
    if (
      currentTask.expirationTime > currentTime &&
      (!hasTimeRemaining || shouldYieldToHost())
    ) {
      // 当前任务还没到期,并且时间不够或应该让出执行权,退出循环
      break;
    }

    // 获取当前任务的回调函数
    const callback = currentTask.callback;
    
    // 如果当前任务存在回调函数
    if (typeof callback === 'function') {
      // 将当前任务的回调函数清空,避免重复执行
      currentTask.callback = null;

      // 记录当前的优先级
      currentPriorityLevel = currentTask.priorityLevel;

      // 检查任务是否超时(到期时间是否小于等于当前时间)
      const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;

      // 如果启用了性能分析,标记任务开始执行
      if (enableProfiling) {
        markTaskRun(currentTask, currentTime);
      }

      // 执行当前任务的回调函数,传入是否超时的标志
      const continuationCallback = callback(didUserCallbackTimeout);

      // 更新当前时间
      currentTime = getCurrentTime();

      // 如果回调返回的是一个函数,表示任务未完全结束,需要继续执行
      if (typeof continuationCallback === 'function') {
        // 将任务的回调更新为继续回调
        currentTask.callback = continuationCallback;
        
        // 如果启用了性能分析,标记任务被挂起
        if (enableProfiling) {
          markTaskYield(currentTask, currentTime);
        }
      } else {
        // 否则,任务已完成,执行相应的清理操作
        if (enableProfiling) {
          markTaskCompleted(currentTask, currentTime);
          currentTask.isQueued = false;
        }

        // 如果当前任务仍然是队列中的第一个任务,则将其移出队列
        if (currentTask === peek(taskQueue)) {
          pop(taskQueue);
        }
      }
      
      // 再次更新定时器
      advanceTimers(currentTime);
    } else {
      // 如果当前任务没有回调函数,直接将其移出任务队列
      pop(taskQueue);
    }

    // 获取队列中的下一个任务
    currentTask = peek(taskQueue);
  }

  // 判断是否还有更多的任务需要执行
  if (currentTask !== null) {
    // 如果有更多任务,返回 true
    return true;
  } else {
    // 如果没有更多任务,检查定时器队列中是否有任务
    const firstTimer = peek(timerQueue);
    
    // 如果有定时器任务,设置超时请求
    if (firstTimer !== null) {
      requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
    }
    
    // 返回 false,表示没有更多任务需要处理
    return false;
  }
}

performConcurrentWorkOnRoot

主要处理 React 16/17 以及之后的并发渲染逻辑

function performConcurrentWorkOnRoot(root, didTimeout) {
   ...
  // 在某些情况下禁用时间切片:如果工作已经 CPU 绑定了太久(“过期”工作,以防止饿死),或者我们处于同步更新默认模式
  // TODO: 我们仅仅防御性地检查 `didTimeout`,以考虑 Scheduler 中我们仍在调查的 bug。修复 Scheduler 中的 bug 后,可以移除此检查,因为我们自己跟踪过期
  const shouldTimeSlice =
    !includesBlockingLane(root, lanes) &&
    !includesExpiredLane(root, lanes) &&
    (disableSchedulerTimeoutInWorkLoop || !didTimeout);
  let exitStatus = shouldTimeSlice
    ? renderRootConcurrent(root, lanes) // 如果支持时间切片,则使用并发模式渲染根节点
    : renderRootSync(root, lanes); // 否则,使用同步模式渲染根节点

    if (exitStatus === RootDidNotComplete) {
      // 渲染在没有完成树的情况下回溯
      // 这种情况仅在并发渲染期间发生,而不是离散或同步更新
      markRootSuspended(root, lanes);
    } else {
      // 渲染完成

      // 检查此渲染是否可能让出给并发事件
      // 如果是这样,确认任何新渲染的存储器是否一致
      // TODO: 即使并发渲染可能从未让出主线程,如果它足够快,或者它过期了,也可以跳过一致性检查
      const renderWasConcurrent = !includesBlockingLane(root, lanes);
      const finishedWork = root.current.alternate;
      if (
        renderWasConcurrent &&
        !isRenderConsistentWithExternalStores(finishedWork)
      ) {
        // 在交错事件中存储器被更改。再渲染一次,使用同步模式来阻止进一步的变更
        exitStatus = renderRootSync(root, lanes);

        // 再次检查是否出现了错误
        if (exitStatus === RootErrored) {
          const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
          if (errorRetryLanes !== NoLanes) {
            lanes = errorRetryLanes;
            exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
            // 假设树现在是一致的,因为我们没有让出给任何并发事件
          }
        }
        if (exitStatus === RootFatalErrored) {
          const fatalError = workInProgressRootFatalError;
          prepareFreshStack(root, NoLanes);
          markRootSuspended(root, lanes);
          ensureRootIsScheduled(root, now());
          throw fatalError;
        }
      }

      // 现在我们有了一致的树。下一步是提交它,或者如果有挂起,则在超时后提交
      root.finishedWork = finishedWork;
      root.finishedLanes = lanes;
      finishConcurrentRender(root, exitStatus, lanes);
    }
  }

  // 确保根节点被调度
  ensureRootIsScheduled(root, now());
  // 如果当前执行的任务节点与原始任务节点相同,需要返回一个继续执行的任务
  if (root.callbackNode === originalCallbackNode) {
    // 继续执行当前任务
    return performConcurrentWorkOnRoot.bind(null, root);
  }
  // 如果任务节点已更改或不再存在,则返回 null
  return null;
}

渲染模式

renderRootSync

createWorkInProgress

创建新的 workInProgress(render过程最重要的tree)。该函数是 创建fiber树的开始,首次使用createFiber,先创建根fiber,其实根fiber在 createRoot 时候已经创建了,又创建一个新的根fiber,将原有根fiber属性复制过来

/**
 * 该函数用于创建当前 Fiber 节点的 alternate(备用 fiber 节点),
 * 用于在新的渲染周期中进行工作。
 *
 * @param current 当前正在工作的 Fiber 节点。
 * @param pendingProps 传入的新的待处理属性。
 * @returns 返回一个新的或者复用的 Fiber 节点。
 */
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
  // 检查当前 Fiber 节点是否已经有一个备用节点
  let workInProgress = current.alternate;

  if (workInProgress === null) {
    // 如果没有备用 Fiber 节点,创建一个新的
    // 使用双缓冲技术来减少对象分配,保证最多有两份 Fiber 树
    // 懒加载创建 alternate 以避免不必要的内存分配
    // 这样当节点不更新时可以回收内存
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );

    // 复制现有 Fiber 节点的一些属性到备用节点上
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;

    // 互相引用 alternate,形成双向链接
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    // 如果已经有一个备用 Fiber 节点,则复用它
    workInProgress.pendingProps = pendingProps;

    // 更新 type,Blocks 类型组件会存储数据在 type 上
    workInProgress.type = current.type;

    // 重置 effect 标记,因为已经有 alternate 节点
    workInProgress.flags = NoFlags;

    // 清除无效的子树效果
    workInProgress.subtreeFlags = NoFlags;
    workInProgress.deletions = null;

    // 如果启用了 Profiler Timer,则重置时间相关的属性
    if (enableProfilerTimer) {
      workInProgress.actualDuration = 0;
      workInProgress.actualStartTime = -1;
    }
  }

  // 重置所有非静态的效果标记
  workInProgress.flags = current.flags & StaticMask;

  // 复制 lanes 和 childLanes 用于调度优先级
  workInProgress.childLanes = current.childLanes;
  workInProgress.lanes = current.lanes;

  // 复制 Fiber 节点的子节点、已记忆的属性和状态
  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;

  // 克隆依赖关系对象,防止渲染阶段修改时与当前 Fiber 共享
  const currentDependencies = current.dependencies;
  workInProgress.dependencies =
    currentDependencies === null
      ? null
      : {
        lanes: currentDependencies.lanes,
        firstContext: currentDependencies.firstContext,
      };

  // 这些字段在父组件的协调过程中会被覆盖
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;

  // 如果启用了 Profiler Timer,复制一些基础持续时间数据
  if (enableProfilerTimer) {
    workInProgress.selfBaseDuration = current.selfBaseDuration;
    workInProgress.treeBaseDuration = current.treeBaseDuration;
  }

  return workInProgress;
}

workLoopSync

同步执行整个 Fiber 树的渲染,不会中断或让步给浏览器的主线程。它一直处理 workInProgress,直到所有任务完成

function workLoopSync() {
  // 已经超时,所以执行工作时不再检查是否需要让步给浏览器。
  // React 在并发模式下会定期检查是否需要暂停渲染,以避免长时间占用主线程导致界面卡顿。
  // 但在同步模式下(如这里的 `workLoopSync`),不需要做这种检查。
  
  while (workInProgress !== null) {
    // `workInProgress` 表示当前正在处理的 Fiber 节点。
    // 调用 `performUnitOfWork` 来处理当前的 Fiber 节点的工作。
    performUnitOfWork(workInProgress);
  }
}

performUnitOfWork

function performUnitOfWork(unitOfWork: Fiber): void {
  // 获取当前 Fiber 的备用状态(alternate)
  // 理想情况下,其他代码不应依赖于此,但依赖于此可避免在工作进程中增加额外的字段
  const current = unitOfWork.alternate;

  // 设置当前调试 Fiber(仅在开发模式下)
  setCurrentDebugFiberInDEV(unitOfWork);

  let next;

  // 如果启用性能分析器且 Fiber 的模式包含 ProfileMode
  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
    // 启动性能分析器计时器
    startProfilerTimer(unitOfWork);

    // 开始处理当前 Fiber 的工作
    next = beginWork(current, unitOfWork, subtreeRenderLanes);

    // 停止性能分析器计时器并记录时间差
    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
  } else {
    // 不启用性能分析器,则直接处理当前 Fiber 的工作
    next = beginWork(current, unitOfWork, subtreeRenderLanes);
  }

  // 重置调试 Fiber(仅在开发模式下)
  resetCurrentDebugFiberInDEV();

  // 更新当前 Fiber 的 memoizedProps
  unitOfWork.memoizedProps = unitOfWork.pendingProps;

  // 如果 `next` 为 null,表示没有新工作被生成,完成当前工作
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    // 否则,将 `workInProgress` 设置为下一个工作单元
    workInProgress = next;
  }

  // 清空 ReactCurrentOwner
  ReactCurrentOwner.current = null;
}

beginWork

function beginWork(current, workInProgress, renderLanes) {
  // 处理当前 Fiber 节点的工作
  switch (workInProgress.tag) {
    case FunctionComponent:
      // 处理函数组件
      updateFunctionComponent(workInProgress, renderLanes);
      break;
    case ClassComponent:
      // 处理类组件
      updateClassComponent(current, workInProgress, renderLanes);
      break;
    case HostComponent:
      // 处理宿主组件
      updateHostComponent(current, workInProgress);
      break;
    // 处理其他类型的 Fiber 节点
    // ...
  }

  // 处理子节点
  reconcileChildren(current, workInProgress, renderLanes);

  // 决定是否需要继续工作
  return workInProgress.child;
}

workInProgress.child会作为新的workInProgress。接着performUnitOfWork,如果有子节点一直beginWork

HostRoot 根节点

image.png

// 根据传入的 React 元素创建一个新的 Fiber 节点
const created = createFiberFromElement(element, returnFiber.mode, lanes);

// 处理 `ref` 属性并将其设置到新创建的 Fiber 节点上
created.ref = coerceRef(returnFiber, currentFirstChild, element);

// 将新创建的 Fiber 节点的 `return` 属性设置为父级 Fiber 节点
created.return = returnFiber;

// 返回新创建的 Fiber 节点,以供后续使用
return created;

最后返回workInProgress.child,就函数组件fiber。

函数组件

mountIndeterminateComponent函数

let children = Component(props, secondArg);

image.png

createFiberFromElement

由于有原生标签,将其转为fiber

image.png

useState

function mountState<S>(
  initialState: (() => S) | S, // `initialState` 可以是一个值或者是一个返回值的函数。
): [S, Dispatch<BasicStateAction<S>>] { // 返回一个数组,包含当前的状态和一个用于更新状态的 `dispatch` 函数。

![image.png](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/fb39107ad0c949088e06febbb6fcead8~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5YCf5Liq54GrZXI=:q75.awebp?rk3s=f64ab15b&x-expires=1771683791&x-signature=u4GGepvKtMUaBqtL9H2%2F2nQZ1JA%3D)
  const hook = mountWorkInProgressHook();
  // 获取当前正在处理的 Hook。这是 React 内部的一部分,表示当前的 `useState` Hook 在工作链表中的位置。

  if (typeof initialState === 'function') {
    initialState = initialState();
  }

  // 将 `initialState` 存储在 `hook.memoizedState``hook.baseState` 中。
  // `memoizedState` 是当前的状态值,`baseState` 是基础状态,用于计算新的状态。
  hook.memoizedState = hook.baseState = initialState;

  // 创建一个更新队列,这个队列存储了所有将要应用的状态更新。
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,  // `pending` 存储挂起的更新。初始化为 `null`,表示当前没有挂起的更新。
    interleaved: null, // 用于存储跨组件的并发更新,初始化为 `null`。
    lanes: NoLanes, // `lanes` 用于跟踪更新的优先级。
    dispatch: null, // `dispatch` 是实际触发状态更新的函数,在稍后会初始化。
    lastRenderedReducer: basicStateReducer, // 指向最后使用的状态更新函数,默认为 `basicStateReducer`。
    lastRenderedState: (initialState: any), // 存储最后一次渲染时的状态,这里是初始状态。
  };

  // 将创建的队列与当前 Hook 关联起来,以便在后续的状态更新中使用。
  hook.queue = queue;

  // 定义 `dispatch` 函数,绑定到当前渲染的 Fiber 和更新队列上。
  // `dispatchSetState` 是负责处理状态更新的函数,绑定之后的 `dispatch` 函数将在 `useState` 中返回。
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber, // 绑定当前正在渲染的 Fiber,这样在触发更新时,React 知道是哪个组件发起的。
    queue, // 绑定更新队列,用于追踪和应用状态更新。
  ): any));

  // 返回当前状态和 `dispatch` 函数,以便组件在后续可以通过 `dispatch` 更新状态。
  return [hook.memoizedState, dispatch];
}

useEffect

前置行为与useState一致,都是先创建hook,再与上一个hook关联

function mountWorkInProgressHook(): Hook {
  // 创建一个新的 Hook 对象,初始化它的状态和队列
  const hook: Hook = {
    memoizedState: null, // 用于存储 Hook 的状态值,最常见的是 `useState` 的状态
    baseState: null,     // 用于保存 Hook 的基本状态,通常用于处理某些优化
    baseQueue: null,     // 用于保存基础的更新队列
    queue: null,         // 这个 queue 是用来存放当前 Hook 的更新队列,例如 `useState` 里的 setState 触发的更新
    next: null,          // 指向下一个 Hook,形成一个链表结构
  };

  // 如果 `workInProgressHook` 为 null,表示这是当前 fiber 上第一个 hook
  if (workInProgressHook === null) {
    // 将这个新创建的 Hook 挂载到 `currentlyRenderingFiber` 的 `memoizedState` 上
    // `currentlyRenderingFiber` 表示当前正在渲染的组件的 fiber 节点
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // 如果已经有 hook 存在了,则把这个 hook 添加到链表末尾
    // `workInProgressHook.next` 指向新创建的 hook
    workInProgressHook = workInProgressHook.next = hook;
  }

  // 返回当前工作中的 hook
  return workInProgressHook;
}

再将其挂到fiber的updateQueue和lastEffect

function pushEffect(tag, create, destroy, deps) {
  // 创建一个 Effect 对象,包含标签(tag)、创建函数(create)、销毁函数(destroy)和依赖项(deps)
  const effect: Effect = {
    tag,        // Effect 的类型标签,用于区分不同的副作用类型,比如 useEffect、useLayoutEffect 等
    create,     // 副作用的创建函数,也就是在渲染完成之后要执行的逻辑
    destroy,    // 用于清理副作用的销毁函数,在下次渲染之前或组件卸载时调用
    deps,       // 依赖项数组,用于决定副作用是否需要重新执行
    next: (null: any), // 链表结构,用于将 Effect 连接起来形成一个循环链表
  };

  // 获取当前渲染的 Fiber 的 updateQueue,它用于保存 Function 组件的更新队列
  let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);

  if (componentUpdateQueue === null) {
    // 如果当前 Fiber 没有 updateQueue,则创建一个新的 FunctionComponentUpdateQueue
    componentUpdateQueue = createFunctionComponentUpdateQueue();
    currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);

    // 初始化 Effect 链表,将当前 effect 作为唯一的节点,形成一个自循环链表
    componentUpdateQueue.lastEffect = effect.next = effect;
  } else {
    // 如果已经有更新队列,说明之前可能已经存在 effect 链表
    const lastEffect = componentUpdateQueue.lastEffect;

    if (lastEffect === null) {
      // 如果更新队列中没有 lastEffect,则说明这是第一个 effect
      // 初始化为一个自循环的链表
      componentUpdateQueue.lastEffect = effect.next = effect;
    } else {
      // 如果已经有 lastEffect,说明更新队列中已经有一些 effect 了
      // 找到链表的第一个 effect
      const firstEffect = lastEffect.next;
      
      // 将新 effect 插入到链表的末尾,形成环状链表
      lastEffect.next = effect;
      effect.next = firstEffect;
      
      // 更新 lastEffect,指向最新的 effect
      componentUpdateQueue.lastEffect = effect;
    }
  }
  
  // 返回创建的 effect 对象
  return effect;
}

reconcileChildren

export function reconcileChildren(
  current: Fiber | null,      // 当前 fiber 节点
  workInProgress: Fiber,      // 正在处理的 fiber 节点
  nextChildren: any,          // 下一次渲染的子节点
  renderLanes: Lanes,         // 渲染优先级
) {
  if (current === null) {
    // 如果这是一个新的组件(即还没有渲染过),
    // 我们不会通过应用最小副作用来更新其子节点。
    // 相反,我们会在它渲染之前将所有子节点添加到该组件中。
    // 这意味着我们可以通过不跟踪副作用来优化协调过程。
    workInProgress.child = mountChildFibers(
      workInProgress,  // 当前正在工作的 fiber 节点
      null,            // 当前没有旧的子节点,因为这是首次渲染
      nextChildren,    // 新的子节点
      renderLanes      // 当前渲染的优先级
    );
  } else {
    // 如果 `current` 存在,表示当前组件已经渲染过,接下来进行更新。
    // 如果当前的子节点与 `workInProgress` 的子节点相同,意味着这些子节点
    // 尚未开始处理,因此我们需要通过克隆算法来复制所有当前子节点。
    
    // 如果在此之前有已经进行了一部分的工作,那部分工作在此时无效,
    // 所以我们会将其抛弃,并重新进行协调。
    workInProgress.child = reconcileChildFibers(
      workInProgress,  // 当前正在工作的 fiber 节点
      current.child,   // 上一次渲染的子节点
      nextChildren,    // 新的子节点
      renderLanes      // 当前渲染的优先级
    );
  }
}
reconcileChildFibers
function reconcileChildFibers(parentFiber, currentFirstChild, newChild, lanes) {
  // 如果顶层是无键片段(<>...</>),则将其视为数组处理
  if (newChild 是无键片段) {
    newChild = newChild.props.children;
  }

  // 如果新子节点是对象类型
  if (newChild 是对象) {
    switch (newChild.$$typeof) {
      case REACT_ELEMENT_TYPE:
        // 如果是 React 元素,调用 `reconcileSingleElement` 进行协调
        return placeSingleChild(
          reconcileSingleElement(parentFiber, currentFirstChild, newChild, lanes)
        );
      case REACT_PORTAL_TYPE:
        // 如果是 Portal,调用 `reconcileSinglePortal` 进行协调
        return placeSingleChild(
          reconcileSinglePortal(parentFiber, currentFirstChild, newChild, lanes)
        );
      case REACT_LAZY_TYPE:
        // 如果是懒加载组件,初始化后递归调用 `reconcileChildFibers`
        return reconcileChildFibers(
          parentFiber,
          currentFirstChild,
          newChild._init(newChild._payload),
          lanes
        );
    }

    // 如果是数组类型,调用 `reconcileChildrenArray` 进行协调
    if (Array.isArray(newChild)) {
      return reconcileChildrenArray(parentFiber, currentFirstChild, newChild, lanes);
    }

    // 如果是可迭代对象,调用 `reconcileChildrenIterator` 进行协调
    if (getIteratorFn(newChild)) {
      return reconcileChildrenIterator(parentFiber, currentFirstChild, newChild, lanes);
    }
  }
  // 如果新子节点是文本节点(字符串或数字),调用 `reconcileSingleTextNode` 进行协调
  if (typeof newChild === 'string' || typeof newChild === 'number') {
    return placeSingleChild(
      reconcileSingleTextNode(parentFiber, currentFirstChild, String(newChild), lanes)
    );
  }

  // 其他情况,视为无效子节点,删除剩余的旧子节点
  return deleteRemainingChildren(parentFiber, currentFirstChild);
}

如果新子节点是一个 React 元素,调用reconcileSingleElement的createFiberFromElement,再打上标记

// 标记这个 fiber 为 Placement,以指示它需要被插入。
newFiber.flags |= Placement;

image.png

completeUnitOfWork

HostComponent

创建dom

export function createInstance(
  type: string, // 元素类型,例如 'div' 或 'span'
  props: Props, // 元素的属性
  rootContainerInstance: Container, // 根容器实例
  hostContext: HostContext, // 主机上下文
  internalInstanceHandle: Object, // 内部实例句柄
): Instance {
  let parentNamespace: string;
  // 创建 DOM 元素
  const domElement: Instance = createElement(
    type,
    props,
    rootContainerInstance,
    parentNamespace,
  );

  // 缓存 Fiber 节点到dom上
  precacheFiberNode(internalInstanceHandle, domElement);

  // 更新 Fiber 属性
  updateFiberProps(domElement, props);

  return domElement;
}

image.png

// 创建一个新的 DOM 实例
const instance = createInstance(
  type, // 组件类型(如 'div', 'span')
  newProps, // 组件的新属性(props)
  rootContainerInstance, // 根容器实例,通常是页面的根 DOM 元素
  currentHostContext, // 当前主机上下文,包含关于当前环境的信息
  workInProgress // 当前正在处理的 Fiber 对象,用于关联新创建的 DOM 实例
);

// 将所有子元素附加到新创建的 DOM 实例上
appendAllChildren(instance, workInProgress, false, false);
// 参数解释:
// - instance: 新创建的 DOM 元素
// - workInProgress: 当前 Fiber 节点,包含子元素的信息

// 将新创建的 DOM 实例赋值给当前 Fiber 节点的 stateNode 属性
workInProgress.stateNode = instance;

经过该操作,fiber上的stateNode就有dom了

commitRoot

提交流程主要操作的是 finishedWork,对应render流程的 workInProgress image.png

function commitRoot(
  root: FiberRoot,
  recoverableErrors: null | Array<CapturedValue<mixed>>,
  transitions: Array<Transition> | null,
) {
  console.log("commitRoot...");
  
  // 记录当前的更新优先级和过渡配置
  const previousUpdateLanePriority = getCurrentUpdatePriority(); // 保存当前更新优先级
  const prevTransition = ReactCurrentBatchConfig.transition; // 保存当前过渡配置

  try {
    // 在提交过程中,清除当前的过渡配置
    ReactCurrentBatchConfig.transition = null;

    // 设置当前更新的优先级为离散事件优先级
    setCurrentUpdatePriority(DiscreteEventPriority);

    // 调用实际的提交实现函数,处理根 Fiber 的提交
    commitRootImpl(
      root,
      recoverableErrors,
      transitions,
      previousUpdateLanePriority,
    );
  } finally {
    // 恢复之前保存的过渡配置
    ReactCurrentBatchConfig.transition = prevTransition;

    // 恢复之前保存的更新优先级
    setCurrentUpdatePriority(previousUpdateLanePriority);
  }

  // 函数执行完毕,返回 null
  return null;
}

再一个消息,触发浏览器事件循环

image.png

image.png

commitMutationEffects

image.png

commitMutationEffectsOnFiber

commitReconciliationEffects

function commitReconciliationEffects(finishedWork: Fiber) {
  // 获取当前 fiber 的 effect flags,这些标志位记录了需要处理的副作用类型。
  const flags = finishedWork.flags;

  // 如果当前 fiber 上有 Placement 标志位,表示需要执行插入操作。
  if (flags & Placement) {
    try {
      // 执行插入操作,将 fiber 插入到 DOM 中。
      commitPlacement(finishedWork);
    } catch (error) {
      // 如果插入操作中发生错误,捕获并记录错误。
      captureCommitPhaseError(finishedWork, finishedWork.return, error);
    }
    // 成功插入后,清除 Placement 标志位,表示当前 fiber 已经被插入到 DOM 中。
    // TODO: 之前的实现依赖于这个标志位来判断元素是否已插入,但 isMounted 已被弃用,未来可能不再需要这个清除操作。
    finishedWork.flags &= ~Placement;
  }

  // 如果当前 fiber 上有 Hydrating 标志位,表示需要执行 hydration 操作。
  if (flags & Hydrating) {
    // 清除 Hydrating 标志位,表示 hydration 操作已完成。
    finishedWork.flags &= ~Hydrating;
  }
}

commitPlacement

function commitPlacement(finishedWork: Fiber): void {
  // 如果不支持 mutation 操作(即不支持直接修改 DOM),则不执行任何操作。
  if (!supportsMutation) {
    return;
  }

  // 获取当前 fiber 的父级 fiber,用于确定插入的位置。
  const parentFiber = getHostParentFiber(finishedWork);

  // Note: these two variables *must* always be updated together.
  switch (parentFiber.tag) {
    case HostComponent: {
      // 如果父级 fiber 是一个 HostComponent,则获取它的实例。
      const parent: Instance = parentFiber.stateNode;

      // 如果父级 fiber 有 ContentReset 标志位,则重置文本内容。
      if (parentFiber.flags & ContentReset) {
        // 重置父级节点的文本内容,以便插入新内容。
        resetTextContent(parent);
        // 清除 ContentReset 标志位,表示文本内容已被重置。
        parentFiber.flags &= ~ContentReset;
      }

      // 获取当前 fiber 的同级 sibling(兄弟节点)中的插入位置。
      const before = getHostSibling(finishedWork);

      // 递归插入当前 fiber 及其所有子节点到 DOM 中。
      insertOrAppendPlacementNode(finishedWork, before, parent);
      break;
    }
    case HostRoot:
    case HostPortal: {
      // 如果父级 fiber 是 HostRoot 或 HostPortal,则获取它的容器信息。
      const parent: Container = parentFiber.stateNode.containerInfo;
      // 获取当前 fiber 的同级 sibling(兄弟节点)中的插入位置。
      const before = getHostSibling(finishedWork);
      // 递归插入当前 fiber 及其所有子节点到容器中。
      insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
      break;
    }
  }
}

image.png

image.png

到此 页面上才有了交互变化

image.png 再次发送一个消息,通知浏览器事件循环

commitPassiveMountOnFiber

commitHookEffectListMount

// 该函数用于处理 React 中的 Hook 的挂载阶段的副作用(如 useEffect 或 useLayoutEffect)
// 根据传入的 flag 类型(如 HookPassive 或 HookLayout),来执行相应的副作用

function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
  // 获取当前 fiber 的更新队列
  const updateQueue: FunctionComponentUpdateQueue | null =
    (finishedWork.updateQueue: any);
  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;

  // 如果有副作用链表,则执行挂载副作用
  if (lastEffect !== null) {
    // 获取第一个副作用
    const firstEffect = lastEffect.next;
    let effect = firstEffect;

    // 循环遍历所有副作用,直到回到第一个副作用
    do {
      // 检查当前副作用是否与传入的 flags 匹配
      if ((effect.tag & flags) === flags) {
        // 开启性能分析器时,会标记副作用挂载的开始与结束
        if (enableSchedulingProfiler) {
          if ((flags & HookPassive) !== NoHookEffect) {
            // 标记被动副作用的挂载开始
            markComponentPassiveEffectMountStarted(finishedWork);
          } else if ((flags & HookLayout) !== NoHookEffect) {
            // 标记布局副作用的挂载开始
            markComponentLayoutEffectMountStarted(finishedWork);
          }
        }

        // 挂载阶段,调用副作用的 create 函数,通常是 useEffect 的回调
        const create = effect.create;

        // 执行 create 函数并将其返回值(销毁函数)保存到 effect.destroy 中
        effect.destroy = create();

        // 结束性能分析器的标记
        if (enableSchedulingProfiler) {
          if ((flags & HookPassive) !== NoHookEffect) {
            markComponentPassiveEffectMountStopped();
          } else if ((flags & HookLayout) !== NoHookEffect) {
            markComponentLayoutEffectMountStopped();
          }
        }
      }

      // 移动到下一个副作用
      effect = effect.next;
    } while (effect !== firstEffect); // 如果回到第一个副作用,结束循环
  }
}

更新

由于useState第二个参数绑定着fiber concurrentQueues存着

image.png 找到根fiber,再次scheduleUpdateOnFiber

const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
  // 获取当前事件触发的时间戳,记录事件发生的时间
  const eventTime = requestEventTime();

  // 调度更新,处理当前 Fiber 节点上的更新任务
  // root:当前 Fiber 树的根节点
  // fiber:需要更新的 Fiber 节点
  // lane:表示此次更新的优先级
  // eventTime:事件触发的时间戳
  scheduleUpdateOnFiber(root, fiber, lane, eventTime);

  // 处理过渡更新,使当前更新与其他更新建立联系
  // root:当前 Fiber 树的根节点
  // queue:当前 Fiber 节点上的 Hook 队列
  // lane:表示此次更新的优先级
  entangleTransitionUpdate(root, queue, lane);
}
export function addFiberToLanesMap(
  root: FiberRoot, // Fiber 树的根节点
  fiber: Fiber, // 当前需要更新的 Fiber 节点
  lanes: Lanes | Lane, // 本次更新的 lane(更新优先级)
) {
  // 如果没有启用 Updater 跟踪,则直接返回
  if (!enableUpdaterTracking) {
    return;
  }
  // 如果 DevTools 不存在,则不需要进行任何操作
  if (!isDevToolsPresent) {
    return;
  }

  // 从根节点获取 pendingUpdatersLaneMap,记录待处理的更新
  const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;

  // 循环处理 lanes,直到所有位都处理完
  while (lanes > 0) {
    // 将 lanes 转换为索引,用于找到对应的 lane
    const index = laneToIndex(lanes);

    // 使用位运算提取当前 lane
    const lane = 1 << index;

    // 获取对应 lane 的更新器集合
    const updaters = pendingUpdatersLaneMap[index];

    // 将当前 Fiber 节点添加到该 lane 对应的更新器集合中
    updaters.add(fiber);

    // 移除已处理的 lane,继续处理下一个
    lanes &= ~lane;
  }
}

image.png 接着ensureRootIsScheduled,prepareFreshStack(root, lanes);

createWorkInProgress

创建一个新的tree

// 如果已经有一个备用 Fiber 节点,则复用它
workInProgress.pendingProps = pendingProps;

// 更新 type,Blocks 类型组件会存储数据在 type 上
workInProgress.type = current.type;

// 重置 effect 标记,因为已经有 alternate 节点
workInProgress.flags = NoFlags;

// 清除无效的子树效果
workInProgress.subtreeFlags = NoFlags;
workInProgress.deletions = null;

// 如果启用了 Profiler Timer,则重置时间相关的属性
if (enableProfilerTimer) {
  workInProgress.actualDuration = 0;
  workInProgress.actualStartTime = -1;
}

workLoopSync

又执行performUnitOfWork的beginWork

reconcileSingleElement

image.png

image.png

image.png

image.png

image.png

performSyncWorkOnRoot

root.finishedWork就是渲染阶段的workInProgress

image.png

commitRootImpl

发送消息,下一个事件循环执行dom操作

commitUpdate

image.png

image.png

image.png 调用setTextContent设置nodeValue,此时页面更新

commitPassiveUnmountEffects

image.png

    // 执行副作用的销毁函数
    function safelyCallDestroy(
      current: Fiber,
      nearestMountedAncestor: Fiber | null,
      destroy: () => void,
    ) {
      try {
        destroy();
      } catch (error) {
        captureCommitPhaseError(current, nearestMountedAncestor, error);
      }
    }

commitPassiveMountEffects

image.png

image.png

updateFunctionComponent