优先级

lane 优先级

在 React lanes 模型是 31 个二进制通道。以 1 的位置表示当前更新的优先级,全为 0 时表示没有更新。优先级从右往左依次降低。

如下图所示:

事件优先级

事件优先级有以下几种:

ini 复制代码
export const DiscreteEventPriority = SyncLane;
export const ContinuousEventPriority = InputContinuousLane;
export const DefaultEventPriority = DefaultLane;
export const IdleEventPriority = IdleLane;

可以看到,事件优先级的值其实就是对应的 lane 优先级。由于 react 是合成事件,所以在触发事件时,会调用 setCurrentUpdatePriority 函数,将优先级设置为当前事件对应优先级,方便调用。

js 复制代码
export function getEventPriority(domEventName: DOMEventName): * {
  switch (domEventName) {
    // 删除其他事件
    // ...
    case 'click':
      return DiscreteEventPriority;
    case 'drag':
      return ContinuousEventPriority;
    default:
      return DefaultEventPriority;
  }
}

getEventPriority 函数根据事件名返回不同的事件优先级。

这个函数在创建事件监听函数时会调用,获取当前事件对应的事件优先级。

ini 复制代码
export function createEventListenerWrapperWithPriority(
  targetContainer: EventTarget,
  domEventName: DOMEventName,
  eventSystemFlags: EventSystemFlags,
): Function {
  const eventPriority = getEventPriority(domEventName);
  let listenerWrapper;
  switch (eventPriority) {
    case DiscreteEventPriority:
      listenerWrapper = dispatchDiscreteEvent;
      break;
    case ContinuousEventPriority:
      listenerWrapper = dispatchContinuousEvent;
      break;
    case DefaultEventPriority:
    default:
      listenerWrapper = dispatchEvent;
      break;
  }
  return listenerWrapper.bind(
    null,
    domEventName,
    eventSystemFlags,
    targetContainer,
  );
}
function dispatchDiscreteEvent(
  domEventName,
  eventSystemFlags,
  container,
  nativeEvent,
) {
  const previousPriority = getCurrentUpdatePriority();
  const prevTransition = ReactCurrentBatchConfig.transition;
  ReactCurrentBatchConfig.transition = null;
  try {
    setCurrentUpdatePriority(DiscreteEventPriority);
    dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
  } finally {
    setCurrentUpdatePriority(previousPriority);
    ReactCurrentBatchConfig.transition = prevTransition;
  }
}

需要注意的是 getCurrentUpdatePrioritysetCurrentUpdatePriority,这两个函数操作的一个全局变量 currentUpdatePriority。这个变量缓存了当前更新的优先级。

在调用 setState(这里举个例子,其实不止这个函数) 函数时,就可以调用 requestUpdateLane 获取当前优先级。

js 复制代码
export function requestUpdateLane(fiber: Fiber): Lane {
  // ...
  const updateLane: Lane = (getCurrentUpdatePriority(): any);
  if (updateLane !== NoLane) {
    return updateLane;
  }
  const eventLane: Lane = (getCurrentEventPriority(): any);
  return eventLane;
}

requestUpdateLane 先从 getCurrentUpdatePriority 函数获取优先级,如果存在则直接返回,不存在则返回事件优先级。

最后在创建更新时,就有了优先级,于是创建的更新队列也就有了优先级。

如图所示:

这是加了优先级之后的更新链表。为了实现高优先级先执行,还需要维护一个链表。这个链表是实际执行的链表。为此需要在更新队列对象中增加几个参数。

js 复制代码
function initializeUpdateQueue(fiber) {
  const queue = {
    baseState: fiber.memoizedState, //本次更新前该Fiber节点的state,Update基于该state计算更新后的state
    firstBaseUpdate: null, //本次更新前该Fiber节点已保存的Update链表头
    lastBaseUpdate: null, //本次更新前该Fiber节点已保存的Update链表尾
    shared: {
      //触发更新时,产生的Update会保存在shared.pending中形成单向环状链表
      //当由Update计算state时这个环会被剪开并连接在lastBaseUpdate后面
      pending: null,
    },
  };
  fiber.updateQueue = queue;
}

老更新队列为空示例图:

老更新队列不为空示意图:

由上图所示,当更新队列执行时,实际执行的队列为 firstBaseUpdate 开头到 lastBaseUpdate 结束的更新队列。

当更新优先级低于渲染优先级时,该更新会被跳过,并将 firstBaseUpdate 指向该更新,继续遍历链表,将 lastBaseUpdate 指向链表尾部。

在下一次更新时,先合并链表,再根据优先级执行。

源码在 /Users/sunxianfu/Desktop/Project/react/packages/react-reconciler/src/ReactFiberClassUpdateQueue.old.js

相关推荐
Mintopia2 分钟前
Vite 发展现状与回顾:从“极致开发体验”到生态基础设施
前端
前端双越老师25 分钟前
前端面试常见的 10 个场景题
前端·面试·求职
孟祥_成都1 小时前
【全网最通俗!新手到AI全栈开发必读】 AI 是如何进化到大模型的
前端·人工智能·全栈
牛奶2 小时前
AI辅助开发的基础概念
前端·人工智能·ai编程
摸鱼的春哥2 小时前
Agent教程15:认识LangChain,Agent框架的王(上)
前端·javascript·后端
明月_清风3 小时前
自定义右键菜单:在项目里实现“选中文字即刻生成新提示”
前端·javascript
明月_清风3 小时前
告别后端转换:高质量批量导出实战
前端·javascript
刘发财7 小时前
弃用html2pdf.js,这个html转pdf方案能力是它的几十倍
前端·javascript·github
牛奶10 小时前
2026年大模型怎么选?前端人实用对比
前端·人工智能·ai编程
牛奶10 小时前
前端人为什么要学AI?
前端·人工智能·ai编程