React学习: React性能优化

React性能优化,你需要知道的一切
更新于: 2022-07-03 23:05:42

Q&A

  • 为什么 React需要性能优化API
    • 因为默认情况,react-tree 中的一个组件更新,会触发 root.render 来渲染整个组件更新
    • 为了让上面的更新更加的效率,有时候,我们需要显式的让 React 知道哪些是没有变化的,从而直接跳过Diff,更新环节
    • 即使更新整棵 tree,没有变化的也是直接跳过,所以,性能也没有什么大问题
  • React性能优化应该遵循的原则
    • 变的部分不变的部分分离
    • 变的部分:
      • props
      • state
      • context
    • 3部分都是由 state 衍生出来的
  • 原理
    • 因为如果 state/props/context 不变,VIEW就不应该发生变化;
    • 不变,性能就是最好的。
    • 性能优化的本质:让不应该变化的 state 保持不变,并让 React 知道

场景1

平级组件之间存在:变化 与不变 的场景,直接分离变化,让 Entry 组件为没有任何 state/props/context 的组件

  • 优化前
  • 优化后
  • 这个是不使用性能优化API的例子
import { useState } from 'react';

export default () => {
  const [num, setNum] = useState(0);
  return (
    <div>
      <input type="number" value={num} onChange={(e) => setNum(+e.target.value)} />
      <p>Number is:{num}</p>
      <ExpensiveCon />
    </div>
  );
};

function ExpensiveCon() {
  let now = performance.now();
  while (performance.now() - now < 100) {}
  console.log('expensive render...');
  return <p>耗时的组件....</p>;
}
import { useState } from 'react';

export default () => {
  return (
    <div>
      <Input />
      <ExpensiveCon />
    </div>
  );
};

function Input() {
  const [num, setNum] = useState(0);
  return (
    <>
      <input type="number" value={num} onChange={(e) => setNum(+e.target.value)} />
      <p>Number is:{num}</p>
    </>
  );
}

function ExpensiveCon() {
  let now = performance.now();
  while (performance.now() - now < 100) {}
  console.log('expensive render...');
  return <p>耗时的组件....</p>;
}

变化的部分

function Input() {
  const [num, setNum] = useState(0);
  return (
    <>
      <input type="number" value={num} onChange={(e) => setNum(+e.target.value)} />
      <p>Number is:{num}</p>
    </>
  );
}

场景2

父子组件(容器)之间存在:变化 与不变 的场景,直接分离变化,让 Entry 组件为没有任何 state/props/context 的组件

  • 优化前
  • 优化后
  • 不使用性能优化,将children 不变抽离
import { useState } from 'react';

export default () => {
  const [num, setNum] = useState(0);
  return (
    <div data-num={num}>
      <input type="number" value={num} onChange={(e) => setNum(+e.target.value)} />
      <p>Number is:{num}</p>
      <ExpensiveCon />
    </div>
  );
};

function ExpensiveCon() {
  let now = performance.now();
  while (performance.now() - now < 100) {}
  console.log('expensive render...');
  return <p>耗时的组件....</p>;
}
import React, { useState } from 'react';

export default () => {
  return (
    <InputWrapper>
      <ExpensiveCon />
    </InputWrapper>
  );
};

function InputWrapper({ children }: { children: React.ReactNode }) {
  const [num, setNum] = useState(0);
  return (
    <div data-num={num}>
      <input type="number" value={num} onChange={(e) => setNum(+e.target.value)} />
      <p>Number is:{num}</p>
      {children}
    </div>
  );
}

function ExpensiveCon() {
  let now = performance.now();
  while (performance.now() - now < 100) {}
  console.log('expensive render...');
  return <p>耗时的组件....</p>;
}

如何判断 state/props/context 不变呢?

为什么需要性能优化 API,因为有时候会出现 children{} 造成重复渲染的情况。

  • 如何比较:全等,比较高效,但不易命中
  • 浅对比:低效,易命中

ps: React 组件的 children,即使没有值,也会有 {} 这个产生

没有属性的时候,还是会有一个空对象
react 源码中的部分,会认为有变化,会渲染

总结,为什么需要性能优化

  • React 的子组件变化会导致整体重新渲染
  • 但如果优化得当,很多不变的节点,可以直接跳过渲染
  • 最终每次重新渲染的情况,只会渲染变化的
  • 保证 React 性能非常的 ok

参考