主题
实现性能优化策略
第23课 实现性能优化策略
本节课代码地址(请参考课程获取) 性能优化的一般思路 性能优化的一般思路:将「变化的部分」与「不变的部分」分离 什么是「变化的部分」?
State
Props
Context命中「性能优化」的组件可以不通过reconcile 生成wip.child ,而是直接复用上次更新 生成的wip.child 。 总结起来有两点: 性能优化的思路是将「变化的部分」与「不变的部分」分离 命中性能优化的组件的子组件(而不是他本身)不需要render 源码内部有哪些性能优化策略?
jsx
function App() {
const [num, update] = useState(0);
console.log("App render ", num);
return (
<div onClick={() => update(1)}>
<Cpn />
</div>
);
}
function Cpn() {
console.log("cpn render");
return <div>cpn</div>;
}在线示例地址 对于上述例子,存在两种性能优化策略:
bailout策略:减少不必要的子组件rendereagerState策略:不必要的更新,没必要开启后续调度流程 ⚠️ 性能优化策略的实现没有兼容Suspense
bailout策略
命中「性能优化」(bailout 策略)的组件可以不通过reconcile 生成wip.child ,而是 直接复用上次更新生成的wip.child 。 bailout 策略存在于beginWork 中 bailout 四要素:
props不变 比较props变化是通过「全等比较」,使用React.memo后会变为「浅比较」state不变 两种情况可能造成state不变: 不存在update存在update,但计算得出的state没变化context不变type不变 如果Div变为P,返回值肯定变了 为了判断「bailout四要素」中的「state不变」,需要判断当前fiber是否存在未执行的
update 。fiber.lanes工作流程
作用:保存一个fiberNode 中「所有未执行更新对应的lane」 延伸功能:fiber.childLanes ,保存一个fiberNode 子树中「所有未执行更新对应的 lane」
产生:enqueueUpdate
消费:beginWork未消费时的重置:processUpdateQueue
eagerState策略
状态更新前后没有变化,那么没有必要触发更新,为此需要做:
- 计算更新后的状态
- 与更新前的状态做比较 通常情况下,「根据update计算state」发生在
beginWork,而我们需要在「触发更新时」 计算状态: 只有满足「当前fiberNode没有其他更新」才尝试进入eagerState策略。
实现React.memo
demo:performance/memo.tsx 作用:让「props的全等比较」变为「props的浅比较」 本质:在子组件与父组件之间增加一个MemoComponent ,MemoComponent 通过「props的 浅比较」命中bailout 策略
实现useMemo、useCallback
demo:performance/Hook.tsx demo:performance/useMemo.tsx useCallback :缓存函数 useMemo :缓存变量(特殊用法:手动bailout )
context兼容bailout的实现
Q:不触发context 更新的原因? A:命中了bailout 策略 Q:在context 场景下,如何才能不命中bailout 策略? A:在context 场景下,可以提前标记从ctx.Provider 到consumer 之间的childLanes 注意:比较state 变化的第二种情况「有新的update,但是经过计算后发现state没变」, 在标记didReceiveUpdate 时对于context 也同样适用。