本文主要是介绍玩转Reactjs第四篇-组件(生命周期),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、前言
前一篇主要介绍了Reactjs组件的创建模式,了解了state以及props的基本知识和用法。本篇将重点介绍下Reactjs的生命周期这对于我们认识Reactjs的实现原理,正确使用钩子函数至关重要。
Reactjs在V16.3版本之前使用的是旧版本的生命周期,之后启用了新版的生命周期,V17版本将彻底废弃旧版本。我们将两个版本都介绍下。
二、旧版本
Reactjs生命周期包括挂载时(Mounting),更新时(Updateing),卸载时(Unmounting)三种场景,每种场景在不同状态提供相关的钩子函数,我们可以使用这些钩子函数,实现自定义功能。
生命周期钩子函数时序的示意图如下:
1、挂载时(Mounting)
Reactjs组件首次加载时执行的流程,constructor->componentWillMount->render->componentDidMount。
- constructor
调用时机:组件挂载前会调用构造函数。
钩子作用:前一篇我们也讲到,在构造方法里,主要做以下两件事:
(1)初始化state,这里是唯一一个可以使用this.state进行初始化的地方,其他的地方只能使用setState更新。
(2)组件内定义的方法绑定实例,前一篇也讲到如果不用箭头函数定义方法,必须在构造器中绑定this。
如果不需要使用以上两点,可以不定义组件的构造函数。
函数示例:示例如下:
constructor(props){// 1、调用父类构造方法super(props);//2、初始化state对象this.state = {hotItem:['口罩','手套','酒精',]};//绑定thisthis.changeHot = this.changeHot.bind(this);
}
- componentWillMount(即将废弃)
在方法即将废弃,可以使用到React 17版本,但是会和新的组件生命周期钩子函数冲突,建议不要使用。
调用时机:在调用render()前调用。
钩子作用:在该方法里面调用setState不会触发额外的渲染,所以想要在该方法中请求外部数据,减少一次render,是一个常见的误区,总的来说该方法没有太大的作用。
- render
调用时机:首次挂载以及更新时,都会调用该方法进行渲染。
钩子作用:render方法是class组件中唯一需要实现的方法。在该钩子方法里,将React JSX渲染成DOM节点,并挂载到DOM树中。
render函数应该是纯的,意味着不应该改变组件的状态,其每次调用都应返回相同的结果,同时它不会直接和浏览器交互。
函数示例:该方法的实现形式,前一篇我们也重点介绍了,如App.js中,实例如下:
render(){return (<div className="App">{/* 引入组件 */}<SearchBox changeSearchVal = {this.changeSearchVal}/><SearchHot /><SearchList searchListVal={this.state.searchListVal}><div>为您搜索到2条记录</div></SearchList></div>);}
- componentDidMount
调用时机:执行完render函数(DOM节点已创建完成并挂载到DOM数中(节点插入DOM数)),立即执行。
钩子作用:在componentDidMount中可以进行以下的操作:
(1)请求网络数据,并调用setState进行更新,它将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了在此情况下即使render()将会调用两次,用户也不会看到中间状态
(2)通过ref获取DOM的节点,获取DOM属性或者操作DOM。
函数示例:在App.js中,我们增加componentDidMount方法,在该钩子函数中获取默认的搜索列表,这样就能实现在首次加载完成后,页面展示默认列表。
componentDidMount(){console.log("App componentDidMount");fetch("http://localhost:3000/search.json",{method:'GET',}).then(response => response.json()).then((data)=>{this.setState({searchListVal:data.search});})}
2、更新时(Updateing)
挂载完成后,当传入父组件的props,以及组件内的state值发生变化,会触发更新时流程。componentWillReceiveProps->shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate
- componentWillReceiveProps(nextProps)(即将废弃)
该钩子函数将在React 17版本废弃。
调用时机:在已挂载的组件接收新的 props 之前被调用。
钩子作用:当接收到新属性后,通过if比较新旧Props不同,此方法中使用 this.setState()
执行 state 转换。这个和新版本的getDerivedStateFromProps作用类似。
- shouldComponentUpdate(nextProps,nextState)
调用时机:props或者state发送变化后,render调用前调用该钩子函数。父组件更新prop,或者调用setState都会触发。但是首次挂载以及forceUpdate不会触发该方法。
钩子作用:该函数可以决定是否继续渲染(是否执行render方法),入参为新的props和state,返回的是个布尔值,true表示继续渲染,false则表示阻止渲染,默认返回true。
在Vue中,通过监听相关属性值的变化,通过机制自动判断是否需要渲染,无法"人为"干预。在Reactjs中,利用shouldComponentUpdate为我们提供了干预的契机,进行精确控制。
适用场景:在某些状态或属性变化时,通过this.props和nextProps以及this.state 和 nextState比较,来判断是否需要重新渲染该组件。
函数示例:我们来改写下SearchHot.js,当热搜的个数有变化时,返回false,不继续渲染。
shouldComponentUpdate(nextProps,nextState){console.log(nextState.hotItem.length);if(this.state.hotItem.length != nextState.hotItem.length){return false;}return true;}
由于state.hotItem新旧值的长度不一致,所以点击"下一批",页面不会有变化。
需要注意的是,state和props需要保持结构简单,层次不要太深,不建议在 shouldComponentUpdate()
中进行深层比较或使用 JSON.stringify()
。这样非常影响效率,且会损害性能。
如果仅做浅层次比较,可以考虑使用React.PureComponent。
- componentWillUpdate(nextProps, nextState)(即将废弃)
该钩子函数将在React 17版本废弃,
调用时机:当组件收到新的 props 或 state 时,会在渲染之前调用。
钩子作用:入参为新的props以及state,使用此作为在更新发生之前执行准备更新的机会,初始渲染不会调用此方法。
适用场景:可以读取DOM属性,为componentDidUpdate作准备(使用场景少)。不能在此方法中调用 this.setState()
- componentDidUpdate(prevProps, prevState, snapshot)
调用时机:在组件已经重新渲染之后调用,首次渲染不会执行此方法。
钩子作用:入参为前props,state值,第三个参数与新版本getSnapshotBeforeUpdate有关,下一节介绍。其作用类似componentDidMount,此时已经挂载完成,可以做DOM进行操作,也可以进行网络请求。
使用场景:对DOM进行操作,比如滚动条指定到 位置,获取DOM尺寸等。在该方法里,可以调用setState,但是要包裹在一定的条件中,否则会 引起死循环。调用setSate会引起再一次的渲染,虽然用户看不见(此时浏览器还没有变化),但是会影响性能。
函数示例: 我们改写下 SearchList.js,每次更新完,滚动条指向列表的指定位置。(这里使用了ref获取dom对象,后面会专门介绍)
componentDidUpdate(prevProps, prevState, snapshot){console.log("componentDidUpdate");//列表的滚动条指向60的位置this.searchul.current.scrollTop=60;}
...
render(){...<ul className="u" ref={this.searchul}>
...
}
3、卸载时(Unmounting)
这个阶段是指组件将被删除并从DOM中清除。
- componentWillUnmount()
调用时机:在组件卸载及销毁之前直接调用。
适用场景:此方法中执行必要的清理操作,例如,清除 timer,取消网络请求。不要在此方法中调用setState,因为该组件永远不会渲染。
三、新版本
V16.3版本启用了新版本生命周期钩子函数,不过此版本还可以兼容老版本的钩子函数,与老版本一样,也分三种场景。
生命周期的钩子函数时序的示意图如下:
与老版本比较,钩子函数发送如下变化:
删除:componentWillMount,componentWillReceiveProps,componentWillUpdate
新增:getDerivedStateFromProps,getSnapshotBeforeUpdate。
其他的函数保留。我们重点介绍下新增的。
- static getDerivedStateFromProps(nextProps,preState)
调用时机:props以及state发送更新后,会在调用 render 方法之前调用发。与旧的生命周期图比较可以看出,getDerivedStateFromProps处于原来的componentWillMount与componentWillReceiveProps位置,setState以及forceUpdate方法的调用都会触发该钩子。
钩子作用:从钩子的函数名称可以看出,用接受的props值来改变state值,返回一个新的state。它的入参是新props,旧state,该函数为一个静态的,这就表示无法使用组件实例(无法调用this)。
适用场景:组件分为受控和非受控组件,受控是指数据由父组件的props传入的组件(可以由父组件控制),非受控是指数据保存在内部的state的组件(外部无法直接控制)。如果state可以由props派生出,那么就意味着该组件既是受控的,又是非受控的。其数据源就不唯一了,会导致一些意想不到的问题。通过props派生出state,是我们尽量需要避免的情况。
官方文档中,也为我们罗列了以下两种常见场景的替代方案,在你可能不需要使用派生 state这篇博文中进行了详细的介绍。
(1)如果只想在 prop 更改时重新计算某些数据,请使用 memoization helper 代替。
(2)如果你想在 prop 更改时“重置”某些 state,请考虑使组件完全受控或使用 key 使组件完全不受控 代替。
函数示例:SearchList组件中,搜索的结果列表都是由父组件App通过props传给该组件,下面我们修改下,在SearchList定义一个默认列表保存到state中,当父组件传入的prop的列表结果更新后,修改state的值,实现更新。
constructor(props){super(props);this.searchul = React.createRef();//定义默认搜索列表this.state={searchListVal:['电脑','电视','冰箱']};}static getDerivedStateFromProps(nextProps,preState){//当props的列表发送更新,且与state的列表不一致,且不为空if(nextProps.searchListVal!=preState.searchListVal&&nextProps.searchListVal.length > 0){//修改state的searchListVal值,返回新值return{searchListVal:nextProps.searchListVal}}//其他更新,state不变化return null;}
- getSnapshotBeforeUpdate(prevProps, prevState)
调用时机:在最近一次渲染输出(提交到 DOM 节点)之前调用。
钩子作用:它让你的组件能在当前的值可能要改变前获得它们。这一生命周期返回的任何值将会 作为参数被传递给componentDidUpdate(),即snapshot入参。
适用场景:适用的场景也不常用,它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。
函数示例:在SearchList组件中,我们通过componentDidUpdate,每次加载列表,将滚动条滚动到指定的位置。下面我们再次修改下,获取滚动条之前的位置,在此基础上再累加一个指定值,模拟懒加载的情况下,每次显示最新加载的值(列表底部)。
getSnapshotBeforeUpdate(prevProps, prevState){//获取当前滚动条位置return this.searchul.current.scrollTop;}componentDidUpdate(prevProps, prevState, snapshot){//在滚动条位置上,累加60个if(snapshot!=null){this.searchul.current.scrollTop=snapshot+60;} }
四、与Vue的生命周期比较
这里我们将Reactjs的生命周期与Vue的比较下。
1、挂载时
2、更新时
3、销毁时
五、总结
本章节主要描述了Reactjs的新旧生命周期以及相关钩子函数的调用时机,功能,以及适用场景,并与vue的生命周期做了比较。
老版本的生命周期钩子函数的时序:
1、挂载时,constructor->componentWillMount->render->componentDidMount
2、更新时,componentWillReceiveProps->shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate
3、卸载时,componentWillUnmount
新版本的生命周期钩子函数的时序:
1、挂载时,constructor->getDerivedStateFromProps->render->componentDidMount
2、更新时,getDerivedStateFromProps->shouldComponentUpdate->render->componentDidUpdate
3、卸载时,componentWillUnmount
这篇关于玩转Reactjs第四篇-组件(生命周期)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!