本文解读了react生命周期的源码,如果你还是个入门的小白,当然可以忽略源码,看一看作者写的demo。也可以明白生命周期奥义。
React基础1--生命周期详解React组件生命周期根据广义的定义,可分为挂在、渲染和卸载几个阶段。当渲染后的组件再次更新时,react会重新去渲染组件,直至卸载。
在定义组建的时候,我们会根据需要在组件在生命周期不同阶段实现不同逻辑。
当组件首次挂载时,按顺序执行getDefaultProps、getInitialState、componentWillMount、render、componentDidMount。
当卸载组件时,执行componentWillUnmount
当重新挂载组件时候,此时按顺序执行 getInitialState、componentWillMount、 render、componentDidMount(此时不会执行getDefaultProps)
当组件重新渲染时,组件接收到更新状态,此时执行顺序是:componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate
在es6的与法理static defaultProps = {} 相当于其实就是调用内部的getDefaultProps,this.state = {} 就相当于getInitialState。
自定义组件的生命周期主要通过三个阶段:
MOUNTING、RECEIVE_PROPS、UNMOUNTING对应的方法分别为:mountComponent、updateComponent、unmountComponent
一、使用createClass创建自定义组件
createClass方法是创建自定义组件的入口方法。该方法只在生命周期中调用一次,所有的实例化的props都会共享。
Paste_Image.png找到ReactClass.js
Paste_Image.png以下为源码
var ReactClass = {
createClass: function (spec) {
var Constructor = function (props, context, updater) {
// 触发自动绑定
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
// 初始化参数
this.props = props;
this.context = context;
this.refs = emptyObject;// 本组件对象的引用,可以利用它来调用组件的方法
this.updater = updater || ReactNoopUpdateQueue;
// 调用getInitialState()来初始化state变量
this.state = null;
var initialState = this.getInitialState ? this.getInitialState() : null;
if (initialState === undefined && this.getInitialState._isMockFunction) {
initialState = null;
}
this.state = initialState;
};
// 继承父类
Constructor.prototype = new ReactClassComponent();
Constructor.prototype.constructor = Constructor;
Constructor.prototype.__reactAutoBindPairs = [];
//按顺序合并mixins
injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));
mixSpecIntoComponent(Constructor, spec);
// 待到所有mixins合并完成,调用getDefaultProps,并挂载到组件类上(整个周期只会调用一次)。
if (Constructor.getDefaultProps) {
Constructor.defaultProps = Constructor.getDefaultProps();
}
if (Constructor.getDefaultProps) {
Constructor.getDefaultProps.isReactClassApproved = {};
}
if (Constructor.prototype.getInitialState) {
Constructor.prototype.getInitialState.isReactClassApproved = {};
}
!Constructor.prototype.render ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createClass(...): Class specification must implement a `render` method.') : _prodInvariant('83') : void 0;
// 减少查找,并设置原型时间
// React中暴露给应用调用的方法,如render componentWillMount。
// 如果应用未设置,则将他们设为null
for (var methodName in ReactClassInterface) {
if (!Constructor.prototype[methodName]) {
Constructor.prototype[methodName] = null;
}
}
return Constructor;
},
injection: {
injectMixin: function (mixin) {
injectedMixins.push(mixin);
}
}
};
createClass主要做了:
定义构造方法Constructor,构造方法中进行props,refs等的初始化,并调用getInitialState来初始化state
调用getDefaultProps,并放在defaultProps类变量上。这个变量不属于某个单独的对象。可理解为static 变量
将React中暴露给应用,但应用中没有设置的方法,设置为null。
二、MOUNTING
MOUNTING阶段定义的方法是mountCompont,如ReactCompositeComponent.js下
所示
//初始化组件,注册事件监听
mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
//当前元素对应的上下文
this._context = context;
this._mountOrder = nextMountID++;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
// 添加到ReactComponentElement元素的props属性
var publicProps = this._currentElement.props;
// 通过Component.contextTypes过滤由上层组件注入的context属性,并做校验
var publicContext = this._processContext(context);
// 纯函数无状态组件、或者继承自PureComponent的纯组件构造函数、或者继承自Component的组件构造函数
var Component = this._currentElement.type;
// 传入组件ReactComponent的第三个参数updater,默认是ReactUpdateQueue模块,用于实现setState等方法
var updateQueue = transaction.getUpdateQueue();
// 校验是否纯组件或组件;返回否值,当作非状态组件、或ReactClass的工厂函数处理
var doConstruct = shouldConstruct(Component);
// 创建纯组件或组件实例,或者获取无状态组件的返回值
var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
// 待挂载的ReactComponentElement元素
var renderedElement;
// 无状态组件,没有更新队列它只专注于渲染
if (!doConstruct && (inst == null || inst.render == null)) {
renderedElement = inst;
warnIfInvalidElement(Component, renderedElement);
// 将无状态组件function(props,context,updateQueue){}包装为带有render原型方法的构造函数形式
inst = new StatelessComponent(Component);
this._compositeType = CompositeTypes.StatelessFunctional;
} else {
if (isPureComponent(Component)) {
// 添加纯组件标识
this._compositeType = CompositeTypes.PureClass;
} else {
// 添加组件标识
this._compositeType = CompositeTypes.ImpureClass;
}
}
// 原本作为构造函数的参数传入,为方便起见,再次赋值,同时保证实例数据的准确性
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
inst.updater = updateQueue;
// ReactInstanceMap中添加组件实例
this._instance = inst;
ReactInstanceMap.set(inst, this);
// 获取初始state,并提示state只能设置为对象形式
var initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
// 执行实例inst的render方法,嵌套调用mountComponent,将返回值ReactNode元素转化成DomLazyTree输出
var markup;
if (inst.unstable_handleError) {
markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
} else {
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
}
// 向后置钩子transaction.getReactMountReady()中添加实例的生命周期方法componentDidMount
if (inst.componentDidMount) {
if (process.env.NODE_ENV !== 'production') {
transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this);
} else {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
}
return markup;
}
performInitialMountWithErrorHandling: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
var markup;
var checkpoint = transaction.checkpoint();
try {
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onError();
}
}
transaction.rollback(checkpoint);
this._instance.unstable_handleError(e);
if (this._pendingStateQueue) {
// _processPendingState方法获取组件setState、replaceState方法执行后的最终state
this._instance.state = this._processPendingState(this._instance.props, this._instance.context);
}
checkpoint = transaction.checkpoint();
this._renderedComponent.unmountComponent(true);
transaction.rollback(checkpoint);
markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
}
return markup;
},
performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
var inst = this._instance;
// 执行组件实例的componentWillMount方法
// componentWillMount方法内调用setState、replaceState,_pendingStateQueue有值,刷新state后再行绘制
inst.componentWillMount();
if (this._pendingStateQueue) {
// _processPendingState方法获取组件setState、replaceState方法执行后的最终stat
inst.state = this._processPendingState(inst.props, inst.context);
}
}
// 间接执行ReactClass或TopLevelWrapper实例的render方法,获取待挂载的元素ReactNode
// 组件若为函数式无状态组件function(props,context,updateQueue){},renderedElement由传参提供
if (renderedElement === undefined) {
// 调用组件实例inst的render方法,获取待挂载的元素ReactNode
renderedElement = this._renderValidatedComponent();
}
var nodeType = ReactNodeTypes.getType(renderedElement);
this._renderedNodeType = nodeType;
var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
);
// render方法内子组件实例
this._renderedComponent = child;
var selfDebugID = 0;
// 嵌套调用mountComponent,完成renderedElement元素相应组件的实例化及render方法执行
// 最终通过ReactDomElement转化为DOMLazyTree对象输出,其node属性为需要插入文档dom对象
var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), selfDebugID);
return markup;
}
在组件挂载的过程中,propTypes和defaultProps分别代表props类型检查和默认类型。之后我们会看到两个方法componentWillMount和componentDidMount,其中,componentWillMount在render之前执行,componentDidMount在render之后执行分别代表了渲染前后的时刻。如果我们在componentWillMount执行setState方法,组件会更新state,但是组件只渲染一次。因此这是无意义的,初始化state可以放在this.state里。
其实,mountComponent本质上是通过递归渲染内容的,父组件的componentWillMount在子组件的componentWillMount之前调用,componentDidMount在子组件的componentDidMount之后调用。
看下面一组代码:
class Botton extends React.Component{
constructor(props){
super(props)
this.state = {
value:false,
pro : 'aaa'
}
}
componentWillMount(){
console.log('子组件willmount')
}
componentDidMount(){
console.log('子组件didmount')
this.setState({pro:'bbb'})
}
setSta(){
this.setState({
value: true,
})
}
set(){
this.setState({
pro: '1111',
})
}
render(){
console.log("子组件render")
let comp ,style;
const { value } = this.state;
return(
<div>
<button onClick={ this.setSta.bind(this) }>切换</button>
<button onClick={ this.set.bind(this) }>切换2</button>
</div>
)
}
}
class Wrap extends React.Component {
constructor(props){
super(props)
this.state = {
value : false
}
}
componentWillMount(){
console.log('父组件willmount')
}
componentDidMount(){
console.log('父组件didmount')
}
sets(){
this.setState({
value : true
})
}
render(){
console.log("父组件render")
let style;
if(this.state.value){
style = {
display : 'block'
}
}else{
style = {
display : 'none'
}
}
return(
<div>
<Botton style={style}/>
<button onClick={this.sets.bind(this)}>切换</button>
</div>
)
}
}
const handleChange = (value) => {
console.log(value)
}
ReactDOM.render(
<div>
<Wrap />
</div>,
document.getElementById('app')
)
运行结果:
Paste_Image.png首先父组件wrap 初始化 props state,之后执行wrap的componentWillMount方法,合并state,执行render方法,在render方法里调用Botton组件,之后执行Botton的componentWillMount -- render --componentDidMount 然后执行父组件componentDidMount。
三、RECEIVE_PROPS阶段
还是先看源码:
// 接受新的组件待渲染元素nextElement,以替换旧的组件元素this._currentElement
// 通过performUpdateIfNecessary方法调用,nextElement由this._pendingElement提供
// 该方法触发执行的实际情形是ReactDom.render(ReactNode,pNode)挂载的组件元素,其父节点pNode由react方式绘制
// 通过_updateRenderedComponent方法调用,nextElement为待变更的子组件元素
receiveComponent: function (nextElement, transaction, nextContext) {
var prevElement = this._currentElement;
var prevContext = this._context;
this._pendingElement = null;
this.updateComponent(transaction, prevElement, nextElement, prevContext, nextContext);
},
// 判断props变更情况,执行shouldComponentUpdate方法,重绘组件或者更改组件的属性
// 参数transaction,组件重绘时用于向子组件提供updater参数,setState等方法可用;以及实现componentWillMount挂载功能
// 参数prevParentElement变更前的组件元素ReactNode,nextParentElement变更后的组件元素,作为render方法渲染节点的父元素
// 参数prevUnmaskedContext更迭前的context,nextUnmaskedContext更迭后的context
updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
var inst = this._instance;
var willReceive = false;
var nextContext;
// 更新context
if (this._context === nextUnmaskedContext) {
nextContext = inst.context;
} else {
nextContext = this._processContext(nextUnmaskedContext);
willReceive = true;
}
var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;
// 包含仅待渲染元素的props变更
if (prevParentElement !== nextParentElement) {
willReceive = true;
}
// 更新context、或变更带渲染组件元素或其props时willReceive赋值为真,由父组件发起,调用componentWillReceiveProps方法
if (willReceive && inst.componentWillReceiveProps) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillReceiveProps');
}
}
inst.componentWillReceiveProps(nextProps, nextContext);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillReceiveProps');
}
}
}
//将最新的state合并到队列中
var nextState = this._processPendingState(nextProps, nextContext);
//更新组件
var shouldUpdate = true;
// 调用组件的shouldComponentUpdate判断是否需要重绘
// 纯组件不能设置shouldComponentUpdate方法,仅判断props、state是否变更
if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'shouldComponentUpdate');
}
}
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'shouldComponentUpdate');
}
}
} else {
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}
}
}
this._updateBatchNumber = null;
if (shouldUpdate) {
//重新更新队列
this._pendingForceUpdate = false;
// 执行componentWillUpdate方法,重绘组件实例render方法内待渲染的子组件,挂载componentDidUpdate方法
this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
} else {
// 只变更组件的部分属性,不开启重绘功能
this._currentElement = nextParentElement;
this._context = nextUnmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
}
},
// 执行componentWillUpdate方法,重绘组件实例render方法内待渲染的子组件,挂载componentDidUpdate方法
_performComponentUpdate: function (nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) {
var inst = this._instance;
var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
var prevProps;
var prevState;
var prevContext;
// 如果存在componentDidUpdate就将props、state、context保存一份
if (hasComponentDidUpdate) {
prevProps = inst.props;
prevState = inst.state;
prevContext = inst.context;
}
// 执行componentWillUpdate方法
if (inst.componentWillUpdate) {
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillUpdate');
}
}
inst.componentWillUpdate(nextProps, nextState, nextContext);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillUpdate');
}
}
}
this._currentElement = nextElement;
this._context = unmaskedContext;
//更新props、state、context
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
// 以更新子组件的方式或重新创建子组件的方式重绘render方法待渲染的子组件
this._updateRenderedComponent(transaction, unmaskedContext);
// 向后置钩子transaction.getReactMountReady()中添加实例的生命周期方法componentDidUpdate
if (hasComponentDidUpdate) {
if (process.env.NODE_ENV !== 'production') {
transaction.getReactMountReady().enqueue(invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext), this);
} else {
transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
}
}
},
// 以更新子组件的方式或重新创建子组件的方式重绘render方法待渲染的子组件
_updateRenderedComponent: function (transaction, context) {
// 组件render待渲染的子组件实例
var prevComponentInstance = this._renderedComponent;
var prevRenderedElement = prevComponentInstance._currentElement;
// _renderValidatedComponent方法调用组件实例inst的render方法,获取待挂载的元素
var nextRenderedElement = this._renderValidatedComponent();
// shouldUpdateReactComponent方法返回真值,更新组件实例;返回否值,销毁实例后、重新创建实例
// 组件元素的构造函数或key值不同,销毁实例后再行创建
// render方法子组件构造函数及key相同,通过ReactReconciler.receiveComponent方法更新子组件实例
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context));
} else {
//渲染组件
var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
ReactReconciler.unmountComponent(prevComponentInstance, false);
var nodeType = ReactNodeTypes.getType(nextRenderedElement);
this._renderedNodeType = nodeType;
var nextMarkup = ReactReconciler.mountComponent(child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context), selfDebugID);
if (process.env.NODE_ENV !== 'production') {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onSetChildren(this._debugID, child._debugID !== 0 ? [child._debugID] : []);
}
}
// 替换文档中挂载的Dom元素DomLazyTree
this._replaceNodeWithMarkup(oldHostNode, nextMarkup, prevComponentInstance);
}
},
updateComponent方法:负责判断props变更情况,调用componentWillReceiveProps方法,无论属性是否变化,只要父组件发生render的时候子组件就会调用componentWillReceiveProps。调用组件的shouldComponentUpdate判断是否需要更新。
_performComponentUpdate方法:负责执行componentWillUpdate方法,重绘组件实例render方法内待渲染的子组件,挂载componentDidUpdate方法。
_updateRenderedComponent方法:更新子组件的方式或重新创建子组件的方式重绘render方法待渲染的子组件
upadteComponent负责管理生命周期的componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate。
通过下面例子具体看一下:
class Botton extends React.Component{
constructor(props){
super(props)
this.state = {
value:false,
pro : 'aaa'
}
}
componentWillMount(){
console.log('子组件willmount')
}
componentDidMount(){
console.log('子组件didmount')
this.setState({pro:'bbb'})
}
componentWillUpdate(){
console.log('子组件WillUpdata')
}
componentDidUpdate(){
console.log('子组件DidUpdata')
}
componentWillReceiveProps(nextProps) {
console.log('子组件WillReceiveProps')
}
setSta(){
this.setState({
value: true,
})
}
set(){
this.setState({
pro: '1111',
})
}
render(){
console.log("子组件render")
let comp ,style;
const { value } = this.state;
if(value){
comp = (
<div >
你好
<p>123</p>
</div>
)
}else{
comp = (
<div >
ta好
</div>
)
}
return(
<div>
{comp}
<button onClick={ this.setSta.bind(this) }>切换</button>
<button onClick={ this.set.bind(this) }>切换2</button>
</div>
)
}
}
class Wrap extends React.Component {
constructor(props){
super(props)
this.state = {
value : false
}
}
componentWillMount(){
console.log('父组件willmount')
}
componentDidMount(){
console.log('父组件didmount')
this.setState({pro:'bbb'})
}
componentWillUpdate(){
console.log('父组件WillUpdata')
}
componentDidUpdate(){
console.log('父组件DidUpdata')
}
sets(){
this.setState({
value : true
})
}
render(){
console.log("父组件render")
let style;
if(this.state.value){
style = {
display : 'block'
}
}else{
style = {
display : 'none'
}
}
return(
<div>
<Botton style={style }/>
<button onClick={this.sets.bind(this)}>切换</button>
</div>
)
}
}
ReactDOM.render(
<div>
<Wrap />
</div>,
document.getElementById('app')
)
运行结果:
Paste_Image.png当我们点击父级切换按钮的时候调用sets方法,从而执行setState方法,于是父级组件开始进入RECEIVE_PROPS阶段。
Paste_Image.png从执行结果上看更新过程也是递归执行的。
shouldComponentUpdate:
组件挂载之后,每次调用setState后都会调用shouldComponentUpdate判断是否需要重新渲染组件。默认返回true,需要重新render。在比较复杂的应用里,有一些数据的改变并不影响界面展示,可以在这里做判断,优化渲染效率。
//在botton里添加
shouldComponentUpdate(){
return false;
}
结果
Paste_Image.png四、UNMOUNTING
生命周期的最后一个阶段componentWillUnmount代表将要卸载。
在componentWillUnmount内所有的状态都被置为null
if (this._renderedComponent) {
ReactReconciler.unmountComponent(this._renderedComponent, safely);
this._renderedNodeType = null;
this._renderedComponent = null;
this._instance = null;
}
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
this._pendingCallbacks = null;
this._pendingElement = null;
this._context = null;
this._rootNodeID = 0;
this._topLevelWrapper = null;
//清除公公类
ReactInstanceMap.remove(inst);
看完生命周期,我想大家应该对props、state有了新的认识。大家可以深入去读一些源码,了解react的生命周期,在编程上会给大家更多的帮助。
我们试着分析一下上面的那个demo:
1.最外层组件wrap我们并没有设置props所以它的props是空的,
2.this.state即getInitialState 设置了一个value = false
3.执行自己的componentWillMount方法
4.合并state
5.执行render方法,
<Botton style={{display:'none'}}/>
我们在此给 Botton设置了 style 属性
6.执行getDefaultProps方法设置style属性
7.执行getInitialState方法即this.state = {
value:false,
pro : 'aaa'
}
8.执行componentWillMount方法
9.合并state
10.执行render方法
11.执行componentDidMount方法
12.执行父级Wrap的componentDidMount方法,此时我们设置
setState({
value : true
})
13.Wrap会执行componentWillUpdate方法(shouldComponentUpdate默认为更新)
14.创建component实例
15.render
16.子组件调用componentWillReceiveProps
17.shouldComponentUpdate默认为更新
18.调用componentWillUpdate
19.创建component实例 render
20.componentDidUpdate
21.Wrap组件componentDidUpdate
当点击 切换按钮时
继续13-21.
有错欢迎指正。
下一篇我们深入挖掘 setState 函数。
你也可以看其他文章
React基础2--深入挖掘setState
React基础3--diff算法
网友评论