在我们使用react进行开发的时候,子组件中经常会出现因为父组件的重新渲染而导致子组件重复渲染的情况。本文将从类组件和函数组件两个方面去介绍如何进行性能优化。
类组件
未优化
父组件
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
status: true,
color: "green"
};
}
render() {
return ( <>
<Child1 status={this.state.status} />
<Child2 color={this.state.color} />
<button onClick={() => {this.setState({status: !this.state.status})}}>切换开关</button>
</>
)
}
}
子组件1
class Child1 extends Component {
constructor(props) {
super(props);
}
render() {
console.log("Child1渲染");
return (<div>
状态:{this.props.status ? "开" : "关"}
</div>)
}
}
子组件2
class Child2 extends Component {
constructor(props) {
super(props);
}
render() {
console.log("Child2渲染");
return (<div>
颜色:{this.props.color}
</div>)
}
}
结果
当你点击按钮修改了status时,父组件重新渲染,那么两个子组件也会渲染,每点击一次按钮,控制台就会打印两个子组件的log。
这里由于我们只更新了status,那么不依赖status的子组件2不重新渲染才是我们想要的。
shouldComponentUpdate改造
了解react生命周期的话你会知道,当定义了shouldComponentUpdate方法,则是否重新渲染组件就由方法返回的结果决定了,因此对子组件2做如下改动即可
子组件2
class Child2 extends Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
return this.props.color !== nextProps.color;
}
render() {
console.log("Child2渲染");
return (<div>
颜色:{this.props.color}
</div>)
}
}
PureComponent改造
react提供了一个PureComponent,简单来说和Component的区别在于PureComponent帮你处理了shouldComponentUpdate事件去进行性能优化,当props或state发生变化,PureComponent会对两者都做浅比较
浅比较
react内部的一个方法shallowEqual
源码如下:
function is(x, y) {
return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
;
}
var objectIs = typeof Object.is === 'function' ? Object.is : is;
var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA, objB) {
if (objectIs(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
} // Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty$2.call(objB, keysA[i]) || !objectIs(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
可以看到shallowEqual是基于Object.is来进行的一个比较,并且如果props是object的话只会去比较第一层
那为什么不用 == 或者 === 去做比较呢?
原因是某些特殊情况这两种方法会得出和我们预期不一致的结果,比如
+0 === -0 // true
NaN === NaN // false
Object.is则可以得出正确的结果
子组件2
import React, {Component, PureComponent} from 'react';
class Child2 extends PureComponent {
constructor(props) {
super(props);
}
render() {
console.log("Child2渲染");
return (<div>
颜色:{this.props.color}
</div>)
}
}
hooks
未优化
改造一下类组件的代码
父组件
import React, { useState } from 'react';
function App(){
const [status, setStatus] = useState(true);
const [color, setColor] = useState("green");
return(<>
<Child1 status={status} />
<Child2 color={color} />
<button onClick={() => { setStatus(!status) }}>切换开关</button>
</>)
}
子组件1
function Child1(props){
console.log("Child1渲染");
return (<div>
状态:{props.status ? "开" : "关"}
</div>)
}
子组件2
function Child2(props){
console.log("Child2渲染");
return (<div>
颜色:{props.color}
</div>)
}
结果
同类组件一致都会造成子组件2的重复渲染
memo改造
作用类似于PureComponent
父组件
import React, { useState, memo } from 'react';
const Child22 = memo(Child2);
function App(){
const [status, setStatus] = useState(true);
const [color, setColor] = useState({a: "green"});
return(<>
<Child1 status={status} />
<Child22 color={color} />
<button onClick={() => { setStatus(!status) }}>切换开关</button>
</>)
}
因为React.memo会让整个函数不执行,但当我们不使用React.memo却又想函数组件里面的某些变量不用每次重新声明时,就需要用到useCallback和useMemo了,这里不再赘述用法,官方文档