前言
组件是构成React视图的一个基本单元。有些组件会有自己本地的状态(state), 有些组件依赖传入的参数(props)。当用户的操作而发生改变时,组件就会重新渲染。在一个React应用中,一个组件可能会被频繁地进行渲染。有些无用的重复的渲染可能在你不知不觉中进行着,当组件越来越大时,它们的存在会大大降低我们应用的性能。
本此将介绍React16.6加入的专门用来优化函数组件(Functional Component)性能的方法:React.memo。
无用的渲染
我们来看一个类组件例子
//子组件import React from 'react';class TestC extends React.Component {constructor(props) {super(props);}componentDidUpdate(prevProps, prevState) {console.log('componentDidUpdate')}render() {return (<div>{ this.state.count }</div>);}}export default TestC;//父组件import Test from './components/test'import React from 'react'class App extends React.Component {constructor() {super()this.state = {count: 1,}this.updateBaseClass = this.updateBaseClass.bind(this)}//点击按钮设置传入组件的值为2updateBaseClass(){this.setState({count:2})}render() {return (<div><Test count={this.state.count}></Test><button onClick={this.updateBaseClass}>update</button></div>);}}export default App;
当我们点击Click按钮时,count的值被设置为2。这时候屏幕的数字将会由1变成2,组件重新渲染了,没有问题。当我们再次点击该按钮时,count的值还是2,这时候其实我们主观上并不想让TestC组件重新渲染,事实是每次点击他都会重新渲染。
类组件解决方案
pureComponent
React在v15.5的时候引入了Pure Component组件。React在进行组件更新时,如果发现这个组件是一个PureComponent,它会将组件现在的state和props和其下一个state和props进行浅比较,如果它们的值没有变化,就不会进行更新。要想让你的组件成为Pure Component,只需要extends React.PureComponent即可。
//子组件import React from 'react';class TestC extends React.PureComponent {constructor(props) {super(props);}componentDidUpdate(prevProps, prevState) {console.log('componentDidUpdate')}render() {return (<div>{ this.state.count }</div>);}}export default TestC;
函数组件
自React v16.8推出React hooks版本后,函数式组件也成为我们日常开发的选择之一。对于函数组件,我们不能像在类组件中使用shouldComponentUpdate等生命函数去控制函数组件的重渲染,更不能继承React.PureComponent。
我们先将类组件写成函数组件
import React from 'react'const TestC = (props) => {console.log('Rendering TestC :', props)return (<span>{ props.count }</span>)}export default TestC
在count值变与不变的情况,TestC都会去重新渲染,下面我们用 React.memo() 就改造一下他。
import React from 'react'const TestC = (props) => {console.log('Rendering TestC :', props)return (<span>{ props.count }</span>)}export default React.memo(TestC);
React.memo会返回一个纯化(purified)的组件 MemoFuncComponent,这个组件将会在JSX标记中渲染出来。当组件的参数props和状态state发生改变时,React将会检查前一个状态和参数是否和下一个状态和参数是否相同,如果相同,组件将不会被渲染,如果不同,组件将会被重新渲染。
引用类型的变化导致React组件重新渲染
引用类型
“引用类型”是一个 JavaScript 程序员必须烂熟于心的概念,如果无法分辨这个概念,会无意中写出很多损耗性能的代码。有几种我们常见的引用类型,分别是对象(object)、数组(array)和函数(function)。
//对象{ name: 'a' } === { name: 'a' }; // false{ name: 'a' } == { name: 'a' }; // false//数组[1, 2] === [1, 2]; // false[1, 2] == [1, 2]; // false//函数const funcA = () => "hello";const funcB = () => "hello";funcA === funcB; // falsefuncA == funcB; // false
来看一段代码
//子组件import React from 'react'let TestC = (props) => {console.log('Rendering Test2 :', props)return (<span>{ props.params[0].baseClass }</span>)}function areEqual(prevProps, nextProps) {console.log(prevProps, nextProps,prevProps==nextProps)return false}export default React.memo(TestC);//父组件import Test from './components/test'import Test2 from './components/test2'import React from 'react'class App extends React.Component {constructor() {super()this.state = {//查看 基础类型和引用类型 传入组件的更新情况baseClass: 1,objClass: [{ baseClass: 1 }]}this.updateBaseClass = this.updateBaseClass.bind(this)this.updateObjClass = this.updateObjClass.bind(this)}updateBaseClass(){this.setState({baseClass:2})}updateObjClass(){const a = [...this.state.objClass]a[0].baseClass=2this.setState({objClass:a})}render() {return (<div><div style={{marginBottom:'20px'}}><h3 style={{margin:0,display:'inline'}}>展示值:</h3><Test params={this.state.baseClass}></Test><button onClick={this.updateBaseClass}>updateBaseClass</button></div><div><h3 style={{margin:0,display:'inline'}}>展示值:</h3><Test2 params={this.state.objClass}></Test2><button onClick={this.updateObjClass}>updateObjClass</button></div></div>);}}export default App;
