Skip to content

实现性能优化策略

第23课 实现性能优化策略

本节课代码地址(请参考课程获取) 性能优化的一般思路 性能优化的一般思路:将「变化的部分」与「不变的部分」分离 什么是「变化的部分」?

State
Props
Context

命中「性能优化」的组件可以不通过reconcile 生成wip.child ,而是直接复用上次更新 生成的wip.child 。 总结起来有两点: 性能优化的思路是将「变化的部分」与「不变的部分」分离 命中性能优化的组件的子组件(而不是他本身)不需要render 源码内部有哪些性能优化策略?

jsx
function App() {

const [numupdate= useState(0);

console.log("App render ", num);

return (

<div onClick={() => update(1)}>

<Cpn />

</div>

);

}
function Cpn() {

console.log("cpn render");

return <div>cpn</div>;

}

在线示例地址 对于上述例子,存在两种性能优化策略:

  1. bailout 策略:减少不必要的子组件render
  2. eagerState 策略:不必要的更新,没必要开启后续调度流程 ⚠️ 性能优化策略的实现没有兼容Suspense

bailout策略

命中「性能优化」(bailout 策略)的组件可以不通过reconcile 生成wip.child ,而是 直接复用上次更新生成的wip.childbailout 策略存在于beginWorkbailout 四要素:

  1. props 不变 比较props 变化是通过「全等比较」,使用React.memo 后会变为「浅比较」
  2. state 不变 两种情况可能造成state 不变: 不存在update 存在update ,但计算得出的state 没变化
  3. context 不变
  4. type 不变 如果Div 变为P ,返回值肯定变了 为了判断「bailout四要素」中的「state不变」,需要判断当前fiber 是否存在未执行的
update 。

fiber.lanes工作流程

作用:保存一个fiberNode 中「所有未执行更新对应的lane」 延伸功能:fiber.childLanes ,保存一个fiberNode 子树中「所有未执行更新对应的 lane」

产生:enqueueUpdate
消费:beginWork

未消费时的重置:processUpdateQueue

eagerState策略

状态更新前后没有变化,那么没有必要触发更新,为此需要做:

  1. 计算更新后的状态
  2. 与更新前的状态做比较 通常情况下,「根据update计算state」发生在beginWork ,而我们需要在「触发更新时」 计算状态: 只有满足「当前fiberNode没有其他更新」才尝试进入eagerState 策略。

实现React.memo

demo:performance/memo.tsx 作用:让「props的全等比较」变为「props的浅比较」 本质:在子组件与父组件之间增加一个MemoComponentMemoComponent 通过「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.Providerconsumer 之间的childLanes 注意:比较state 变化的第二种情况「有新的update,但是经过计算后发现state没变」, 在标记didReceiveUpdate 时对于context 也同样适用。

用心学习,用代码说话 💻