文章目录
一、笔者述 :
笔者最近在查阅关于 React 生命周期
的相关资料来构建自己的 React 组件的生命体系的时候发现同行们在记录自己对 React 组件及生命周期的理解时,或多或少都提到了有关 React 组件优化
方面的一些思路。这对笔者的帮助实在是不可小觑,在 SPA
模式大行其道的年代,对代码编译的速度与效率的确是一场考验,下面是笔者对 React 组件优化方面的一些探索 :
二、React 组件优化的两种思路 :
1. 使用 React.Component
来构建 React 组件,通过 shouldComponentUpdate
钩子来进行优化
-1). 优化前 :
导语 : 其实 React.Component
是咱们经常使用的,但是大多数情况下。咱们都是草草的完成业务逻辑,可能没有进一步想过 : "当一个父组件有两个 state 属性 【A、B】,A 渲染到当前父组件上,B 注入到子组件中去显示。父组件上有一个 btn ,当点击 btn 的时候在其回调中更新 state 中的 A 属性 : 试想一下此时咱们没有重新向子组件注入新的 B 属性值【就是说 state 中的 B 属性始终没变化】,那么子组件会重新 render 吗 ? "
答案 : 子组件会重新 render
。
结论 : 无论父组件向子组件重新传入的值是否发生变化子组件都会重新 render
。
图解 :
子组件明明没有变化却莫名的被重新 render
了一次,这种结果是咱们不愿意看到的,也是 React 组件可以优化的地方。
代码 :
父组件 :
// 导入 React 官方包
import React from 'react';
// 导入自定义包
import ShowAge from './components/ShowAge';
// 测试 React Component ...
class App extends React.Component {
// 初始化数据
constructor(props) {
super(props);
console.log("react component parent constructor ...");
this.state = {
name: "FruitJ",
age: 22
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component parent getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component parent componentDidMount ...");
}
// btn's click callback
handleClick = () => {
console.log("react component parent click ...");
// update state
this.setState({
name: "LJ",
});
};
render() {
const { name, age } = this.state;
return (
<div className="App">
<h1>Hello React Component, My name is { name }</h1>
<ShowAge age = { age } />
<button onClick = { this.handleClick }>
parent 点我啊 ~
</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log("react component parent shouldComponentUpdate ...");
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component parent getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component parent componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component parent componentWillUnmount ...");
}
}
export default App;
子组件 :
import React, { Component } from 'react'
class ShowAge extends Component {// 定义组件
// 初始化数据
constructor(props) {
super(props);
console.log("react component child constructor ...");
this.state = {
hobby: "coding"
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component child getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component child componentDidMount ...");
}
handleClick = () => {
console.log("react component child handleClick ...");
// update state
this.setState({
hobby: "read source code!"
});
};
render() {
return (
<div>
<h3>props age : { this.props.age }</h3>
<h3>state hobby : { this.state.hobby }</h3>
<button onClick = { this.handleClick }>
child 点我啊 ~
</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log("react component child shouldComponentUpdate ...");
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component child getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component child componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component child componentWillUnmount ...");
}
}
export default ShowAge;
动图 :
-2). 优化后 :
方案一 : 浅比较
导语 : 浅比较的特点 : 仅仅属于表层比较不会触及更深层次的比较 ( 对于基本类型仅比较基本类型值,对于引用类型的比较则永远返回 false【具体原因下面会说到】 ),并且有一点需要言明 : " 笔者认为有些说法言道在 shouldComponentUpdate 周期方法中进行引用类型的比较云云 ... 这里笔者 掷地有声 回答,那种说法是错的 ! 因为 nextProps 与 this.props 、nextState 与 this.state这两对儿的对比者都是不同的两个对象,他们的引用怎么可能相同 ? 别不用说其下挂载的数据了,除了基本类型值其余的都是没法比较的! "
图解 :
代码 :
父组件 :
// 导入 React 官方包
import React from 'react';
// 导入自定义包
import ShowAge from './components/ShowAge';
// 测试 React Component ...
class App extends React.Component {
// 初始化数据
constructor(props) {
super(props);
console.log("react component parent constructor ...");
this.state = {
name: "FruitJ",
age: 22
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component parent getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component parent componentDidMount ...");
}
// btn's click callback
handleClick = () => {
console.log("react component parent click ...");
// update state
this.setState({
name: "LJ",
});
};
handleChange = (val) => {
this.setState({
age: val
});
};
render() {
console.log("react component parent render ...");
const { name, age } = this.state;
return (
<div className="App">
<h1>Hello React Component, My name is { name }</h1>
<ShowAge age = { this.state.age }
handleChange = { this.handleChange }
/>
<button onClick = { this.handleClick }>
parent 点我啊 ~
</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log("react component parent shouldComponentUpdate ...");
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component parent getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component parent componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component parent componentWillUnmount ...");
}
}
export default App;
子组件 :
import React, { Component } from 'react'
class ShowAge extends Component {// 定义组件
// 初始化数据
constructor(props) {
super(props);
console.log("react component child constructor ...");
this.state = {
hobby: "coding"
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component child getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component child componentDidMount ...");
}
handleClick = () => {
console.log("react component child handleClick ...");
// update state
this.setState({
hobby: "read source code!"
});
};
render() {
console.log("react component child render ...");
return (
<div>
<h3>props age : { this.props.age }</h3>
<h3>state hobby : { this .state.hobby }</h3>
<button onClick = { () => {this.props.handleChange(23)} }>
child 点我啊 ~
</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log("react component child shouldComponentUpdate ...");
// 优化 - 浅比较
return !(nextProps.age === this.props.age);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component child getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component child componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component child componentWillUnmount ...");
}
}
export default ShowAge;
动图 :
方案二 : 深比较
导语 : 深比较则是针对引用数据类型而言的,我们可以进行深比较,上面我们说过直接对比引用类型地址是不可能也不合适的,不能是因为两对儿对比对象 (nextState
和 this.state
、nextProps
和 this.props
) 的地址一定不同,所以用返回 false ,也就失去了意义。其次即使真的可以这样比,意义也不大,因为仅仅是比较地址而不比较其内部的属性变化,咱们是无法及时且正确的判定组件是否应该更新还是停止渲染 …。
所以笔者认为, React.Component 中 shouldComponentUpdate 中通过深比较来优化 react 组件的方式,首先是针对引用类型,其次是迭代引用类型的所有属性,并判断对比双方的所有属性值是否相同,有一个不同的情况都要重新渲染组件否则则停止渲染。
图解 :
代码 :
// 导入 React 官方包
import React from 'react';
// 导入自定义包
import ShowAge from './components/ShowAge';
// 测试 React Component ...
class App extends React.Component {
// 初始化数据
constructor(props) {
super(props);
console.log("react component parent constructor ...");
this.state = {
name: "FruitJ",
age: 22
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component parent getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component parent componentDidMount ...");
}
// btn's click callback
handleClick = () => {
console.log("react component parent click ...");
// update state
this.setState({
name: "LJ",
});
};
handleChange = (val) => {
this.setState({
age: val
});
};
render() {
console.log("react component parent render ...");
const { name, age } = this.state;
return (
<div className="App">
<h1>Hello React Component, My name is { name }</h1>
<ShowAge age = { this.state.age }
handleChange = { this.handleChange }
/>
<button onClick = { this.handleClick }>
parent 点我啊 ~
</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log("react component parent shouldComponentUpdate ...");
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component parent getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component parent componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component parent componentWillUnmount ...");
}
}
// 测试 React PrueComponent
export default App;
子组件 :
import React, { Component } from 'react'
class ShowAge extends Component {// 定义组件
// 初始化数据
constructor(props) {
super(props);
console.log("react component child constructor ...");
this.state = {
name: "XXY",
hobby: "coding"
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component child getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component child componentDidMount ...");
}
handleClick = () => {
console.log("react component child handleClick ...");
// update state
this.setState({
// hobby: "read source code!"
hobby: "coding"
});
};
render() {
console.log("react component child render ...");
return (
<div>
<h3>props age : { this.props.age }</h3>
<h3>state hobby : { this .state.hobby }</h3>
<button onClick = { this.handleClick }>
child 点我啊 ~
</button>
</div>
);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log("react component child shouldComponentUpdate ...");
// 检测当前组件的 state 对象中的每个属性值 ( 深比较 )
let arr = Object.keys(nextState);
try {
return !arr.every((item, index) => nextState[arr[index]] === this.state[arr[index]]);
}catch(err) {
return true;
}
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component child getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component child componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component child componentWillUnmount ...");
}
}
export default ShowAge;
动图 :
2. 使用 React.PrueComponent
来构建 React 组件,默认自带优化效果
导语 : 我们可以使用 React.PureComponent
来定义 React 组件,因为其自带优化效果,并且如果应用确实需要优化,该方式也是 React 官方推荐使用的,而不是自己使用 React.Component
去 "定制"
,但是 React.PureComponent
也有不足之处 : 就是简单的数据结构可以进行优化,但是复杂的数据结构 React.Component
也无能为力。
代码 :
父组件 :
// 导入 React 官方包
import React from 'react';
// 导入自定义包
import ShowAge from './components/ShowAge';
// 测试 React Component ...
class App extends React.PureComponent {
// 初始化数据
constructor(props) {
super(props);
console.log("react component parent constructor ...");
this.state = {
name: "FruitJ",
age: 22
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component parent getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component parent componentDidMount ...");
}
// btn's click callback
handleClick = () => {
console.log("react component parent click ...");
// update state
this.setState({
name: "LJ",
});
};
handleChange = (val) => {
this.setState({
age: val
});
};
render() {
console.log("react component parent render ...");
const { name, age } = this.state;
return (
<div className="App">
<h1>Hello React Component, My name is { name }</h1>
<ShowAge age = { this.state.age }
handleChange = { this.handleChange }
/>
<button onClick = { this.handleClick }>
parent 点我啊 ~
</button>
</div>
);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component parent getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component parent componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component parent componentWillUnmount ...");
}
}
// 测试 React PrueComponent
export default App;
子组件 :
import React, { Component } from 'react'
class ShowAge extends React.PureComponent {// 定义组件
// 初始化数据
constructor(props) {
super(props);
console.log("react component child constructor ...");
this.state = {
name: "XXY",
hobby: "coding",
arr: [1, 2, 3, 4, 5, 6]
};
}
static getDerivedStateFromProps(props, state) {
console.log("react component child getDerivedStateFromProps ...");
return null;
}
componentDidMount() {
console.log("react component child componentDidMount ...");
}
handleClick = () => {
console.log("react component child handleClick ...");
// update state
this.setState({
// hobby: "read source code!",
hobby: "coding",
// arr: [1, 2, 3, 4, 5, 6]
});
};
render() {
console.log("react component child render ...");
return (
<div>
<h3>props age : { this.props.age }</h3>
<h3>state hobby : { this .state.hobby }</h3>
{/*<button onClick = { () => {this.props.handleChange(23)} }>*/}
<button onClick = { this.handleClick }>
child 点我啊 ~
</button>
</div>
);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("react component child getSnapshotBeforeUpdate ...");
return "";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("react component child componentDidUpdate ...");
}
componentWillUnmount() {
console.log("react component child componentWillUnmount ...");
}
}
export default ShowAge;
动图 :
三、总述 :
如果是使用 React.Component
来定义的组件,就通过 shouldComponentUpdate 周期方法来优化组件,但须注意对于 基本类型 可以采取 浅比较
的方式;对于 引用类型 可以采取 深比较
的方式。
如果是使用 React.PureComponent
来定义组件,默认其有着优化组件的策略,对于简单一点的数据结构可以达成我们的需求,但是复杂的数据结构 React.PureComponent
还是无能为力。
只有实在没办法的时候才可以通过 shouldComponentUpdate 进行定制化的深比较来实现需求。