Skip to content

useLayoutEffect

作用

其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

尽可能使用标准的 useEffect 以避免阻塞视觉更新。

useLayoutEffect 的保存

js
function mountEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null
): void {
  return mountEffectImpl(
    UpdateEffect | PassiveEffect,
    HookPassive,
    create,
    deps
  );
}

function mountLayoutEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null
): void {
  return mountEffectImpl(UpdateEffect, HookLayout, create, deps);
}

可见其对于 effect 的创建和保存跟 useEffect 是一样的,只是其 FiberNode.flagseffect.tag 不同

执行时机

在函数组件 useLayoutEffect 执行是在 Scheduler 任务调度的 commit 阶段的 commitMutationEffectscommitLayoutEffects

其中分为 destroy 的执行 和 create 的执行两个过程,下面分别看这两个过程

commitMutationEffects

在这个阶段 执行 useLayoutEffect 的 destroy 回调函数

commitLayoutEffects

在这个阶段 执行 useLayoutEffect 的 create 回调函数

结论

执行时机

  • useLayoutEffect的destroy函数 : 在 flushPassiveEffects 阶段执行,所以其在 DOM 更新之前
  • useLayoutEffect的create函数 : 在 flushPassiveEffects 阶段执行,所以其在 DOM 更新之前

执行顺序

  • useLayoutEffect的destroy函数 : 在 flushPassiveEffects 阶段执行,按照 先父后子的顺序执行
  • useLayoutEffect的create函数 : 在 flushPassiveEffects 阶段执行,按照 先子后父的顺序执行

关键信息

  1. fiberFlags
  • 初始化渲染阶段 : PassiveEffect | PassiveStaticEffect
  • 更新 渲染阶段 : PassiveEffect
  1. hookFlags
  • 初始化渲染阶段 : HookPassive
  • 更新 渲染阶段 : HookPassive

useLayoutEffect 和 useEffect 的异同点

相同点

  1. 都是 effect 类型的 hook
  • 都会在函数组件执行阶段在 FiberNode.memoizedState 上生成一个 hook 对象
  • 都会在函数组件执行阶段在 FiberNode.updateQueue 上添加一个 effect 副作用
  1. 缓存数据结构、语法(deps、create、destroy)都很像

不同点

  1. 执行时机不同
  • useLayoutEffect

    • create 在 commit 的最后阶段(commitLayoutEffects)中才会执行,并生成 destroy

    • destroy 在 commit 的第二阶段(commitMutationEffects)中如果是函数类型组件的卸载操作,就会执行 destroy 回调

    • useEffect 是在flushPassiveEffects 的时候统一执行(destroy -> create)

  1. 执行的方式不同

    • useLayoutEffect 他是在组件 Commit 阶段同步执行的
    • useLayout : 在组件 Commit 阶段收集,然后通过 scheduler 高优先级任务队列回调执行的