16.3版本之前的生命周期
生命周期一共费为四个阶段:initialization(组件初始化阶段),mounting(组件挂载阶段),update(组件更新阶段),unMounting(组件卸载阶段)。
initialization(组件初始化阶段)
import React, { Component } from 'react';
class SetWord extends Component {
constructor(props){
super(props)
}
render() {
...
}
}
export default SetWord
在组件的初始化阶段,创建的类继承react component这个基类,这样才可以使用render,生命周期等方法。这里也反映出了为什么函数组件中不能使用这些方法的原因。
当我们没有显式的写constructor,程序自己增加一个空的constructor,如果我们显式的写了之后就必须调用super()
super()函数是用来调用基类的constructor()的,将父组件的props注入给子组件。供子组件读取(只读)。
mounting(组件挂载阶段)
- componentWillMount
- 组件挂载到DOM前调用
- 只会被调用一次
- 在这个生命周期中setState不会引起组件重新渲染
- 可以调用setStat,但是没啥用,不会触发渲染
- render
- 当组件的state和props变化的时候就会触发render方法
- return一个react元素,不负责渲染工作,之后react自身根据此元素去渲染页面
- render是纯函数(函数的返回结果只依赖于他的传入结果,函数执行过程中没有副作用)
- 在render中不能使用setState(),会改变组件状态的副作用,并且会造成死循环栈溢出
- render不能返回undefined
- componentDidMount
- 组件挂载到DOM之后调用,并且只执行一次
- 数据初始化和添加订阅事件的工作可以在这进行
- 可以调用setState
update(组件的更新阶段)
react更新机制:setState引起state更新,父组件引起props更新,更新之后无论state和props有没有变化,都会引起子组件的重新render
- componentWillReceiveProps(nextProps)
- 当props更新的时候,这个生命周期会被触发。这也是props变化之后进行更新数据的唯一方,参数nextProps是父组件传递过来新的props数据
- 但是父组件render方法的调用不能保证nextProps的数据和之前是有变化的。所以在这个方法中可以对props进行对比
- 在componentWillReceiveProps中可以将props转化成子组件自己的state,并且在这里setState不会引起二次渲染。
- 可以调用setState
- shouldComponentUpdate(nextProps,nextState)
- 当props和state更新的时候会触发这个生命周期
- 在这个生命周期中有两个参数:nextProps和nextState,nextProps是传递Props的更新,nextState传递的是state的更新。
- 在这个生命周期中我们可以通过比对nextProps和this.props,nextState和this.state,来判断页面需不需要render
- 当componentWillReceiveProps中执行了setState()更新了state,但是在render之前this.state依然指向更新前的state。
- 这个生命周期中必须有返回值,返回值类型是boolean,返回true则代表渲染(当返回true的时候,react元素回进行比对,然后来判断是否要render某一模块),返回false则代表不渲染(返回false则不会进行比对)
- 注意:这个生命周期虽然可以进行性能优化,但是远远没有那么优秀
- 添加shouldComponentUpdate方法一般都会拖慢组件的更新速度
- 这是为什么呢?显然当我们使用shouldComponentUpdate的时候,会有一些比对的操作,当props很大的时候,这个比对的操作可能不会很快。
- 而且react diff算法 自己对于什么时候更新,更新什么,有自己一套很好的逻辑。所以我们自己来做大程度上回画蛇添足。
- 使用shouldComponentUpdate对于我们的性能提升可能也是微乎其微的。
- 使用shouldComponentUpdate的时候,可能会引起一些不易察觉的bug,
- 所以综上:我们使用shouldComponentUpdate的时候,确实会对自己页面有真实显著的提升的时候可以使用,如果微乎其微,则没有必要使用。
- 可以使组件继承react.PureComponent,这个时候当props和state更新的时候可以有一个浅比较
- componentWillUpdate(prevProps, prevState)
- 执行一些组件更新前的操作。
- 使用率不高
- 不能调用setState,会造成重复循环
- render
- componentDidUpdate(prevProps, prevState)
- 此方法在组件更新之后调用,可以操作dom,
- 参数prevProps和prevState代表的时候组件更新之前的props和state
- 适合请求接口数据
- setState,可能回触发重复渲染。需要自己判断
unMounting(卸载阶段)
- componentWillUnMount
- 在组件卸载之前调用
- 在这执行一些清理工作,比如结束定时器,绑定的事件什么的,操作避免内存泄漏
16.4+版本生命周期
当16.0+版本中推出fiber之后,旧版的生命周期就不适合了。当开启async rendering,在render之前的所有函数都有可能被执行多次。这也意味着这些函数中的操作也要被执行多次。
render之前的生命周期除了shouldComponentUpdate,其他的全部都被getDerivedStateFromProps所替代
mounting(挂载阶段)
update(更新阶段)
- static getDerivedStateFromProps(props, state)
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- 看名字就能知道这个生命周期的作用:更新前获取快照
- 这个周期执行的时候DOM还没有被更新,给了我们一个获取DOM信息的机会。计算得到一个snapshot,snapshot会作为componentDidUpdate的第三个参数传入。
- 纵然这样,这个方法还是挺鸡肋的,你细品,好像没有多少场景可以用到这个方法。
getSnapshotBeforeUpdate(prevProps,prevState){
console.log(prevProps,prevState)
return 'fanfan'
}
conponentDidUpdate(prevProps, prevState, snapshot){
console.log(snapshot); // fanfan
}
unMounting(卸载时)
总结:新的生命周期最大的特点就是:使用getDerivedStateFromProps是为了强制让开发者在render之前只做无副作用的操作。并没局限于根据props和state决定新的state