美文网首页
React源码解析之Update和UpdateQueue

React源码解析之Update和UpdateQueue

作者: 小进进不将就 | 来源:发表于2019-08-25 18:34 被阅读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


    (完)

    相关文章

      网友评论

          本文标题:React源码解析之Update和UpdateQueue

          本文链接:https://www.haomeiwen.com/subject/bscysctx.html