优先级

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

相关推荐
We་ct6 小时前
深度剖析浏览器跨域问题
开发语言·前端·浏览器·跨域·cors·同源·浏览器跨域
weixin_427771616 小时前
前端调试隐藏元素
前端
爱上好庆祝7 小时前
学习js的第五天
前端·css·学习·html·css3·js
C澒7 小时前
IntelliPro 产研协作平台:基于 AI Agent 的低代码智能化配置方案设计与实现
前端·低代码·ai编程
一袋米扛几楼987 小时前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴8 小时前
前端与后端的区别与联系
前端
EnCi Zheng8 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen8 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技8 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人8 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html