前言
React Native中组件的生命周期,也就是React.js中Component的生命周期。
英文官方文档:React.Component
类比Android中activity和fragment,了解其生命周期对于我们掌握一个组件从创建到销毁的整个完整的过程和优化逻辑有很大的帮助。
图解
首先来一张经典的生命周期的图:
(图片来自这里)
在图中可以看出大概分为3个阶段:
首先getDefaultProps
是创建前的准备工作,来设置默认props的值,所以严格地来说,这不是组件的生命周期的一部分。需要注意的是如果有从父组件传过来的同名的props,那么会覆盖。还有就是多个实例间共享引用,而不是有多个!
另外,在ES6中使用defaultProps
替代了getDefaultProps
,如:
static defaultProps = {
name: 'mars',sex: 'boy',age: 10
};//设置默认属性
import PropTypes from 'prop-types';
static propTypes = {
name: PropTypes.string.isrequired,sex: PropTypes.string,age: PropTypes.number,};//设置属性的类型
①创建实例化阶段
这个阶段才是组件生命周期真正开始的阶段。主要有组件实例创建,初始化状态,挂载渲染等过程。
在ES6中,初始化state放在了constructor
构造函数中,而不再使用getInitialState
,如:
constructor(props){
super(props);
this.state = {
like:false
};
}
②运行更新阶段
这个阶段主要是用来处理用户交互事件,组件接收父组件props更新或者state改变,界面更新。这个阶段是生命周期中最重要的阶段,我们常在这一阶段对组件进行优化,减少render,从而大大提高性能!
③销毁阶段
这一阶段比较简单,只有一个回调函数,在组件销毁的时候调用!这里主要做一些组件的卸载销毁操作,最典型的比如定时器!
各个阶段回调函数
defaultProps
就不再说了,基本上也就上边说的那点内容,而且不算是生命周期的一部分!
创建实例化阶段
constructor() (ES6)
构造函数,组件创建时调用,写法就是上边那种格式,super(props)
一定要写在第一行。然后使用:
this.state={
xx:xx,xx:xx,};
来初始化状态!
componentWillMount()
该回调方法在组件挂载前调用!这个函数调用时机是在组件创建,并初始化了状态之后,在第一次绘制 render() 之前。可以在这里做一些业务初始化操作,也可以设置组件状态。这个函数在整个生命周期中只被调用一次。
render()
在组件渲染的时候调用!只允许返回一个最外层容器组件。render函数尽量保持纯净,只渲染组件,不修改状态,不执行副操作(比如计时器)。所以我们不要在render()函数里边做一些更改状态或者与服务器交互的操作!
componentDidMount()
在组件挂载之后调用!需要注意的是,RN 框架是先调用子组件的 componentDidMount()
,然后调用父组件的函数。这个函数里我们就可以与服务器进行交互,进行网络请求加载数据的操作,或者与 JS 其他框架交互了,例如设置计时setTimeout
。
运行更新阶段
componentWillReceiveProps(object nextProps)
该回调函数在接收到新的props时调用,一般是由父组件传递过来的!参数nextProps
是接收的到新的props,当前旧的属性还是可以通过 this.props
来获取。在这个回调函数里面,你可以根据属性的变化,通过调用 this.setState() 来更新你的组件状态,这里调用更新状态是安全的,并不会触发额外的 render() 调用。
shouldComponentUpdate(object nextProps,object nextState)
该函数返回一个布尔值!由上图中可以看出,在接收到父组件属性,或者每次state更改的时候,都会调用该函数!
如果返回ture,则进行组件的重新渲染render,相反返回false,则不做任何处理!
默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。但是在某些特定条件下,我们可以重载这一方法根据传递过来的props和state来选择是否更新,从而提高性能。
说到这里,顺便提一下PureComponent
,我们知道最常见的一般extents Component
来写一个组件,如果我们知道这一组件不需要重绘或者其他改变的props或者state跟这个组件没有关系的话,我们可以使用extents PureComponent
来避免重绘!
因为PureComponent
内部已经帮我们实现了shouldComponentUpdate()
函数!
源码:
// 这个变量用来控制组件是否需要更新
var shouldUpdate = true;
// inst 是组件实例
if (inst.shouldComponentUpdate) {
shouldUpdate = inst.shouldComponentUpdate(nextProps,nextState,nextContext);
} else {
if (this._compositeType === CompositeType.PureClass) {
// 用 shallowEqual 对比 props 和 state 的改动
// 如果都没改变就不用更新
shouldUpdate =
!shallowEqual(prevProps,nextProps) ||
!shallowEqual(inst.state,nextState);
}
}
需要注意的是shallowEqual()
是浅比较,那么涉及到一些对象,数组的话,还是需要我们自己写逻辑判断的!
关于PureComponent
更多的内容,可以参考下边这篇文章:
React PureComponent 源码解析
componentWillUpdate(object nextProps,object nextState)
shouldComponentUpdate
返回true或者调用forceUpdate
之后,就会开始准更新组件,并调用 componentWillUpdate()
。
输入参数与shouldComponentUpdate
一样,在这个回调中,可以做一些在更新界面之前要做的事情。需要特别注意的是,在这个函数里面,你就不能使用 this.setState 来修改状态。这个函数调用之后,就会把 nextProps
和 nextState
分别设置到 this.props
和 this.state
中。紧接着这个函数,就会调用 render() 来更新界面了。
render()
跟初始化的时候一样,渲染函数。
componentDidUpdate(object prevProps,object prevState)
组件更新渲染完成之后,就会调用改回调函数!因为到这里已经完成了属性和状态的更新了,此函数的输入参数变成了 prevProps 和 prevState。
同样的,这里也不能使用 this.setState 来修改状态!
销毁阶段
componentWillUnmount()
组件被卸载的时候调用。一般在componentDidMount里面注册的事件需要在这里删除。比如定时器,监听等!
更新方式
此部分内容来自:React组件生命周期小结,写的很好,这里学习记录一下!
在react中,触发render的有4条路径。
以下假设shouldComponentUpdate都是按照默认返回true的方式。
- 首次渲染Initial Render
- 调用this.setState (并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render)
- 父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render)
- 调用
this.forceUpdate()
注意,如果在shouldComponentUpdate里面返回false可以提前退出更新路径。
总结
对生命周期进行一个整体的回顾:
上边的内容是在查阅一些资料文档博客之后的一个学习总结,对自己了解组件生命周期有很大的帮助,也非常感谢这些前辈的指引!
参考文章:
React Native 中组件的生命周期
React Native 组件生命周期(ES6)
React组件生命周期小结
React-Native生命周期详解