一. React 生命周期
通过这张图你可以看到React生命周期的四个大阶段:
- Initialization:初始化阶段。
- Mounting: 挂载阶段。
- Updation: 更新阶段。
- Unmounting: 销毁阶段
什么是生命周期函数
如果非要用一句话把生命周期函数说明白,我觉的可以用这句话来说明:
生命周期函数指在某一个时刻组件会自动调用执行的函数
1.1 Initialization阶段
我们可以把构造函数里的这个阶段叫做Initilization阶段,包括定义属性(props)和状态(state)。
constructor(props) {
super(props);
this.state = {
inputValue: '',
list: ['aaaa', 'bbbb', 'cccc']
}
}
1.2 Mounting阶段
Mounting阶段即挂载阶段,伴随着整个虚拟DOM的生成,它里边有三个小的生命周期函数,分别是:
- componentWillMount : 在组件即将被挂载到页面的时刻执行。
- render : 页面state或props发生变化时执行。
- componentDidMount : 组件挂载完成时被执行。
//生命周期函数测试
UNSAFE_componentWillMount() {
console.log('componentWillMount')
}
componentDidMount() {
console.log('componentDidMount')
}
注意的问题:
componentWillMount
和componentDidMount
这两个生命周期函数,只在页面刷新时执行一次,而render函数是只要有state和props变化就会执行
,这个初学者一定要注意。- componentWillMount在React的V16.9版本中进行了升级,推荐使用
UNSAFE_componentWillMount
来代替componentWillMount
。 参考如下速读 | 了解 React v16.9 最新特性
二. React生命周期中的Updation阶段
React生命周期中的Updation阶段,也就是组件发生改变的更新阶段,这是React生命周期中比较复杂的一部分,它有两个基本部分组成,一个是props属性改变,一个是state状态改变
(这个在生命周期的图片中可以清楚的看到)。
2.1 shouldComponentUpdate函数
shouldComponentUpdate
函数会在组件更新之前,自动被执行。比如写入下面的代码:
shouldComponentUpdate(){
console.log('shouldComponentUpdate---组件发生改变前执行')
}
它要求返回一个布尔类型的结果,必须有返回值,这里就直接返回一个true了(真实开发中,这个是有大作用的)。
shouldComponentUpdate(){
console.log('shouldComponentUpdate---组件发生改变前执行')
return true
}
现在就可以在控制台console里看到结果了,并且结果是每次文本框发生改变时都会随着改变。如果你返回了false,这组件就不会进行更新了。 简单点说,就是返回true,就同意组件更新;返回false,就反对组件更新。
2.2 componentWillUpdate函数
componentWillUpdate
在组件更新之前,但shouldComponenUpdate
之后被执行。但是如果shouldComponentUpdate
返回false
,这个函数就不会被执行了。
//shouldComponentUpdate返回true才会被执行。
componentWillUpdate(){
console.log('componentWillUpdate---组件更新前,shouldComponentUpdate函数之后执行')
}
2.3 componentDidUpdate
componentDidUpdate
在组件更新之后执行,它是组件更新的最后一个环节。
componentDidUpdate(){
console.log('componentDidUpdate----组件更新之后执行')
}
为了方便我们看出结果,可以在每个函数前加上序号。最后我们可以看到控制台输出的结果如下:
1-shouldComponentUpdate---组件发生改变前执行
2-componentWillUpdate---组件更新前,shouldComponentUpdate函数之后执行
3-render----开始挂载渲染
4-componentDidUpdate----组件更新之后执行
2.4 componentWillReceiveProps 函数
我们可以先在Xiaojiejie.js
组件里写下这个函数,例如下面的代码。
componentWillReceiveProps(){
console.log('componentWillReceiveProps')
}
这时候会发现函数什么时候都不会被执行,因为Xiaojiejie.js
算是一个顶层组件,它并没接收任何的props
。可以把这个函数移动到XiaojiejieItem.js
组件中。
凡是组件都有生命周期函数,所以子组件也是有的,并且子组件接收了props,这时候函数就可以被执行了。
componentWillReceiveProps(){
console.log('child - componentWillReceiveProps')
}
这个时候再预览,就会看到componentWillReceiveProps
执行了。那现在可以总结一下它的执行时间了:
子组件接收到父组件传递过来的参数,父组件render函数重新被执行,这个生命周期就会被执行。
注意:
子组件接收到父组件传递过来的参数,父组件render函数重新被执行,这个生命周期就会被执行。
- 也就是说这个组件第一次存在于Dom中,函数是不会被执行的;
- 如果已经存在于Dom中,函数才会被执行。
这个生命周期算是比较复杂的一个生命周期,需要我们花点时间去消化。
2.5 React生命周期中的Unmounting阶段
componentWillUnmount
这个生命周期函数是组件DOM中被移除时执行。
比如在XiaojiejieItem.js
,写入下面的代码:
//当组件从页面中删除的时候执行
componentWillUnmount(){
console.log('child - componentWillUnmount')
}
写完后,可以到浏览器终端中查看结果,当我们点击服务项,服务项被删除时,这个函数就被执行了。
备注:
- shouldComponentUpdate(nextProps, nextState)
- componentDidUpdate(prevProps, prevState, snapshot)
这两个生命周期在首次渲染时不会执行
,参考如下:https://zh-hans.reactjs.org/docs/react-component.html#componentdidupdate
同时,也要注意这两个生命周期函数所接收的参数
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 1
};
}
render() {
const { count } = this.state;
return (
<div>
<button onClick={() => this.setState({ count: count + 1 })}>按钮{count}</button>
<CustomTextInput value={count} />
</div>
);
}
}
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps) {
console.log('this.props', this.props);
console.log('nextProps', nextProps);
return true;
}
componentDidUpdate(prevProps) {
console.log('prevProps', prevProps);
console.log('props', this.props);
}
render() {
return (
<div>
</div>
);
}
}
打印结果:
三. 生命周期改善程序性能
已经对React生命周期有了认识,那如何利用它提高组件的性能呢?下面来说一下通过shouldComponentUpdate
函数,改善React组件性能
的例子。
3.1 小姐姐组件存在性能问题
是的,小姐姐组件已经写的很熟悉了,但是它有一个性能问题,我一直没告诉你,那就是子组件XiaojiejieItem
频繁无用渲染render
。如何能看出这个问题的呢?
在 React Developer Tools
中, 找到React标签,然后在右边点开设置,选中highlight Updates。
这时候你在浏览器的文本框中输入一下内容,你可以清楚的看到子组件也发生了重新render的情况。
有很多程序员会忽略这样的性能损耗,认为没有什么大不了的,但是软件的卡顿是一点点产生的,所以必须要减少性能损耗。
可以在XiaojiejieItem.js
的render函数里加入下面的代码,更直观的看到这个问题。
render() {
console.log('child-render')
return (
<div onClick={this.handleClick}>
{this.props.avname}为你做- {this.props.content}
</div>
);
}
3.2 利用shouldComponentUpdate解决
这个问题看似很小,但是当你页面很复杂时,足以影响用户体验,不要骗自己了。其实用shouldComponentUpdate
函数就可以简单的解决调这个问题。
直接在XiaojiejieItem.js
中加入下面的代码:
shouldComponentUpdate(){
return false;
}
这时候在浏览器中查看,问题已经没有了。但是这样做太暴力了,否定了所有的东西
,那如果在真实项目中,需要改变值属性值,就不能进行渲染了。所以我们可以更优雅一下,写出下面的代码。
shouldComponentUpdate
有两个参数:
- nextProps:变化后的属性;
- nextState:变化后的状态;
shouldComponentUpdate(nextProps,nextState){
if(nextProps.content !== this.props.content){
return true
}else{
return false
}
}
现在的代码就优雅一些了,也不那么暴力了。这就算是完美解决了子组件的渲染性能问题,你写的代码质量也得到了提高。其实在面试React让写TODOList应用的,都是看这个来区分等级的,能写出来的,这算普通程序员;能写出来并作性能优化的,这算有经验的程序员。
四. 生命周期总结(自己理解)
- 生命周期总共有4个阶段。
- 在第一次打开浏览器页面的时候,只触发Initiation和Mounting阶段。
- 在对页面有变动操作的时候,会触发第三个阶段,即Updating阶段。
- 在父组件render之后会执行子组件里的生命周期,包括还是那4个阶段,即又回到了1。