React几个重要的生命周期
本章主要介绍了React中6个比较重要的生命周期,及它们在类组件中的使用方式。
什么是组件的生命周期?
在之前我们学习了DOM的一些操作:创建元素(createEelement),元素的挂载(appendChild),元素内容的修改(innerText,innerHTML),元素的移除(remove)。类比这些,我们把组件的创建,挂载,更新,销毁的过程称作组件的生命周期,当然组件的生命周期可能比这些要更复杂。
React的6个生命周期函数
和Vue的生命周期函数(钩子)一样,React的生命周期函数(钩子)也是在组件运行的不同阶段执行的函数。
作为初次学习React,我们至少需要掌握6个React的生命周期钩子,当然React不止这6个钩子。
钩子列表:
constructor()shouldComponentUpdate()render()componentDidMount()componentDidUpdate()componentDidUnmount()
通过了解React的钩子种类后,我们发现其实在创建组件时我们就已经使用过2个React的钩子:constructor() 和 render()。
在详细地了解这6个钩子之前,我们先看一张钩子执行顺序的图:

constructor()
-
用途:用于初始化 props,state及函数。
-
细节与特点:
-
这个生命周期钩子在组件只需要初始化props时可以省略不写,但是只要手动写了这个钩子,就必须写完整:
extends,constructor(props),super(props)class myComponent extends from React.Component{ constructor(props){ super(props) this.state = {...} } } -
组件内部函数的定义其实是在 constructor 中定义的。但是由于比较麻烦(具体回看React组件基础相关部分),React提供了一个语法糖,让开发者不需要把函数写在constructor内部。
-
不要在这个阶段使用
setState因为此时 state 还未被初始化。
-
shouldComponentUpdate(newProps,newState )
-
用途:
在组件更新(update)前判断是否更新。函数返回
true则更新,返回false则不更新。 -
应用场景:
比如在某些时候我们
setState后,更新了数据,因为setState是用一个新的对象来覆盖旧的对象,那么就可能会出现,state 的地址发生了变化,但state中数据的值并没有发生变化,此时我们就能通过这个函数来阻止页面不必要的更新,以节省性能。 -
React更新UI的过程图:

-
函数的2个参数:
这个钩子有2个参数,第一个是组件更新前新的props,第二个就是新的state。 这样我们就能很方便地在这个钩子内部进行数据的比对了。
shouldComponentUpdate(newProps,newState){ if(newState.n === this.state.n){ return false }else{ return true } } -
利用 PureComponent 来代替这个钩子:
我们使用这个钩子的大多数情况就是用于比对数据变更前后,props和state的数据的值是否发生了变化,以此作为更新页面的依据。
那React对此提供一个 PureComponent 来简化这个过程。
PureComponent的使用很简单,就是把
class App extends from React.Component替换成... React.PureComponent。PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
当然PureComponent的比对是浅比对,即:只对比数据的第一层 。
render()
-
用途:
根据函数返回的虚拟DOM,展示视图。
-
虚拟DOM的定义及细节:
- render 返回的是一个类似 DOM元素的东西(
<div>....</div>),但这不是DOM元素,而是表示DOM元素的对象,称为:虚拟DOM。 - 虚拟DOM可以 log 出来。
- 虚拟DOM,或者说 render 返回的内容,必须且只有一个闭合的根标签。正常来说我们会用一个
<div>作为根标签,如果想不把那个根标签渲染到DOM树上怎么办呢?React提供了一个标签占位符:<React.Fragment>...</React.Fragment>,简写:<>...</>,这个占位就模拟了一个根标签,但是并不会渲染到DOM树上。 - 可以用一个变量来存储虚拟DOM,如:
message = <div>哈哈</div> - 在虚拟DOM中使用变量或者JS表达式:
return ( {message} <button>+1</button> ),用{}包裹变量名,{}不仅可以包裹变量名,还能写JS表达式,如:return ({ this.state.n%2===0?<div>偶数</div>:<div>奇数</div> } <button>+1</button>)。
- render 返回的是一个类似 DOM元素的东西(
-
render的使用经验:
-
既然这是一个(生命周期)函数,那么我们其实可以在这里面做其他想做的事情,而不是仅仅只能渲染视图,比如:在函数中 log 一下查看这个函数是否被调用了。
-
render的选择性渲染:
当我们需要根据某些数据的特性,渲染不同的内容时,我们可以通过if else,或三元表达式来选择 return 不同的虚拟DOM,或则将虚拟DOM保存在某个变量中。这样我们就能根据数据的不同来渲染不同的内容。
如:
return ({ this.state.n%2===0?<div>偶数</div>:<div>奇数</div> } <button>+1</button>) -
render中使用循环:
render里面写for 循环的时候注意,因为第一次return的时候就跳出循环了。一般来说我们会使用数组的map来代替循环。注意:循环返回的虚拟DOM都要绑定key。
render(){ // 注意绑定了key,以及在虚拟DOM中使用变量要使用 {} return this.state.array.map( n => <span key={n}> {n} </span>) }
-
componentDidMount()
-
用途:
在元素或组件挂载到页面后执行的钩子函数。
-
使用情景:
一般这个生命周期会和DOM元素联合使用,就像Vue中的Mount一样。在这个生命周期函数中DOM已经被挂载到页面上,我们可以获取到DOM元素的样式及其他信息,同时也能够操作DOM。换句话说,需要操作DOM的操作可以在这个钩子中进行。
-
React中获取DOM元素的技巧:
-
通过选择器:
document.querySelector()ordocument.getElementById() -
通过React提供的ref:
ref可以使用到DOM元素上,也可以使用到React组件上。
使用ref的时候的好习惯:先声明
xxxRef = undefined告诉阅读代码的人这里使用了多少的ref然后再创建ref。这样可以便于代码的维护与阅读。class App extends from React.Component{ // 先声明ref提示代码阅读者一共创建了多少个ref divRef = undefined constructor(props){ super(props); this.state = {}; // 创建ref this.divRef = React.createRef() } render(){ // 绑定ref到DOM元素上 return (<div id="haha" ref={this.divRef}></div>) } // 后续就能在其他函数中通过this.divRef来获取到这个id为haha的元素了 }
-
-
componentDidMount的使用技巧:
- 官方推荐Ajax加载数据的请求在这个阶段发起。
- 首次渲染就会执行一次这个生命周期函数。
componentDidUpdate(preProps,preState,snapshot )
-
用途:
视图更新后执行钩子函数。
-
使用经验:
-
这里也可以进行Ajax请求,一般是更新数据的请求。
-
首次渲染不会执行该生命周期函数。
-
在这个生命周期函数中执行 setState 要特别注意不要引起死循环。
-
当生命周期 shouldComponentUpdate 返回 false 后就不会执行这个生命周期函数。
-
-
参数:
该函数有3个参数。常用的是前两个,第一个是之前的Props,第二个是之前的State,而第三个参数用得比较少。
componentWillUnmount()
-
用途:
在组件被移出页面然后被销毁之前执行的代码。注意:移出页面然后被销毁,是2个动作,移除页面是移除DOM树,销毁指删除其在内存中的数据。 unmount过后的组件不会再次mount。
-
使用场景:
清除一些具有持久性影响的操作。如:事件监听,创建的Timer,发送的Ajax请求,这些操作因为组件或元素的移除与销毁都会变得没有意义,但是仍占据内存,一个优秀的前端要释放这些没用的内存。