1.浅比较策略
React 中的浅比较策略是用于确定何时重新渲染组件的一种机制。该策略通过比较新旧属性(props)和状态(state)的引用来决定是否需要触发组件的重新渲染。以下是关于 React 浅比较策略的详细解释:
- 引用比较:React 不会深度递归比较属性和状态对象的内部值。相反,它只比较对象的引用。这意味着如果新旧属性或状态的引用相同,React 认为数据没有发生变化,因此不会触发重新渲染。
- 性能优势:浅比较策略是一种性能优化手段。深度比较对象内部的所有值可能会导致性能下降,因此 React 采用了这种轻量级的比较方式,以确保在大多数情况下能够快速确定是否需要重新渲染。
- 何时触发重新渲染:当组件的 props 或 state 发生变化时,React 会首先进行浅比较。如果新旧 props 或 state 的引用不同,React 就会认为数据发生了变化,并触发重新渲染。这将导致调用组件的 render 方法以生成新的虚拟 DOM 树,并将其与旧的虚拟 DOM 树进行比较,从而确定需要更新的部分。
- 何时不触发重新渲染:如果新旧 props 或 state 的引用相同,React 将认为数据没有变化,从而跳过重新渲染。这是因为 React 认为在这种情况下,即使属性或状态的值发生了变化,但由于引用相同,组件的输出不会发生变化,因此没有必要重新渲染。
- 不可变性的重要性:为了正确使用 React 的浅比较策略,通常建议采用不可变性的方式来更新属性和状态。不可变性意味着在更改属性或状态时创建一个新的对象或数组,而不是直接修改现有的对象或数组。这样可以确保新旧数据的引用不同,从而触发重新渲染。
总之,React 的浅比较策略是一种性能优化策略,通过比较属性和状态的引用来确定是否需要重新渲染组件。不可变性是与这一策略配合使用的关键,可以确保组件在数据变化时能够按预期地重新渲染。
2.shallowEqual
shallowEqual 用于比较两个对象是否相等的工具函数。在 React 生态系统中,它通常用于浅比较对象,以确定两个对象是否具有相同的值。shallowEqual 函数通常由 React 的一些内部机制和自定义性能优化中使用,以避免不必要的重新渲染。
function shallowEqual(objA, objB) {
// 如果两个对象引用相同,则它们肯定相等
if (objA === objB) return true;
// 如果两个对象的键的数量不同,它们肯定不相等
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;
// 遍历对象的所有键,比较它们的值是否相等
for (let i = 0; i < keysA.length; i++) {
const key = keysA[i];
if (objA[key] !== objB[key]) return false;
}
return true;
}
上述代码的主要部分:
- 首先,函数检查两个对象是否引用相同的内存地址(即 objA === objB),如果是,则它们肯定相等,直接返回 true。
- 然后,函数获取两个对象的键数组,keysA 和 keysB。
- 如果两个对象的键数量不同(即 keysA.length !== keysB.length),则它们肯定不相等,直接返回 false。
- 最后,函数遍历 keysA 数组,比较对应键的值是否相等。如果有任何键的值不相等,函数将返回 false,否则返回 true。
这种浅比较的方式适用于对象的值是基本数据类型(如数字、字符串、布尔值等)的情况。如果对象的值包含了引用类型,例如嵌套对象或数组,那么浅比较将只比较它们的引用,而不会深入比较它们的内部内容。
在 React 中,shallowEqual 通常用于 shouldComponentUpdate 方法、React.memo 和 useMemo 等场景,以避免不必要的组件重新渲染。这有助于提高 React 应用的性能,因为它可以防止在父组件状态变化时触发不必要的子组件重新渲染。
3.案例
代码实现了一个简单的图书列表应用,可以添加新的书籍。从 React 库中导入了 PureComponent,这是一个继承自 Component 的类,它默认实现了 shouldComponentUpdate 方法,使用浅比较策略来进行性能优化。
在 addNewBook 方法中,准备了一个新的书籍对象 newBook,然后有两种方法来添加这本书到 books 数组中:
- 第一种方法(被注释掉的部分)是直接修改原有的 this.state.books 数组,然后再通过 setState 重新设置 books 数组。这种方法违反了数据不可变性的原则,因为直接在原数组上进行了修改,可能会引发 React 的浅比较策略失效。
- 第二种方法是遵循不变性原则,你首先创建了一个新的数组 books,将旧的 this.state.books 数组的内容通过扩展运算符复制到新数组中,然后将新书 newBook 添加到新数组末尾。最后,你使用 setState 来更新 books 数组,确保在数据更新时创建新的数组引用,触发 React 的浅比较策略。
因此,当下列案例,采用第一种方法进行添加书籍时,点击按钮并没有新增书籍。因为违反了数据不可变性原则,没有触发PureComponent中的浅比较策略。
而采用第二种方法时,由于新旧state的引用发生变化,可以触发浅比较策略。
通过使用不变性原则,可以确保每次数据更新都会创建新的对象或数组引用,从而与 React 的浅比较策略配合,确保组件在数据变化时能够正确地重新渲染。这有助于提高应用的性能和稳定性。
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
books: [
{ name: "JS高级设计", price: 99, count: 1 },
{ name: "React高级设计", price: 78, count: 1 },
{ name: "Node高级设计", price: 82, count: 1 },
],
};
}
addNewBook() {
const newBook = { name: "TypeScript学习手册", price: 70, count: 1 };
// 1.直接修改原有state,重新设置一遍
// this.state.books.push(newBook);
// this.setState({ books: this.state.books });
// 2.遵守不变性原则,新生成一个books
const books = [...this.state.books];
books.push(newBook);
this.setState({ books });
}
addCount(index) {
const books = [...this.state.books];
books[index].count++;
this.setState({ books });
}
render() {
const { books } = this.state;
return (
<div>
<button onClick={(e) => this.addNewBook(e)}>添加书籍</button>
<ul>
{books.map((item, index) => {
return (
<li key={index}>
{item.name}-{item.price}-{item.count}
<button onClick={(e) => this.addCount(index)}>+1</button>
</li>
);
})}
</ul>
</div>
);
}
}
export default App;