摘要:函数组件上面我们探讨了如何使用和的方法优化类组件的性能。它的作用和类似,是用来控制函数组件的重新渲染的。其实就是函数组件的。
原文链接: Improving Performance in React Functional Component using React.memo
原文作者: Chidume Nnamdi
译者: 进击的大葱
推荐理由: 本文讲述了开发React应用时如何使用shouldComponentUpdate生命周期函数以及PureComponent去避免类组件进行无用的重渲染,以及如何使用最新的React.memo API去优化函数组件的性能。
React核心开发团队一直都努力地让React变得更快。在React中可以用来优化组件性能的方法大概有以下几种:
组件懒加载(React.lazy(...)和
Pure Component
shouldComponentUpdate(...){...}生命周期函数
本文还会介绍React16.6加入的另外一个专门用来优化函数组件(Functional Component)性能的方法: React.memo。
无用的渲染组件是构成React视图的一个基本单元。有些组件会有自己本地的状态(state), 当它们的值由于用户的操作而发生改变时,组件就会重新渲染。在一个React应用中,一个组件可能会被频繁地进行渲染。这些渲染虽然有一小部分是必须的,不过大多数都是无用的,它们的存在会大大降低我们应用的性能。
看下面这个例子:
import React from "react"; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log("componentWillUpdate") } componentDidUpdate(prevProps, prevState) { console.log("componentDidUpdate") } render() { return ({this.state.count}); } } export default TestC;
TestC组件有一个本地状态count,它的初始值是0(state = {count: 0})。当我们点击Click Me按钮时,count的值被设置为1。这时候屏幕的数字将会由0变成1。当我们再次点击该按钮时,count的值还是1, 这时候TestC组件不应该被重新渲染,可是现实是这样的吗?
为了测试count重复设置相同的值组件会不会被重新渲染, 我为TestC组件添加了两个生命周期函数: componentWillUpdate和componentDidUpdate。componentWillUpdate方法在组件将要被重新渲染时被调用,而componentDidUpdate方法会在组件成功重渲染后被调用。
在浏览器中运行我们的代码,然后多次点击Click Me按钮,你可以看到以下输出:
我们可以看到"componentWillUpdate"和"componentWillUpdate"在每次我们点击完按钮后,都会在控制台输出来。所以即使count被设置相同的值,TestC组件还是会被重新渲染,这些就是所谓的无用渲染。
为了避免React组件的无用渲染,我们可以实现自己的shouldComponentUpdate生命周期函数。
当React想要渲染一个组件的时候,它将会调用这个组件的shouldComponentUpdate函数, 这个函数会告诉它是不是真的要渲染这个组件。
如果我们的shouldComponentUpdate函数这样写:
shouldComponentUpdate(nextProps, nextState) { return true }
其中各个参数的含义是:
nextProps: 组件将会接收的下一个参数props
nextProps: 组件的下一个状态state
因为我们的shouldComponentUpdate函数一直返回true,这就告诉React,无论何种情况都要重新渲染该组件。
可是如果我们这么写:
shouldComponentUpdate(nextProps, nextState) { return false }
因为这个方法的返回值是false,所以React永远都不会重新渲染我们的组件。
因此当你想要React重新渲染你的组件的时候,就在这个方法中返回true,否则返回false。现在让我们用shouldComponentUpdate重写之前的TestC组件:
import React from "react"; class TestC extends React.Component { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log("componentWillUpdate") } componentDidUpdate(prevProps, prevState) { console.log("componentDidUpdate") } shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true } render() { return ({ this.state.count }); } } export default TestC;
我们在TestC组件里添加了shouldComponentUpdate方法,判断如果现在状态的count和下一个状态的count一样时,我们返回false,这样React将不会进行组件的重新渲染,反之,如果它们两个的值不一样,就返回true,这样组件将会重新进行渲染。
再次在浏览器中测试我们的组件,刚开始的界面是这样的:
这时候,就算我们多次点击Click Me按钮,也只能看到两行输出:
componentWillUpdate componentDidUpdate
因为第二次点击Click Me按钮后count值一直是1,这样shouldComponentUpdate一直返回false,所以组件就不再被重新渲染了。
那么如何验证后面state的值发生改变,组件还是会被重新渲染呢?我们可以在浏览器的React DevTools插件中直接对TestC组件的状态进行更改。具体做法是, 在Chrome调试工具中点击React标签,在界面左边选中TestC组件,在界面的右边就可以看到其状态state中只有一个键count,且其值是1:
然后让我们点击count的值1,将其修改为2,然后按回车键:
你将会看到控制台有以下输出:
componentWillUpdate componentDidUpdate componentWillUpdate componentDidUpdate
state的count被改变了,组件也被重新渲染了。
现在让我们使用另外一种方法PureComponent来对组件进行优化。
React在v15.5的时候引入了Pure Component组件。React在进行组件更新时,如果发现这个组件是一个PureComponent,它会将组件现在的state和props和其下一个state和props进行浅比较,如果它们的值没有变化,就不会进行更新。要想让你的组件成为Pure Component,只需要extends React.PureComponent即可。
让我们用PureComponent去改写一下我们的代码吧:
import React from "react"; class TestC extends React.PureComponent { constructor(props) { super(props); this.state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log("componentWillUpdate") } componentDidUpdate(prevProps, prevState) { console.log("componentDidUpdate") } /*shouldComponentUpdate(nextProps, nextState) { if (this.state.count === nextState.count) { return false } return true }*/ render() { return ({ this.state.count }); } } export default TestC;
在上面的代码中,我将shouldComponentUpdate的代码注释掉了,因为React.PureComponent本身就帮我们实现了一样的功能。
改完代码后,我们刷新一下浏览器,然后多次点击Click Me按钮看组件被渲染了多少遍:
由上面的输出可知,我们的component只在state由0变为1时被重新渲染了,后面都没有进行渲染。
上面我们探讨了如何使用PureComponent和shouldComponentUpdate的方法优化类组件的性能。虽然类组件是React应用的主要组成部分,不过函数组件(Functional Component)同样可以被作为React组件使用。
function TestC(props) { return (I am a functional component) }
对于函数组件,它们没有诸如state的东西去保存它们本地的状态(虽然在React Hooks中函数组件可以使用useState去使用状态), 所以我们不能像在类组件中使用shouldComponentUpdate等生命函数去控制函数组件的重渲染。当然,我们也不能使用extends React.PureComponent了,因为它压根就不是一个类。
要探讨解决方案,让我们先验证一下函数组件是不是也有和类组件一样的无用渲染的问题。
首先我们先将ES6的TestC类转换为一个函数组件:
import React from "react"; const TestC = (props) => { console.log(`Rendering TestC :` props) return ({props.count}) } export default TestC; // App.js
当上面的代码初次加载时,控制台的输出是:
同样,我们可以打开Chrome的调试工具,点击React标签然后选中TestC组件:
我们可以看到这个组件的参数值是5,让我们将这个值改为45, 这时候浏览器输出:
由于count的值改变了,所以该组件也被重新渲染了,控制台输出Object{count: 45},让我们重复设置count的值为45, 然后再看一下控制台的输出结果:
由输出结果可以看出,即使count的值保持不变,还是45, 该组件还是被重渲染了。
既然函数组件也有无用渲染的问题,我们如何对其进行优化呢?
解决方案: 使用React.memo()React.memo(...)是React v16.6引进来的新属性。它的作用和React.PureComponent类似,是用来控制函数组件的重新渲染的。React.memo(...) 其实就是函数组件的React.PureComponent。
如何使用React.memo(...)?React.memo使用起来非常简单,假设你有以下的函数组件:
const Funcomponent = ()=> { return (Hiya!! I am a Funtional component) }
我们只需将上面的Funcomponent作为参数传入React.memo中:
const Funcomponent = ()=> { return (Hiya!! I am a Funtional component) } const MemodFuncComponent = React.memo(FunComponent)
React.memo会返回一个纯化(purified)的组件MemoFuncComponent,这个组件将会在JSX标记中渲染出来。当组件的参数props和状态state发生改变时,React将会检查前一个状态和参数是否和下一个状态和参数是否相同,如果相同,组件将不会被渲染,如果不同,组件将会被重新渲染。
现在让我们在TestC组件上使用React.memo进行优化:
let TestC = (props) => { console.log("Rendering TestC :", props) return ({ props.count } > ) } TestC = React.memo(TestC);打开浏览器重新加载我们的应用。然后打开Chrome调试工具,点击React标签,然后选中
组件。 接着编辑一下props的值,将count改为89,我们将会看到我们的应用被重新渲染了:
然后重复设置count的值为89:
这里没有重新渲染!这就是React.memo(...)这个函数牛X的地方!
在我们之前那个没用到React.memo(...)的例子中,count的重复设置会使组件进行重新渲染。可是我们用了React.memo后,该组件在传入的值不变的前提下是不会被重新渲染的。
结论以下是几点总结:
React.PureComponent是银
React.memo(...)是金
React.PureComponent是给ES6的类组件使用的
React.memo(...)是给函数组件使用的
React.PureComponent减少ES6的类组件的无用渲染
React.memo(...)减少函数组件的无用渲染
为函数组件提供优化是一个巨大的进步
我是进击的大葱,关注我的公众号,获取我分享的最新技术推送!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/102669.html
摘要:比如就是一种,它可以用来管理状态返回的结果是数组,数组的第一项是值,第二项是赋值函数,函数的第一个参数就是默认值,也支持回调函数。而之所以输出还是正确的,原因是的回调函数中,值永远指向最新的值,因此没有逻辑漏洞。 1. 引言 如果你在使用 React 16,可以尝试 Function Component 风格,享受更大的灵活性。但在尝试之前,最好先阅读本文,对 Function Com...
想要升职加薪就要努力学习这篇React不能将useMemo设置为默认方法原因详解, 很多朋友都建议可以用 React 这样就不直接默认使用这种memorized呢?还可以让所有资料都缓存~减少渲染 话不多说,直接上。大概就是直接让所有的东西都 默认套上一层useMemo (or 其他的xxx)不就好了? 还真不行~ 你能学到 / 本文框架 memo constMyComponent...
摘要:的返回值将作为的参数,如果返回,则不更新,不能返回或以外的值,否则会警告。在更新之前调用,此时已更新返回值作为的第个参数一般用于获取之前的数据语法是从的返回值,默认是的使用场景一般是获取组建更新之前的滚动条位置。 React16 后的各功能点是多个版本陆陆续续迭代增加的,本篇文章的讲解是建立在 16.6.0 版本上本篇文章主旨在介绍 React16 之后版本中新增或修改的地方,所以对于...
摘要:接收一个属性,这个组件会让后代组件统一提供这个变量值。因此对于同一个对象而言,一定是后代元素。解决方法就是把内联函数提取出来,如下讲了这么多,我们还没有讲到其实我们已经讲完了的工作原理了。 本节主要讲解以下几个新的特性: Context ContextType lazy Suspense 错误边界(Error boundaries) memo 想阅读更多优质文章请猛戳GitHub博...
阅读 509·2023-04-26 01:39
阅读 4405·2021-11-16 11:45
阅读 2570·2021-09-27 13:37
阅读 852·2021-09-01 10:50
阅读 3523·2021-08-16 10:50
阅读 2193·2019-08-30 15:55
阅读 2950·2019-08-30 15:55
阅读 2229·2019-08-30 14:07