开新坑,看源码。
在新公司帮别人重构了两次代码,发现改(吐槽)别人的代码还是蛮有意思的一件事。正好需要找一些社区的最佳实践,于是过来看一些源码
https://mp.weixin.qq.com/s/aEfkRrSNymsiXzdvpNRm5Q
源码分析库:https://github.com/7kms/react-illustration-series
组件加载时
export const UpdateEffect = /* */ 0b000000000000100; // 4
export const PassiveEffect = /* */ 0b000001000000000; // 512
export const HookHasEffect = /* */ 0b001; // 1
export const HookLayout = /* */ 0b010; // 2
export const HookPassive = /* */ 0b100; // 4
function mountEffect(create, deps) {
return mountEffectImpl(UpdateEffect | PassiveEffect, HookPassive, create, deps);
}
function mountLayoutEffect(create, deps) {
return mountEffectImpl(UpdateEffect, HookLayout, create, deps);
}
function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber.effectTag |= fiberEffectTag; // --> 2 |= 516 / 2 |= 4
hook.memoizedState = pushEffect(HookHasEffect | hookEffectTag, create, undefined, nextDeps);
}
组件更新时
function updateEffect(create, deps) {
return updateEffectImpl(UpdateEffect | PassiveEffect, HookPassive, create, deps);
}
function updateLayoutEffect(create, deps) {
return updateEffectImpl(UpdateEffect, HookLayout, create, deps);
}
function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
// 1、拿到上一次 Effect 回调函数执行后返回的销毁函数(如果有的话)
destroy = prevEffect.destroy;
// 2、比较依赖项是否发生变化,如果相等,不需要重新触发回调,此时调用pushEffect创建一个effect加入到fiber.updateQueue上
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
pushEffect(hookEffectTag, create, destroy, nextDeps);
return;
}
}
}
// 3、如果依赖不相等,标记 effectTag,并调用 pushEffect 创建 effect 并作为 hook.memoizedState
currentlyRenderingFiber.effectTag |= fiberEffectTag;
hook.memoizedState = pushEffect(
HookHasEffect | hookEffectTag,
create,
destroy,
nextDeps,
);
}
function areHookInputsEqual(nextDeps, prevDeps) {
if (prevDeps === null) {
return false;
}
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (is(nextDeps[i], prevDeps[i])) { // Object.is 比较两个值是否相等
continue;
}
return false;
}
return true;
}
这两个 Effect 的实现原理,其实就是在组件 Render 阶段将 hook 对应的 effect 绑定到组件 Fiber 的 updateQueue 更新队列之上,并给组件标记 effectTag
其实在 commit 阶段,会对这两个 hook 的回调执行调用。由于 commit 阶段又分为三个小阶段:before Mutation、Mutation、Layout三个阶段
Mutation阶段只针对于 Hooks 中的 useLayoutEffect 处理
对于 useLayoutEffect hook,以同步的方式执行。
对于 useEffect hook,以异步调度的方式执行。
https://juejin.cn/post/6951754466009825310#heading-3