React性能优化

49 阅读1分钟

父组件更新一定会引起子组件的更新。但是有些更新是并不需要的。

例如:

function App(){
   const [counter, setCounter] = useState(1);
   return(
      <div>
        counter is {counter}
        <button onClick={() => setCounter(counter + 1)}>+1</button>
        <Comp1 />
        <Comp2 />
      </div>
   )
}

Comp1Comp2不依赖任何App的数据,所以更新是没有必要的。

可以使用React.memo来做到这一点,

const MemoziedComp1 = React.memo(Comp1)
const MemoziedComp2 = React.memo(Comp2)

要注意的是memo实际上和PureComponent一样,也会浅比较props。例如下面这种情况,尽管使用了memo但是组件还是会更新。

function App(){
   const [counter, setCounter] = useState(1);
   return(
      <div>
        counter is {counter}
        <button onClick={() => setCounter(counter + 1)}>+1</button>
        <MemoziedComp1 counter={counter} />
        <MemoziedComp2 counter={counter} />
      </div>
   )
}

可以用给memo传递第二参数来解决

const MemoziedComp1 = React.memo(Comp1, () => false)
const MemoziedComp2 = React.memo(Comp2, () => false)

或者

<MemoziedComp1 counter={useMemo(() => counter, [])} />
<MemoziedComp2 counter={useMemo(() => counter, [])} />

第二个参数和类组件的shouldComponentUpdate类似,返回一个布尔决定是否要更新。但是我并不推荐第二参数固定返回false, 因为这样没有意义,我这里也只是举个例子。


再看一个例子

function App(){
   const [counter, setCounter] = useState(1);
   const test = () => {
      // do something...
   }
   return(
      <div>
        counter is {counter}
        <button onClick={() => setCounter(counter + 1)}>+1</button>
        <MemoziedComp1 test={test} />
        <MemoziedComp2 test={test} />
      </div>
   )
}

这个情况当counter更新时虽然没有依赖counter,但是MemoziedComp1MemoziedComp2依然会被更新。因为函数组件的更新就是将函数组件重新执行一遍就像App(props)一样。函数每次执行就会生成新的执行器上下文,也就是说更新前的test和更新后的test不是同一个test,所以子组件会更新。针对这种情况我们需要用到useCallback来将test缓存。

function App(){
   const [counter, setCounter] = useState(1);
   const memorizedTest = useCallback(function test() {
      // do something...
   }, [])
   return(
      <div>
        counter is {counter}
        <button onClick={() => setCounter(counter + 1)}>+1</button>
        <MemoziedComp1 test={memorizedTest} />
        <MemoziedComp2 test={memorizedTest} />
      </div>
   )
}