在使用 memo 之前
关于 React 性能优化的话题, 很多文章都有所涉及. 大部分文章针对 state 更新卡顿的问题, 给出的方案大都是这样的:
- 首先确认代码是否是在生产环境运行. (开发环境的代码大都会比较慢, 在一些极端情况下, 程度更甚.)
- 确认 state 的位置, 是否被置于组件树中过于高的位置. (比如说, 将用户输入的状态放在一个集中管理的 store 中, 就不太合适)
- 使用 React 开发者工具检测组件重新渲染的次数, 将开销比较大的组件用
memo()
进行包裹. (同时组件内部的变量可以用useMemo()
缓存下来.)
最后一步比较繁琐, 特别是对于处于组件树中间位置的组件, 理想情况下, 可以使用编译器帮助我们完成这一步的工作. 可能在不久的将来就能实现了.
在本文后续的内容中, 我想分享两种与上面所说的不同的方式. 了解之后你会发现这两种方式其实特别地基础, 也因此很多人甚至忽略了它们对渲染性能优化的效果.
这些方式对比你已经了解的优化技巧, 是互补的内容. 它们并不是 memo
和 useMemo
的替代品, 但是在优化过程中首先尝试这些方式, 是比较可取的.
构造出一个渲染性能比较差的组件
下面的示例代码, 是一个有严重性能问题的组件:
1 | import { useState } from 'react'; |
以上代码的问题是, 无论何时 App
中的 color
变化了, 都会进行重新渲染我们手动构造出的存在延迟效果的组件: <ExpensiveTree/>
.
我可以简单地将用 memo()
对这个组件进行包裹, 然后结束我们的优化, 但是这一类优化技巧, 很多其他文章都已经讲得很详细了, 因此我们不会过多着墨于此. 接下来我想介绍的是另外两种优化方式.
解决方案 1 - 将 State 移动到内部
仔细观察渲染的代码, 会发现其实只有高亮的部分关注 color
的值:
1 | export default function App() { |
我们将这部分抽象成 Form
组件, 然后再把状态移到 From
中:
1 | export default function App() { |
经过改造之后, 当 color
变化, 只有 Form
会重新渲染. 问题解决了.
解决方案 2 - 将内容移动到外部
如果 state 在 ExpensiveTree
外部被消费, 然后将 color
属性设置到父级 <div>
上, 就无法使用以上的优化方式了:
1 | export default function App() { |
由于父组件也用到了 color
属性, 我们不能够像先前那样将消费了 color
属性的组件额外抽象出来. 那这样的话, 就只能使用 memo
进行优化了吗?
其实还有另一种简单的方式:
1 | export default function App() { |
将 App
组件拆分成两部分, 把 color
属性相关的逻辑都移到 ColorPicker
中.
这样抽象出来之后, color
相关的部分依然存在于 App
中, 不消费 color
的部分作为 children
被传入了 ColorPicker
中.
当 color
属性变化的时候, ColorPicker
会重新渲染. 但是由于它接受到的 children
属性和前一次渲染没有差别, 因此 children
的内容不会重新渲染.
这样一来, <ExpensiveTree/>
就不会重新渲染.
我想要表达什么
在使用 memo
或者 useMemo
对组件进行优化的时候, 或许可以先重新审视一下我们的代码, 将经常会变化的部分, 和比较不经常变化的部分进行拆分.
比较有意思的一点是, 这样拆分的最终目的其实并不是为了性能优化考虑. 使用 children
属性分离组件, 能够使得我们应用的数据流更加简洁, 同时减少传入组件的 props. 在这类场景下, 提升性能其实是意外的收获.
更惊喜的是, 这样的模式在未来能够带来更大的性能收益.
举个例子, 当 Sever Components 成为 React 的稳定特性之后, 读取 children
属性的工作就能够在在服务端进行. <ExpensiveTree />
也能够在服务端执行, 这样一来, 即使在此层级之上的 state 更新, 在客户端也不会引起 <ExpensiveTree />
那部分代码的执行.
这是 memo
没办法帮我们做到的. 但是重申一次, 我所说的这两种方法需要配合 memo
和 useMemo
使用, 一定不要忘了移动状态 (state) 和内容.
同时记得, 打开 React 开发者工具观察 memo 的优化效果.
类似的文章
看完全文之后, 读者可能会觉得, 以前好像看到过类似的文章? 确实是的.
这并不是一个全新的概念. 是 React 作为组合模型所演变出的合理优化结果. 或许是因为方式太过简单, 被很多人忽略了, 但是其实这是个值得被重视的方法.