一、避免不必要的 render 执行
当父组件的任意状态(state或props)发生变更时,父组件会重新渲染(执行render),同时依赖父组件的所有子组件也都会重新渲染(执行render),但是我们希望只有当父组件传递给子组件的 props 发生变更时,子组件才会重新渲染
1. 解决方式一:使用 shouldComponentUpdate 优化(只适用于类组件)
需要 immutable
脚本库配合来判断 state 和 props 是否变化,变化执行render,不变化不执行 render
- 在父组件中使用 shouldComponentUpdate
import React, { Component } from "react";
import OptimizeChildView from "./OptimizeChildView"
import { Map,is } from 'immutable'; // 可用来判断对象的值是否相等
class Test extends Component {
constructor(props) {
super(props);
this.state = {
height: 170,
age: 20,
};
this.changeAge = this.changeAge.bind(this) /* 修正this指向 */
this.changeHeight = this.changeHeight.bind(this) /* 修正this指向 */
this.changeHeightNo = this.changeHeightNo.bind(this) /* 修正this指向 */
}
render() {
console.log('父组件 render');
return (
<>
<h1>父组件</h1>
<div>{this.state.age}</div>
<div>{this.state.height}</div>
<button onClick={this.changeAge}>改变当前组件的state、改变传入子组件的props</button>
<button onClick={this.changeHeight}>改变当前组件的state、不改变传入子组件的props</button>
<button onClick={this.changeHeightNo}>不改变当前组件的state、不改变传入子组件的props</button>
<hr />
<OptimizeChildView age={this.state.age} />
</>
);
}
// 结果:父子组件 render 都会执行(√)
changeAge(e) {
this.setState((prevState) => {
return { age: prevState.age + 1 };
});
}
// 结果:父组件render会执行,子组件render不会执行(√)
changeHeight(e) {
this.setState((prevState) => {
return { height: prevState.height + 1 };
});
}
// 结果:父子组件 render 都不会执行(√)
changeHeightNo(e) {
this.setState((prevState) => {
return { height: prevState.height };
});
}
// shouldComponentUpdate 优化
shouldComponentUpdate(nextProps,nextState){
const that = this
if(is(Map(nextProps),Map(that.props)) && is(Map(nextState),Map(that.state))){
return false // 代表父组件传过来的props和当前组件的state都没变化,不执行 render
}
return true
}
}
export default Test;
- 在子组件中使用 shouldComponentUpdate
import React, { Component } from "react";
import { Map,is } from 'immutable'; // 可用来判断对象的值是否相等
class Test extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
console.log('子组件 render');
return (
<>
<h1>子组件</h1>
<div>{this.props.age}</div>
</>
);
}
// 代表父组件传过来的props和当前组件的state都没变化,不执行 render
shouldComponentUpdate(nextProps,nextState){
const that = this
if(is(Map(nextProps),Map(that.props)) && is(Map(nextState),Map(that.state))){
return false
}
return true
}
}
export default Test;
2. 解决方式二:使用 PureComponent 优化 (只适用于类组件)
React.PureComponent 组件采用对属性和状态用浅比较的方式在组件内部实现了 shouldComponentUpdate()
- 在父组件中使用 PureComponent
import React, { PureComponent } from "react";
import OptimizeChildView from "./OptimizeChildView"
class Test extends PureComponent {
constructor(props) {
super(props);
this.state = {
height: 170,
age: 20,
};
this.changeAge = this.changeAge.bind(this) /* 修正this指向 */
this.changeHeight = this.changeHeight.bind(this) /* 修正this指向 */
this.changeHeightNo = this.changeHeightNo.bind(this) /* 修正this指向 */
}
render() {
console.log('父组件 render');
return (
<>
<h1>父组件</h1>
<div>{this.state.age}</div>
<div>{this.state.height}</div>
<button onClick={this.changeAge}>改变当前组件的state、改变传入子组件的props</button>
<button onClick={this.changeHeight}>改变当前组件的state、不改变传入子组件的props</button>
<button onClick={this.changeHeightNo}>不改变当前组件的state、不改变传入子组件的props</button>
<hr />
<OptimizeChildView age={this.state.age} />
</>
);
}
// 结果:父子组件 render 都会执行(√)
changeAge(e) {
this.setState((prevState) => {
return { age: prevState.age + 1 };
});
}
// 结果:父组件render会执行,子组件render不会执行(√)
changeHeight(e) {
this.setState((prevState) => {
return { height: prevState.height + 1 };
});
}
// 结果:父子组件 render 都不会执行(√)
changeHeightNo(e) {
this.setState((prevState) => {
return { height: prevState.height };
});
}
}
export default Test;
- 在子组件中使用 PureComponent
import React, { PureComponent } from "react";
class Test extends PureComponent {
constructor(props) {
super(props);
this.state = {};
}
render() {
console.log('子组件 render');
return (
<>
<h1>子组件</h1>
<div>{this.props.age}</div>
</>
);
}
}
export default Test;
3. 解决方式三:使用 React.memo 优化 (只适用于无状态组件)
React.memo 为高阶组件,需要传入两个参数,第一个参数是被缓存的组件,第二个参数是回调函数,只有当第二个回调函数返回true时才会重新渲染缓存组件
一般不传第二个参数(不传:只有当子组件依赖父组件的props发生变化才重新渲染子组件,传:状态变化满足某个条件才重新渲染子组件)
- 在父组件中
import { useState } from "react";
import OptimizeChildView from "./OptimizeChildView";
function OptimizeView() {
const [height, setHeight] = useState(170);
const [age, setAge] = useState(20);
// 结果:父子组件 render 都会执行(√)
function changeAge(e) {
setAge(age + 1);
}
// 结果:父组件render会执行,子组件render不会执行(√)
function changeHeight(e) {
setHeight(height + 1);
}
// 结果:父子组件 render 都不会执行(√)
function changeHeightNo(e) {
setHeight(height);
}
console.log('父组件 render');
return (
<>
<h1>父组件</h1>
<div>{age}</div>
<div>{height}</div>
<button onClick={changeAge}>
改变当前组件的state、改变传入子组件的props
</button>
<button onClick={changeHeight}>
改变当前组件的state、不改变传入子组件的props
</button>
<button onClick={changeHeightNo}>
不改变当前组件的state、不改变传入子组件的props
</button>
<hr />
<OptimizeChildView age={age} />
</>
);
}
export default OptimizeView;
- 在子组件中使用 memo 高阶函数包裹组件
import { memo } from "react";
function OptimizeChildView(props) {
console.log('子组件 render');
return (
<>
<h1>子组件</h1>
<div>{props.age}</div>
</>
);
}
export default memo(OptimizeChildView);
二、避免在 render 函数中使用内联函数和在 render 函数中使用bind绑定this
否则每次执行 render 函数都会创建新的函数实例
- 解决方式:使用非内联的方式定义点击函数,在 constructor 中使用bind绑定this
import React, { Component } from "react";
import OptimizeChildView from "./OptimizeChildView";
class Test extends Component {
constructor(props) {
super(props);
this.state = {
name: "zhangsan",
age: 20,
};
this.changeAge = this.changeAge.bind(this) /* 修正this指向 */
}
render() {
return (
<>
<div>{this.state.name}</div>
<div>{this.state.age}</div>
<button onClick={this.changeAge}>函数的正确打开方式</button>
<button onClick={(e) => {this.setState((prevState) => {return { age: prevState.age + 1 }})}}>内联函数</button>
<OptimizeChildView />
</>
);
}
changeAge(e) {
this.setState((prevState) => {
return { age: prevState.age + 1 };
});
}
}
export default Test;
三、避免创建不必要的组件最外层元素标签
- 解决方式:使用
<></>
代替<div></div>
充当组件最外层元素标签
import React, { Component } from "react";
class Test extends Component {
/* render() {
return (
<div>
<div>hello world</div>
<div>hello world</div>
</div>
);
} */
// 避免生成多余的外层 div
render() {
return (
<>
<div>hello world</div>
<div>hello world</div>
</>
);
}
}
export default Test;
网友评论