优先级

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

相关推荐
方方洛几秒前
电子书阅读器:epub电子书文件的解析
前端·产品·电子书
idaibin1 分钟前
Rustzen Admin 前端简单权限系统设计与实现
前端·react.js
GISer_Jinger7 分钟前
Trae Solo模式生成一个旅行足迹App
前端·javascript
zhangbao90s8 分钟前
Intl API:浏览器原生国际化API入门指南
前端·javascript·html
艾小码10 分钟前
构建现代前端工程:Webpack/Vite/Rollup配置解析与最佳实践
前端·webpack·node.js
跟橙姐学代码15 分钟前
Python 集合:人生中最简单的真理,只有一次
前端·python·ipython
复苏季风17 分钟前
站在2025 年 来看,现在应该怎么入门CSS
前端·css
pepedd86418 分钟前
深度解剖 Vue3 架构:编译时 + 运行时的协作
前端·vue.js·trae
一枚前端小能手20 分钟前
🧪 改个代码就出Bug的恐惧,前端测试来帮忙
前端·测试
s3xysteak21 分钟前
我要成为vue高手02:数据传递
前端·javascript·vue.js