3

我们一般做React性能优化,有两条思路
1、验证是否将树中的状态放在了一个比实际所需更高的位置上。(例如,将输入框的state放到了集中的store里,这样并不合理)
2、运行React开发者工具来检测是什么导致了二次渲染,以及在高开销的子树上包裹memo()。(以及在需要的地方使用useMemo())
除了上面两条思路外,分享两个基础的小技巧😊来提升渲染性能:

这里是一个具有严重渲染性能问题的组件(当App中的color变化时,我们会重新渲染一次被<ExpensiveTree />组件)

import { useState } from 'react';

export default function App() {
  let [color, setColor] = useState('red');
  return (
    <div>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p style={{ color }}>Hello, world!</p>
      <ExpensiveTree />
    </div>
  );
}

function ExpensiveTree() {
  let now = performance.now();
  while (performance.now() - now < 100) {
    // Artificial delay -- do nothing for 100ms
  }
  return <p>I am a very slow component tree.</p>;
}

用我们常用的思路给ExpensiveTree上价格memo()就可以解决此问题。
接下来就说说我们的技巧吧。

技巧1: 向下移动State

仔细看一下渲染代码,可以注意到返回的树中只有一部分真正关心当前的color
所以让我们把这一部分提取到Form组件中然后将state移动到该组件里:

export default function App() {
  return (
    <>
      <Form />
      <ExpensiveTree />
    </>
  );
}

function Form() {
  let [color, setColor] = useState('red');
  return (
    <>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p style={{ color }}>Hello, world!</p>
    </>
  );
}

现在如果color变化了,只有Form会重新渲染,问题解决了。

技巧 2:内容提升

当一部分state在高开销树的上层代码中使用时上述解法就无法奏效了。比吐将color放到父元素div中使用(<div style={{ color }}>)。

export default function App() {
  let [color, setColor] = useState('red');
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p>Hello, world!</p>
      <ExpensiveTree />
    </div>
  );
}

没办法将不使用color的部分提取到另一个组件中了,因为color要在ExpensiveTree的父组件div中使用。
我们可以将App组件分割为两个子组件。依赖color的代码就和color state变量一起放入ColorPicker组件里。 不关心color的部分就依然放在App组件中,然后以JSX内容的形式传递给ColorPicker,也被称为children属性。 当color变化时,ColorPicker会重新渲染。但是它仍然保存着上一次从App中拿到的相同的children属性,所以React并不会访问那棵子树。 因此,ExpensiveTree不会重新渲染。

export default function App() {
  return (
    <ColorPicker>
      <p>Hello, world!</p>
      <ExpensiveTree />
    </ColorPicker>
  );
}

function ColorPicker({ children }) {
  let [color, setColor] = useState("red");
  return (
    <div style={{ color }}>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      {children}
    </div>
  );
}

这两种技巧和memo是互补的,可以根据实际场景来决定使用哪种方式提升渲染性能。


小盼田
0 声望9 粉丝

程序媛