本篇文章带大家了解一下Memoization,介绍一下为什么需要 Memoization,以及 React中实现 Memoization以提高性能的方法,希望对大家有所帮助!
|
本篇文章带大家了解一下Memoization,介绍一下为什么需要 Memoization,以及 React中实现 Memoization以提高性能的方法,希望对大家有所帮助!
我们将介绍以下内容:
本文假设你对 React 中的类和函数组件有基本的了解。
React 是如何渲染视图的?在讨论 React 中的 Memoization 细节之前,让我们先来看看 React 是如何使用虚拟 DOM 渲染 UI 的。【相关推荐:Redis视频教程】 常规 DOM 基本上包含一组用树的形式保存的节点。DOM 中的每个节点代表一个 UI 元素。每当应用程序中出现状态变更时,该 UI 元素及其所有子元素的相应节点都会在 DOM 树中更新,然后会触发 UI 重绘。 在高效的 DOM 树算法的帮助下,更新节点的速度更快,但重绘的速度很慢,并且当该 DOM 具有大量 UI 元素时,可能会影响性能。因此,在 React 中引入了虚拟 DOM。 这是真实 DOM 的虚拟表示。现在,每当应用程序的状态有任何变化时,React 不会直接更新真正的 DOM,而是创建一个新的虚拟 DOM。然后 React 会将此新的虚拟 DOM 与之前创建的虚拟 DOM 进行比较,找到有差异的地方(译者注:也就是找到需要被更新节点),然后进行重绘。 根据这些差异,虚拟 DOM 能更高效地更新真正的 DOM。这样提高了性能,因为虚拟 DOM 不会简单地更新 UI 元素及其所有子元素,而是有效地仅更新实际 DOM 中必要且最小的更改。 为什么需要 Memoization?在上一节中,我们看到了 React 如何使用虚拟 DOM 有效地执行 DOM 更新操作来提高性能。在本节中,我们将介绍一个例子,该例子解释了为了进一步提高性能而需要使用 Memoization。 我们将创建一个父类,包含一个按钮,用于递增名为 //Parent.js
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
handleClick = () => {
this.setState((prevState) => {
return { count: prevState.count + 1 };
});
};
render() {
console.log("Parent render");
return (
<div className="App">
<button onClick={this.handleClick}>Increment</button>
<h2>{this.state.count}</h2>
<Child name={"joe"} />
</div>
);
}
}
export default Parent;此示例的完整代码可在 CodeSandbox 上查看。 我们将创建一个 //Child.js
class Child extends React.Component {
render() {
console.log("Child render");
return (
<div>
<h2>{this.props.name}</h2>
</div>
);
}
}
export default Child;每当我们点击父组件中的按钮时, 传递给子组件的参数在每次父组件重新渲染时都没有改变,因此子组件不应重新渲染。然而,当我们运行上面的代码并继续递增 Parent render Child render Parent render Child render Parent render Child render 你可以在这个 sandbox 中体验上述示例,并查看控制台的输出结果。 从输出中,我们可以看到,当父组件重新渲染时,即使传递给子组件的参数保持不变,子组件也会重新渲染。这将导致子组件的虚拟 DOM 与以前的虚拟 DOM 执行差异检查。由于我们的子组件中没有变更且重新渲染时的所有 props 都没有变,所以真正的 DOM 不会被更新。 真正的 DOM 不会进行不必要地更新对性能确实是有好处,但是我们可以看到,即使子组件中没有实际更改,也会创建新的虚拟 DOM 并执行差异检查。对于小型 React 组件,这种性能消耗可以忽略不计,但对于大型组件,性能影响会很大。为了避免这种重新渲染和虚拟 DOM 的差异检查,我们使用 Memoization。 React 中的 Memoization在 React 应用的上下文中,Memoization 是一种手段,每当父组件重新渲染时,子组件仅在它所依赖的 props 发生变化时才会重新渲染。如果子组件所依赖的 props 中没有更改,则它不会执行 render 方法,并将返回缓存的结果。由于渲染方法未执行,因此不会有虚拟 DOM 创建和差异检查,从而实现性能的提升。 现在,让我们看看如何在类和函数组件中实现 Memoization,以避免这种不必要的重新渲染。 类组件实现 Memoization为了在类组件中实现 Memoization,我们将使用 React.PureComponent。 将子组件更改为如下所示的代码: //Child.js
class Child extends React.PureComponent { // 这里我们把 React.Component 改成了 React.PureComponent
render() {
console.log("Child render");
return (
<div>
<h2>{this.props.name}</h2>
</div>
);
}
}
export default Child;此示例的完整代码显示在这个 sandbox 中。 父组件保持不变。现在,当我们在父组件中增加 Parent render Child render Parent render Parent render 对于首次渲染,它同时调用父组件和子组件的 对于每次增加 函数组件实现 Memoization为了在函数组件中实现 Memoization,我们将使用 React.memo()。 以下是函数组件的代码: //Child.js
export function Child(props) {
console.log("Child render");
return (
<div>
<h2>{props.name}</h2>
</div>
);
}
export default React.memo(Child); // 这里我们给子组件添加 HOC 实现 Memoization同时还将父组件转换为了函数组件,如下所示: //Parent.js
export default function Parent() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
console.log("Parent render");
return (
<div>
<button onClick={handleClick}>Increment</button>
<h2>{count}</h2>
<Child name={"joe"} />
</div>
);
}此示例的完整代码可以在这个 sandbox 中看到。 现在,当我们递增父组件中的 Parent render Child render Parent render Parent render Parent render React.memo() 存在的问题在上面的示例中,我们看到,当我们对子组件使用 但是,需要注意的一个小问题是,如果我们将函数作为参数传递给子组件,即使在使用 我们将更改父组件,如下所示。在这里,我们添加了一个处理函数,并作为参数传递给子组件: //Parent.js
export default function Parent() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
const handler = () => {
console.log("handler"); // 这里的 handler 函数将会被传递给子组件
};
console.log("Parent render");
return (
<div className="App">
<button onClick={handleClick}>Increment</button>
<h2>{count}</h2>
<Child name={"joe"} childFunc={handler} />
</div>
);
}子组件代码将保持原样。我们不会在子组件中使用父组件传递来的函数: //Child.js
export function Child(props) {
console.log("Child render");
return (
<div>
<h2>{props.name}</h2>
</div>
);
}
export default React.memo(Child);现在,当我们递增父组件中的 那么,是什么原因导致子组件重新渲染的呢?答案是,每次父组件重新渲染时,都会创建一个新的 接下来,我们将介绍如何解决此问题。 通过 |
