React性能优化(一):一文看懂优化原理及方案 🚀

990 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

大家好,我是疯狂的小波,这个系列我们会介绍React性能优化的相关内容,包含为什么要做性能优化、原理、多种实用的解决方案。

当我们开发的项目越来越复杂,或者是对性能要求比较高的时候,就容易遇到性能问题。比较常见的就是当改变某个数据时,视图的更新有延迟或者出现卡顿的情况。那哪些场景下会出现性能问题,我们又应该怎么解决呢?

为什么要做性能优化?

我们来看看一个简单的例子:

function SelectCar() {
  const [brand, setBrand] = useState({}); 
  const [count, setCount] = useState(0);

  return (
    <>
      {count}
      <button onClick={() => setCount(count + 1)}>修改count</button>
      <BrandList brand={brand} />
      <Car />
    </>
  );
}

上述代码中,当点击buttoncount+1SelectCar 组件重新渲染;子组件 BrandListCar 也会重新渲染一次,哪怕此时子组件数据没有任何变化。如果这2个组件内部,还有子组件,也会全部重新渲染。

这种父级组件数据变更,会导致所有子孙组件全部重新渲染。我们可以想象当页面复杂时,会有多大的性能损耗。

除了这种简单的场景,还有很多可能会出现性能问题的原因,接下来我们就从 React的更新渲染机制 来看看可以从哪些方向进行相关的优化。

React的更新渲染机制

想要做好性能优化,我们就需要先了解下 React 的更新渲染机制,了解哪些场景会触发更新,更新内容如何判定,这样才能做到有的放矢,针对性的进行优化。

graph TD
    state数据改变 --> 根据最新的数据生成新节点 --> 新节点与上次渲染时VDOM树对比-diff --> 找出差异内容并打标 --> 生成最新的VDOM树  --> 差异内容渲染到真实DOM

如上图所示,React 采用的是虚拟DOM (即 VDOMReact中也叫Fiber)。每次state数据发生变化的时候,React 会检测当前最新的节点和上次渲染的Fiber树之前的差异,然后针对差异的地方进行打标,返回最新的Fiber树,最后将所有差异内容渲染到 真实DOM。这就是整个 更新渲染 的大概过程。

为了获得更优秀的性能,减少更新花费的时间、提高效率。首先映入脑海的便是 减少 diff 的过程,以及减少前后 VDOM 树的差异性,提高 diff 的效率,那么在保证应该更新的节点能够得到更新的前提下,怎么来实现呢?

性能优化方案

减少 diff 的过程我们可以通过 将可变部分与不变部分分离使用性能优化API 这2种方式优化,减少非必要的重渲染。

减少前后 VDOM 树的差异性,提高 diff 的效率,我们可以根据 React diff算法 的基本原则来进行优化。

也就是如下3个方案:

一、将可变部分与不变部分分离

React 中,只要组件的state数据有变更,当前组件及其组件就会重新渲染。而将数据会变动的内容单独拆分到一个独立的组件中,这样就不会影响到父节点和其他的同级节点。

详情查看:《React性能优化(二):将可变部分与不变部分分离 🚀》

二、使用性能优化API

React 内部,props 判断是否变更默认是使用的全等比较。而每次父组件重新渲染时,子组件的 props 都是生成的一个新对象,全等判断前后 props 不相等。所以导致父组件每次更新,子组件也会重新渲染。

而使用 React.memo 包裹组件后,会将 props 对象的全等比较,修改为 props 中属性值的比较。 这样我们就能够控制这种非必要的更新了。

详情查看:《React性能优化(三):使用性能优化API 🚀》

三、提高页面渲染效率

当组件数据更新,组件重新渲染时,可以通过减少前后 VDOM 树的差异性,提高 diff 的效率。

基于Reactdiff算法 的3个基本原理,我们可以通过多种方式提高页面渲染的效率。

详情查看:《React性能优化(四):提高页面渲染效率 🚀》

方案落地

在后面的章节中,会分别针对这3种方案进行说明,包括具体的实现方法,代码示例,以及其背后的原理。

通过以上几种方式优化后,我们就能够避免一些非必要的更新,有效减少更新范围,提高更新时的效率,达到最终的性能优化的目的。让我们的界面更新响应速度越快,运行越流畅。