前言
组件是构成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)
}
//点击按钮设置传入组件的值为2
updateBaseClass(){
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; // false
funcA == 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=2
this.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;