React 应用中性能优化的经验(二)
阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。
上回说道在 React 应用中列表组件应该去优化,今天复杂组件应该怎么写。Jotai 和 Zustand 咕咕咕了,下次再说。
写过大厂屎山的大伙应该都经历过,一个组件能有上百甚至上千行都是常事。一个组件内部嵌套一个组件也是常事。简单总结了下三不要:
- 不要在 Component 内部定义其他 Component
- 不要用 render 的方式渲染 ReactNode, eg.
render(data1, data2, data3) => ReactNode
- 不用 useCallback 定义一个组件
啥意思,简单列举一下上面的错误写法,大家千万不要学:
以上只是非常简单的例子,这样的错误肯定大家都不会犯,因为这个组件很简单,很好拆,怎么会写出这么新手的代码呢。在实际业务场景中,组件复杂度比这个大得多,有的时候好像没有办法去抽离一些逻辑就只能写出这种新手代码了,导致所有的状态全在组件顶层,组件中的组件也会因为顶层组件的 re-render 一直重建,性能是非常低下的。
消除组件内部定义的其他组件
我们定义一个稍微复杂一些的,数据耦合在一起的好像不得不使用组件中定位组件的一个业务组件,然后对它进行优化。
假设我们有一个OrderForm
组件,它需要根据传入的订单信息动态生成不同的OrderItem
组件。这个时候,你可能会想在OrderForm
中定义OrderItem
组件,如下:
错误写法:
但是这样做,每次OrderForm
渲染时,OrderItem
都会被重建,也就意味着这个列表遍历下的所有OrderItem
都在 re-mount 而不是 re-render。在 React 中,创建一个新的组件实例(即组件的首次渲染)通常比更新一个现有的组件实例(即组件的 re-render)要花费更多的时间。
正确的做法是,将OrderItem
组件定义在OrderForm
外面,通过props传递数据:
这样,不管OrderForm
如何渲染,OrderItem
都只会被定义一次,而且它可以根据 props 的改变来决定是否需要重新渲染,从而提高性能。
去除 render 方式传参的组件
有时会看到一些组件是这样定义插槽的。比如下面的 Table 的组件。
上面的写法也不是很好,首先是 render 是一个函数返回的一个 ReactNode,所以你在用这个组件的时候,传入的 render 是不能使用 React Hooks 的。例如:
不仅不能使用 Hooks,而且这样的写法会导致 Table 组件re-render后,下面的所有列的 render 全部重建。因为 render 不是一个组件而是一个 ReactNode,不能用作性能优化。
上述的 Table 可以改写成:
这样就可以在定义列组件时使用 Hooks 了并且列组件只受到 data
的影响(前提需要给列组件套上 memo),而且也只需要把原本的 (data1, data2) =>
改写成 ({ data1, data2 })
就行了。
如:
今天先说到这。
上述部分代码和文字由 GPT-4 编写。