来看看 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

相关文章
相关标签/搜索