前言:自从react16.8推出hooks以来,hooks也被越来越多的人使用,面试中也不可避免的会被问到。那么hooks中的useEffect 与 useLayoutEffect 到底有什么区别呢?这个问题面试中经常会碰到,今天我们就来探究一下。
首先,我们来看下react官网中对此是怎么解释的:
使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 useLayoutEffect Hook 供你使用,其 API 与 useEffect 相同。
也就是说,useEffect 是异步执行的,而 useLayoutEffect 是同步执行的,会阻塞浏览器更新渲染。
那么,两个之间到底有什么区别呢?
1.相同点
在hooks的官方文档中对 useLayoutEffect 是这样定义的:
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
那么我们来看下源码中是怎么定义这两个函数的:
useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
currentHookNameInDev = 'useEffect';
mountHookTypesDev();
checkDepsAreArrayDev(deps);
return mountEffect(create, deps);
},
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
if (__DEV__) {
// $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
if ('undefined' !== typeof jest) {
warnIfNotCurrentlyActingEffectsInDEV(currentlyRenderingFiber);
}
}
return mountEffectImpl(
UpdateEffect | PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps,
);
}
useLayoutEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
currentHookNameInDev = 'useLayoutEffect';
mountHookTypesDev();
checkDepsAreArrayDev(deps);
return mountLayoutEffect(create, deps);
},
function mountLayoutEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
return mountEffectImpl(UpdateEffect, HookLayout, create, deps);
}
复制代码
可以看到两个函数最终调用的都是同一个名为 mountEffectImpl 的函数,入参一致,返回值也一致,所以函数签名是相同的。
至于到底使用哪一个,引用官网中说的一句话:我们推荐你一开始先用 useEffect,只有当它出问题的时候再尝试使用 useLayoutEffect。
2.区别
React 社区中最佳实践是这样的,大多数场景下可以直接使用 useEffect,但是如果你的代码引起了页面闪烁,也就是引起了组件突然改变位置、颜色及其他效果等的情况下,就推荐使用 useLayoutEffect 来处理。那么总结起来就是如果有直接操作 DOM 样式或者引起 DOM 样式更新的场景更推荐使用 useLayoutEffect。
总结一下:
它们的共同点很简单,底层的函数签名是完全一致的,都是调用的 mountEffectImpl ,在使用上没什么差异,基本可以直接替换,都是用于处理副作用。
那不同点就很大了,useEffect 在 React 的渲染过程中是被异步调用的,用于绝大多数场景,而 LayoutEffect 会在所有的 DOM 变更之后同步调用,主要用于处理 DOM 操作、调整样式、避免页面闪烁等问题。也正因为是同步处理,所以需要避免在 LayoutEffect 做计算量较大的耗时任务从而造成阻塞。
在未来的趋势上,两个 API 是会长期共存的,暂时没有删减合并的计划,需要开发者根据场景去自行选择。React 团队的建议非常实用,如果实在分不清,先用 useEffect,一般问题不大;如果页面有异常,再直接替换为 useLayoutEffect 即可。
参考: