來看看 vue3.0 跟 React16 + HOOK的源碼對比 哪一個香?(2)

書接上文數組


effect也就是在React中咱們常說的side effect,在React中相似像componentDidMount這樣的生命週期方法中,由於可能會執行setState這樣的方法而產生新的更新,咱們稱爲side effect即替代。自己FunctionalComponent由於是pure function,因此不會產生任何的異常,而useEffectuseLayoutEffect則是帶給產生FunctionalComponent交替能力的掛鉤,他們的行爲很是相似componentDidMountcomponentDidUpdatebash

他們接受一個方法做爲參數,該方法會在每次渲染完成後被調用;其次還接受第二個參數,是一個數組,這個數組裏的每個內容都會被進行進行先後的對比,若是沒有變化,則不會引起該中斷。


function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
  return {
    lastEffect: null,
  };
}

function pushEffect(tag, create, destroy, inputs) {
  const effect: Effect = {
    tag,
    create,
    destroy,
    inputs,
    // Circular
    next: (null: any),
  };
  if (componentUpdateQueue === null) {
    componentUpdateQueue = createFunctionComponentUpdateQueue();
    componentUpdateQueue.lastEffect = effect.next = effect;
  } else {
    const lastEffect = componentUpdateQueue.lastEffect;
    if (lastEffect === null) {
      componentUpdateQueue.lastEffect = effect.next = effect;
    } else {
      const firstEffect = lastEffect.next;
      lastEffect.next = effect;
      effect.next = firstEffect;
      componentUpdateQueue.lastEffect = effect;
    }
  }
  return effect;
}

複製代碼

不難發現這個過程實際上就是往當前 Fiber上增長一部分 effectTag,而且會建立 updateQueue,這跟 HostComponent相似,這個 queue會在 commit階段被執行。這裏咱們須要注意的是 useLayoutEffecuseEffect增長的 effectTag是不同的,因此他們執行的時機也是不同的。 effectTag
會有如下幾種狀況:

  • useLayoutEffect
    增長
    UpdateEffect
  • useEffect
    增長
    UpdateEffect | PassiveEffect

以上是增長在 Fiber對象上的,而記錄對應的掛鉤對象的 effectTag
以下:

  • useLayoutEffect
    增長
    UnmountMutation | MountLayout
  • useEffect
    增長
    UnmountPassive | MountPassive
  • 若是
    areHookInputsEqual
    符合,則增長
    NoHookEffect

commit階段Hook相關的內容

在如下三個階段都會調用commitHookEffectList方法,咱們來看一下:異步

  • commitWorkcommitHookEffectList(UnmountMutation, MountMutation, finishedWork);
  • commitBeforeMutationLifeCyclescommitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork);
  • commitLifeCyclescommitHookEffectList(UnmountLayout, MountLayout, finishedWork);

commitHookEffectList這個方法的內容就是根據傳入的unmountTagmountTag來判斷是否須要執行對應的destorycreate方法,這是在每一個Hook對象的effect鏈上的。因此看這部分代碼最重要的其實就是看他傳入的effectTag和Hook對象上的effectTag的對比。ide

對比結果就是:ui

  1. useLayoutEffectdestory會在commitWork的時候被執行;而他的create會在commitLifeCycles的時候被執行。
  2. useEffect在這個流程中都不會被執行。

能夠看出來useLayoutEffect的執行過程跟componentDidMountcomponentDidUpdate很是類似,因此React官方也說了,若是你必定要選擇一個相似於生命週期方法的Hook,那麼useLayoutEffect是不會錯的那個,可是咱們推薦你使用useEffect,在你清除他們的區別的前提下,後者是更好的選擇。spa

那麼useEffect何時被調用呢?code

答案在commitRoot的最後,他等其餘sideEffect所有commit完了以後,會執行如下代碼:component

if (
  enableHooks &&
  firstEffect !== null &&
  rootWithPendingPassiveEffects !== null
) {
  let callback = commitPassiveEffects.bind(null, root, firstEffect);
  passiveEffectCallbackHandle = Schedule_scheduleCallback(callback);
  passiveEffectCallback = callback;
}複製代碼

rootWithPendingPassiveEffects是在commitAllLifeCycles的時候若是發現更新中有passive effect的節點的話,就等於FiberRootorm

if (enableHooks && effectTag & Passive) {
  rootWithPendingPassiveEffects = finishedRoot;
}複製代碼

這裏若是有,則會發起一次Schedule_scheduleCallback,這個就是咱們以前講的異步調度模塊Scheduler的方法,流程跟PerformWork相似,這裏咱們再也不重複講解。對象

但咱們看到這裏就清楚了,useEffectdestorycreate都是異步調用的,因此他們不會影響本次更新的提交,因此不會由於在effect中產生了新的更新而致使阻塞DOM渲染的狀況。

那麼commitPassiveEffects作了啥呢?

export function commitPassiveHookEffects(finishedWork: Fiber): void {
  commitHookEffectList(UnmountPassive, NoHookEffect, finishedWork);
  commitHookEffectList(NoHookEffect, MountPassive, finishedWork);
}複製代碼

正好對應了useEffect設置的sideEffect

相關文章
相關標籤/搜索