如果你熟悉avalon,使用过 data-include-rendered 和 data-include-loaded 等回调方法,那么你会很好地理解React组件的各个生命周期。说白了其实就是React组件状态变化前后的时间点,我们可以利用生命周期的接口在相应的时间点做回调操作。React的官方文档提及了如下几个组件的生命周期:Mounting/组件挂载相关: componentWillMountcomponentDidMountUpdating/组件更新相关:componentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdateUnmounting/组件移除相关:componentWillUnmount 下面我们将通过一些实例来理解它们。顺便说下本文的示例都可以在我的github上下载到。一. Mounting/组件挂载相关componentWillMount在组件挂载之前执行操作,但进执行一次,即使多次重复渲染该组件,或者改变了组件的state:
componentWillMount123vari=0;varComponent1=React.createClass({componentWillMount:function(){console.log(i++)},getInitialState:function() {return{isClick:!1}},clickCb:function() {this.setState({isClick :!0})},render:function() {returnisClick:{this.state.isClick?'yes':'nope'}
}});vardiv=document.getElementById('a');React.render(,div);React.render(,div);如果希望该回调能执行多次,可以使用 React.unmountComponentAtNode(该方法我们下篇文章再介绍)移除掉已有的组件,然后再重新 render:
componentWillMount123vari=0;varComponent1=React.createClass({componentWillMount:function(){console.log(i++)},getInitialState:function() {return{isClick:!1}},clickCb:function() {this.setState({isClick :!0})},render:function() {returnisClick:{this.state.isClick?'yes':'nope'}
}});vardiv=document.getElementById('a');React.render(,div);React.unmountComponentAtNode(div);//移除掉已有组件React.render(,div);可以看到输出了两行:componentDidMount顾名思义可以猜到这个是在组件初始化挂载之后执行的,比如我们可以利用它来隐藏掉页面的loading菊花层。同 componentWillMount 一样,同一个组件重复渲染只执行一次,卸载组件后重新渲染可以重新触发一次:
componentDidMount123123vari=0,div=document.getElementById('a'),div2=document.getElementById('b');varComponent1=React.createClass({componentDidMount:function(){console.log(i++)},clickCb:function() {React.render(, div2);},render:function() {return点我给下一个div挂载组件
}});React.render(, div);//React.unmountComponentAtNode(div); //移除掉已有组件React.render(, div);注意上述代码点击div1时会将组件挂载到div2上,触发div2的组件的 componentDidMount 回调(毕竟div1和div2上的组件并非同一个)。二. Updating/组件更新相关:componentWillReceiveProps在组件接收到新props的时间点之前调用,注意组件初始化渲染时则不会执行:
componentWillReceiveProps123123vari=0,div=document.getElementById('a'),div2=document.getElementById('b');varComponent1=React.createClass({componentWillReceiveProps:function(){console.log(i++)},clickCb:function() {React.render(, div2);},render:function() {return点我给下一个div挂载组件
}});React.render(, div//初始化不会触发componentWillReceiveProps);React.render(, div//重复渲染会触发componentWillReceiveProps);React.unmountComponentAtNode(div);//移除掉已有组件React.render(, div//初始化不会触发componentWillReceiveProps);注意我们移除掉组件再挂载的时候,相当于重新初始化渲染了组件(得到的props是初始化props而不是新props),故不会触发 componentWillReceiveProps 。而当我们在div2挂载了组件后再点击div2来重新渲染它的组件,会触发 componentWillReceiveProps :该接口带有一个参数 nextProps,我们可以利用它来获取新 props 的值(this.props 获取到的是当前的,也就是旧的 props):
componentWillReceiveProps123vari=0,div=document.getElementById('a'),render=function(){React.render(, div);};varComponent1=React.createClass({componentWillReceiveProps:function(nextProps){console.log(this.props.i, nextProps.i)},render:function() {returnprops.i的值是:{this.props.i}
}});render();通过点击div1的组件,可以输出 componentWillReceiveProps 时间点(这时候还没重新执行渲染)的 props 以及即将获取到的新 props,执行如下:shouldComponentUpdate前面咱们学习的接口都是叫 componentXXX,而这个把 should 放在前面,翻译过来其实就是“是否应该XXX”的意思,那么可以把该接口直接理解为“组件是否应该做更新”的意思,即其了一个决定组件要不要重新渲染的作用。该接口实际是在组件接收到了新的 props 或者新的 state 的时候(该时间点render还没执行哦)会立即调用,然后通过返回值(Boolean)来决定是否要重新渲染当前的组件。该接口带有两个参数,第一个参数表示新的props,第二个参数表示新的state。我们来个例子,比方要求div要点击3次之后,才重新渲染自身组件:
shouldComponentUpdate123vardiv=document.getElementById('a');varComponent1=React.createClass({getInitialState:function(){return{ i :0}},shouldComponentUpdate:function(nextProps, nextState){console.log(this.state.i, nextState.i );returnnextState.i>3?true:false;//返回true才会渲染组件},clickCb:function(){this.setState({i :this.state.i+1})},render:function() {returnstate.i的值是:{this.state.i}
}});React.render(, div);执行如下,点击第四次之后才会渲染组件,在div里显示出正确的新state.i:componentWillUpdate同 shouldComponentUpdate 一样,在组件收到新的 props 或者 state 的时候会立即调用,而且也有着俩个参数来获取新的 props 和 state。不过本接口会在 shouldComponentUpdate 执行并返回了 true 的时候才会被调用。我们拿上一个代码示例做点小修改:
componentWillUpdate123vardiv=document.getElementById('a');varComponent1=React.createClass({getInitialState:function(){return{ i :0}},shouldComponentUpdate:function(nextProps, nextState){console.log(this.state.i, nextState.i );returnnextState.i>3?true:false;//返回true才会执行componentWillUpdate并重新渲染组件},componentWillUpdate:function(nextProps, nextState){console.log('yoyoyo',this.state.i, nextState.i );},clickCb:function(){this.setState({i :this.state.i+1})},render:function() {returnstate.i的值是:{this.state.i}
}});React.render(, div);利用这个接口,我们可以在组件要重新渲染之前做一些需要的改动。componentDidUpdateDid表示完成时状态,故该接口会在组件更新、重新渲染完毕了之后才触发,它和 componentWillUpdate 一样有着俩个参数来获取新的 props 和 state。我们继续拿上一个例子来做修改:
componentDidUpdate123vardiv=document.getElementById('a');varComponent1=React.createClass({getInitialState:function(){return{ i :0}},shouldComponentUpdate:function(nextProps, nextState){console.log(this.state.i, nextState.i );returnnextState.i>3?true:false;//返回true才会执行componentWillUpdate并重新渲染组件},componentDidUpdate:function(nextProps, nextState){console.log('已经渲染完毕咯',this.state.i, nextState.i );},componentWillUpdate:function(nextProps, nextState){console.log('还没渲染哦',this.state.i, nextState.i );},clickCb:function(){this.setState({i :this.state.i+1})},render:function() {returnstate.i的值是:{this.state.i}
}});React.render(, div);执行如下:三. Unmounting/组件移除相关:componentWillUnmount 在组件要被移除之前的时间点触发,可以利用该方法来执行一些必要的清理,比如清除无效的定时器,或者清除在 componentDidMount 中创建的 DOM 元素等:
componentWillUnmount123这里是div2,点击我会移除上面div的组件
vardiv=document.getElementById('a'),div2=document.getElementById('b');varComponent1=React.createClass({DOMArr : [],getInitialState:function(){return{ i :0}},componentDidUpdate:function(nextProps, nextState){vardom=document.createElement('p');dom.innerText=this.state.i;div2.appendChild(dom);this.DOMArr.push(dom);},componentWillUnmount:function(){if(!this.DOMArr.length)return;vari=0;while(istate.i的值是:{this.state.i}
}});React.render(, div);div2.addEventListener('click',function(){React.unmountComponentAtNode(div)//点击div2则卸载掉第一个div里的组件},false)执行如下:至此我们便学习完了React组件里的共七个声明周期的接口,最后出道题给大家思考下,下面的代码输出的顺序应该是什么呢:
题目varComponent1=React.createClass({componentWillMount:function(){console.log('componentWillMount')},componentDidMount:function(){console.log('componentDidMount')},componentWillReceiveProps:function(){console.log('componentWillReceiveProps')},shouldComponentUpdate:function(){console.log('shouldComponentUpdate');returntrue;},componentWillUpdate:function(){console.log('componentWillUpdate')},componentDidUpdate:function(){console.log('componentDidUpdate')},componentWillUnmount:function(){console.log('componentWillUnmount')},render:function() {return我只是一个安静的div
}});React.render(, document.body);React.render(, document.body);React.unmountComponentAtNode(document.body)建议思考完了再往下滚动看答案吧,如果想不起来,可以翻到文章前面在回顾一下,多温习多思考,总是好习惯。顺便再提一下,本文的全部示例都可以在我的github上下载到。关于上方问题的答案如下:最后建议大家多实践,不局限于看文章。也希望本文能对你有所帮助,共勉~本文链接:ReactJS入门(二)—— 组件的生命周期,转载请注明。