React源碼解析之Update和UpdateQueue

1、Update
位置:
Update位置以下( 詳情請看React源碼解析之ReactDOM.render() ):
updateContainer()—>
updateContainerAtExpirationTime()—>
scheduleRootUpdate()—>
createUpdate()javascript

做用:
(1)用來記錄組件的狀態變化
(2)存放在UpdateQueue
(3)多個Update能夠同時存在
好比設置三個setState()React是不會當即更新的,而是放到UpdateQueue中,再去更新java

源碼:react

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時,爲捕獲性更新,也就是在更新中捕獲到錯誤時,渲染成錯誤狀態web

(2)多個updatepush進更新隊列中,next屬性指向下一節點app

2、UpdateQueue
位置:
UpdateQueue位置以下( 詳情請看React源碼解析之ReactDOM.render() ):
updateContainer()—>
updateContainerAtExpirationTime()—>
scheduleRootUpdate()—>
enqueueUpdate(current, update)——>
createUpdateQueue()ide

做用:
依次執行內部的updatespa

源碼:code

//建立更新隊列
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,
    firstCapturedEffectnull,
    lastCapturedEffectnull,
  };
  return queue;
}
複製代碼

解析:
(1)baseState
在組件setState後,渲染並更新state,在下次更新時,拿的就是此次更新過的stateorm

(2)firstUpdatelastUpdate之間的update經過上個updatenext串聯cdn

3、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


(完)

相關文章
相關標籤/搜索