React源码解析之Update和UpdateQueue

2019-08-25  本文已影响0人  小进进不将就

一、Update
位置:
Update位置如下( 详情请看React源码解析之ReactDOM.render() ):
updateContainer()—>
updateContainerAtExpirationTime()—>
scheduleRootUpdate()—>
createUpdate()

作用:
(1)用来记录组件的状态变化
(2)存放在UpdateQueue
(3)多个Update可以同时存在
比如设置三个setState()React是不会立即更新的,而是放到UpdateQueue中,再去更新

源码:

export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3;

export function createUpdate(
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
): Update<*> {
  return {
    //更新的过期时间
    expirationTime,
    suspenseConfig,

    // export const UpdateState = 0;
    // export const ReplaceState = 1;
    // export const ForceUpdate = 2;
    // export const CaptureUpdate = 3;

    //重点提下CaptureUpdate,在React16后有一个ErrorBoundaries功能
    //即在渲染过程中报错了,可以选择新的渲染状态(提示有错误的状态),来更新页面
    tag: UpdateState, //0更新 1替换 2强制更新 3捕获性的更新

    //更新内容,比如setState接收的第一个参数
    payload: null,

    //对应的回调,比如setState({}, callback )
    callback: null,

    //指向下一个更新
    next: null,

    //指向下一个side effect
    nextEffect: null,
  };
}

解析:
update属性的解释均已写在代码中,需要注意的是
(1)tag的值为CaptureUpdate时,为捕获性更新,也就是在更新中捕获到错误时,渲染成错误状态

(2)多个updatepush进更新队列中,next属性指向下一节点

二、UpdateQueue
位置:
UpdateQueue位置如下( 详情请看React源码解析之ReactDOM.render() ):
updateContainer()—>
updateContainerAtExpirationTime()—>
scheduleRootUpdate()—>
enqueueUpdate(current, update)——>
createUpdateQueue()

作用:
依次执行内部的update

源码:

//创建更新队列
export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
  const queue: UpdateQueue<State> = {
    //应用更新后的state
    baseState,
    //队列中的第一个update
    firstUpdate: null,
    //队列中的最后一个update
    lastUpdate: null,
    //队列中第一个捕获类型的update
    firstCapturedUpdate: null,
    //队列中最后一个捕获类型的update
    lastCapturedUpdate: null,
    //第一个side effect
    firstEffect: null,
    //最后一个side effect
    lastEffect: null,
    firstCapturedEffect: null,
    lastCapturedEffect: null,
  };
  return queue;
}

解析:
(1)baseState
在组件setState后,渲染并更新state,在下次更新时,拿的就是这次更新过的state

(2)firstUpdatelastUpdate之间的update通过上个updatenext串联

三、enqueueUpdate()
作用:
单向链表,用来存放updatenext来串联update

源码:

//每次setState都会update,每次update,都会入updateQueue
//current即fiber
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  // Update queues are created lazily.
  //alternate即workInProgress
  //fiber即current

  //current到alternate即workInProgress有一个映射关系
  //所以要保证current和workInProgress的updateQueue是一致的
  const alternate = fiber.alternate;
  //current的队列
  let queue1;
  //alternate的队列
  let queue2;
  //如果alternate为空
  if (alternate === null) {
    // There's only one fiber.
    queue1 = fiber.updateQueue;
    queue2 = null;
    //如果queue1仍为空,则初始化更新队列
    if (queue1 === null) {
      queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
    }
  } else {
    // There are two owners.
    //如果alternate不为空,则取各自的更新队列
    queue1 = fiber.updateQueue;
    queue2 = alternate.updateQueue;
    if (queue1 === null) {
      if (queue2 === null) {
        // Neither fiber has an update queue. Create new ones.
        //初始化
        queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
        queue2 = alternate.updateQueue = createUpdateQueue(
          alternate.memoizedState,
        );
      } else {
        // Only one fiber has an update queue. Clone to create a new one.
        //如果queue2存在但queue1不存在的话,则根据queue2复制queue1
        queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
      }
    } else {
      if (queue2 === null) {
        // Only one fiber has an update queue. Clone to create a new one.
        queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
      } else {
        // Both owners have an update queue.
      }
    }
  }
  if (queue2 === null || queue1 === queue2) {
    // There's only a single queue.
    //将update放入queue1中
    appendUpdateToQueue(queue1, update);
  } else {
    // There are two queues. We need to append the update to both queues,
    // while accounting for the persistent structure of the list — we don't
    // want the same update to be added multiple times.
    //react不想多次将同一个的update放入队列中
    //如果两个都是空队列,则添加update
    if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
      // One of the queues is not empty. We must add the update to both queues.
      appendUpdateToQueue(queue1, update);
      appendUpdateToQueue(queue2, update);
    }
    //如果两个都不是空队列,由于两个结构共享,所以只在queue1加入update
    //在queue2中,将lastUpdate指向update
    else {
      // Both queues are non-empty. The last update is the same in both lists,
      // because of structural sharing. So, only append to one of the lists.
      appendUpdateToQueue(queue1, update);
      // But we still need to update the `lastUpdate` pointer of queue2.
      queue2.lastUpdate = update;
    }
  }
}

解析:
(1)queue1取的是fiber.updateQueue;
queue2取的是alternate.updateQueue
(2)如果两者均为null,则调用createUpdateQueue()获取初始队列
(3)如果两者之一为null,则调用cloneUpdateQueue()从对方中获取队列
(4)如果两者均不为null,则将update作为lastUpdate


(完)

上一篇下一篇

猜你喜欢

热点阅读