优先级

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

相关推荐
fruge9 分钟前
Vue Pinia 状态管理实战指南
前端·vue.js·ubuntu
sean38 分钟前
开发一个自己的 claude code
前端·后端·ai编程
用户214118326360242 分钟前
dify案例分享-用 Dify 一键生成教学动画 HTML!AI 助力,3 分钟搞定专业级课件
前端
太过平凡的小蚂蚁3 小时前
Kotlin 协程中常见的异步返回与控制方式(速览)
开发语言·前端·kotlin
咖啡の猫3 小时前
Vue初始化脚手架
前端·javascript·vue.js
晨枫阳4 小时前
uniapp兼容问题处理总结
前端·vue.js·uni-app
liusheng5 小时前
腾讯地图 SDK 接入到 uniapp 的多端解决方案
前端·uni-app
拉不动的猪5 小时前
如何处理管理系统中(Vue PC + uni-app 移动端):业务逻辑复用基本方案
前端·javascript·架构
边洛洛5 小时前
next.js项目部署流程
开发语言·前端·javascript
Zsnoin能5 小时前
浏览器连接 新北洋BTP-P33/P32蓝牙打印机,打印 二维码
前端