一:理论知识
React更新流程:
React在props或state发生改变时,会调用render()方法,创建一棵不同的树。
React优化:
同层节点之间相互比较,不会垮节点比较
-
不同类型的节点,产生不同的树结构
-
开发中,通过key来指定哪些节点在不同的渲染下保持稳定
Diffing算法:
1.对比不同类型的元素,节点不同就会拆解原来的树,建立新树
2.对比同类型元素
同类型组件元素:组件会保持不变,React会更新该组件的props,并且调用componentWillReceiveProps() 和 componentWillUpdate() 方法;
下一步,调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归;
3.对子节点进行递归
在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。
所以我们需要key值来进行优化,但是情形一的情况下加不加key对性能无变化
key的注意事项: key应该是唯一的
key不要使用随机数(随机数在下一次render时,会重新生成一个数字)
-
使用index作为key,对性能是没有优化的
key值发生了变化,到时还是需要创建很多的mutation,所以无优化。
二:代码实践
案例想要实现的效果,点击数字时候界面刷新,触发渲染函数,点击文本的时候,因为界面上没有对文本依赖,所以不想触发render函数
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter:10,
message:"hello world"
};
}
render() {
console.log("App render被调用");
return (
<div>
<h3>{this.state.counter}</h3>
<div>
<button onClick={e=>{this.changeCount()}}>数字+1</button>
<button onClick={e=>{this.changeText()}}>改变文本</button>
</div>
</div>
);
}
changeCount(){
this.setState({
counter:this.state.counter+1,
})
}
changeText(){
this.setState({
message:"hello react",
})
}
shouldComponentUpdate(nextProps, nextState) {
if(this.state.counter!==nextState.counter){
return true
}
return false
}
}
上述代码可实现目标需求,但是存在两个问题:
- 如果很多个类,那么每个类组件都需要写shouldComponentUpdate,并且如果属性很多,那判断更多
- 如果是函数式组件怎么办?
解决方案:
- 针对问题一:在不使用shouldComponentUpdate的情况下,所有的类不要继承Component,而是继承PureComponent
// 原来情况
import React, { Component, PureComponent } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter:10,
message:"hello world"
};
}
render() {
console.log("App render被调用");
return (
<div>
<h3>{this.state.counter}</h3>
<div>
<button onClick={e=>{this.changeCount()}}>数字+1</button>
</div>
<Main/>
</div>
);
}
changeCount(){
this.setState({
counter:this.state.counter+1,
})
}
}
class Main extends Component{
render(){
console.log('Main 渲染了')
return (
<div>
main 组件
</div>
);
}
}
点击按钮,App组件的render()函数与Main组件的render()函数都会被调用,实际上Main组件并没有重新渲染的必要,浪费了性能。
//修改后
import React, { Component, PureComponent } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter:10,
message:"hello world"
};
}
render() {
console.log("App render被调用");
return (
<div>
<h3>{this.state.counter}</h3>
<div>
<button onClick={e=>{this.changeCount()}}>数字+1</button>
</div>
<Main/>
</div>
);
}
changeCount(){
this.setState({
counter:this.state.counter+1,
})
}
}
// 主要变化由Component===>PureComponent
class Main extends PureComponent{
render(){
console.log('Main 渲染了')
return (
<div>
main 组件
</div>
);
}
}
对应源码:
- 针对问题二:引入高阶函数memo来解决,此函数返回一个新的组件
import React, { Component, PureComponent,memo } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter:10,
};
}
render() {
console.log("App render被调用");
return (
<div>
<h3>{this.state.counter}</h3>
<div>
<button onClick={e=>{this.changeCount()}}>数字+1</button>
</div>
<Main/>
{/* 观看不同的效果 */}
{/* <Footer/> */}
<MemoFooter/>
</div>
);
}
changeCount(){
this.setState({
counter:this.state.counter+1,
})
}
}
class Main extends PureComponent{
render(){
console.log('Main 渲染了')
return (
<div>
main 组件
</div>
);
}
}
function Footer(){
console.log('footer 渲染了')
return(
<div>footer组件</div>
)
}
const MemoFooter=memo(
function Footer(){
console.log('footer 渲染了')
return(
<div>footer组件</div>
)
}
)
源码对应: