目录
- 生命周期(旧)
- 生命周期
- componentWillMount
- componetdidMount
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- componentWillReceiveProps
- 组件的挂载、更新、销毁
- 案例1
- 渲染过程
- 更新过程1-通过setState去修改数据
- 更新过程2-通过forceUpdate强制更新
- 销毁过程
- 父子组件的挂载、更新、销毁
- 案例2
- 渲染过程
- 更新过程1-通过setState去修改数据(num)
- 更新过程1-通过setState去修改数据(value)
- 更新过程3-通过forceUpdate强制更新
- 销毁过程
- 生命周期(新)
- 生命周期图
组件从创建到销毁会经历一系列的特定的阶段,React组件中会包含一系列的生命周期钩子,会在特定时刻调用。
在此将以类组件为例,讲述一下组件的挂载、更新、卸载的过程。
生命周期(旧)
生命周期
componentWillMount
componetdidMount
shouldComponentUpdate
在react开发中, 大部分数据需要使用state存储,更改时使用setState去修改。
默认情况下,在通过setState去修改数据时会重新渲染整个组件树,若是在某次setState更改数据时不想要重新渲染,可以在shouldComponentUpdate
生命周期钩子中做判断。
在该生命周期函数中,若是返回值为true表示重新渲染,若是返回值为false表示不重新渲染(即使数据改变了)
默认
shouldComponentUpdate(nextProps, nextState){
return true
}
若是在组件中没有显示声明该生命周期,默认情况下会重新渲染整个dom树。
声明
声明该函数时,当通过setState去修改数据时,react会调用此函数并传入两个参数
- nextProps : 表示下一个props
- nextState: 表示下一个state的值
可以对比当前props与下一次的props、当前state与下一次的state来决定是否需要重新渲染组件;
若是当前state中传入的值和通过props接收的值在修改时都需要重新渲染,那么就不需要声明该函数。
componentWillUpdate
componentDidUpdate
该组件有两个参数 preState preprops
componentWillUnmount
componentWillReceiveProps
组件即将接受props
组件的挂载、更新、销毁
案例1
- 如上图 数量num存储在state中;
- 当点击按钮 “点我加1” 时会通过
setState
去修改state中的num值; - 当点击 “点我强制更新” 时会通过
forceUpdate
去强制更新页面; - 当点击 “点我销毁组件” 时会 取消该组件的挂载;
<div id="test"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- 案例:数值 按钮(每次点击按钮数字加1) -->
<script type="text/babel">
class Mycomponent extends React.Component{
constructor(props){
console.log('---constructor---')
super(props)
this.state={num:0}
}
render(){
console.log('---render---')
return (
<div >
<span ref='span'>{this.state.num}</span>
<button onClick={this.addNum}>点我加1</button>
<button onClick={this.force}>点我强制更新</button>
<button onClick={this.unmount}>点我销毁组件</button>
</div>
)
}
componentWillMount(){
console.log('---componentWillMount---', this.state, this.refs) //{num:0},{}
}
componentDidMount(){
console.log('---componentDidMount---', this.state, this.refs) // {num:0},{span:span(dom)}
}
shouldComponentUpdate(){
console.log('---shouldComponentUpdate---')
// return false // 及时通过setState更新状态视图也不会重新渲染
return true // 默认返回值
}
componentWillUpdate(){
console.log('---componentWillUpdate---')
}
componentDidUpdate(){
console.log('---componentDidUpdate---')
}
addNum = ()=>{
const {num} = this.state
this.setState({num:num+1})
}
force = ()=>{
this.forceUpdate()
// 不会判断需不需要更新-直接更新
}
unmount = ()=>{
ReactDOM.unmountComponentAtNode( document.getElementById('test')) // 销毁组件而不能销毁节点
}
componentWillUnmount(){
console.log('---componentWillUnMount----')
}
}
ReactDOM.render(<Mycomponent /> , document.getElementById('test'))
</script>
渲染过程
结合生命周期图以及案例1理解一下渲染过程:
- [1]
发现标签名首字母大写 -> 去寻找对应的组件ReactDOM.render(<Mycomponent /> , document.getElementById('test'))
- [2] 发现组件是一个类组件 -> 通过new关键字实例化对象
- 在实例化对象过程中,会自动调用 构造器(
constructor
) -> 创建实例对象并修改this指向;
- 在实例化对象过程中,会自动调用 构造器(
- 获取到实例化对象后
- [3] 通过实例化对象调用
componentWillMount
函数 -> 在此已经初始化数据但是还没有挂载dom - [4] 通过实例化对象调用
render
函数 -> 在此获取虚拟dom - [5] 通过实例化对象调用
componetdidMount
函数 -> 在此vdom已经挂载到dom元素上
- [3] 通过实例化对象调用
因此在案例1中,一进入页面就会在控制台打印如下
更新过程1-通过setState去修改数据
结合生命周期图以及案例1理解一下更新过程:
- [1] 当点击“点我加1”按钮时,会通过setState去修改state中的数据
- [2] 当通过setState去修改数据时,react会通过实例化对象调用
shouldComponentUpdate
-> 判断组件是否应该被更新- 返回值为true -> 组件确认被更新
- [3] 调用
componentWillUpdate
->组件即将被更新 - [4] 调用
render
-> 获取更新后的vdom - [5] 调用
componentDidUpdate
-组件已经被更新
- [3] 调用
- 返回值为false -> 组件确认不更新 ->结束
- 返回值为true -> 组件确认被更新
因此在案例1中,点击“点我加1”按钮时就会在控制台打印如下
若是将shouldComponentUpdate钩子的返回值改为false, 则点击“点我加1”按钮时就会在控制台打印如下
–> 返回值为false更新停止;
更新过程2-通过forceUpdate强制更新
结合生命周期图以及案例1理解一下更新过程:
- 当点击“点我强制更新”按钮时,会去重新渲染整个组件树(不会去判断是否需要更新)
- 更新过程
- [1]调用
componentWillUpdate
->组件即将被更新 - [2] 调用
render
-> 获取更新后的vdom - [3] 调用
componentDidUpdate
-组件已经被更新
- [1]调用
因此在案例1中,点击“点我强制更新”按钮时就会在控制台打印如下
销毁过程
结合生命周期图以及案例1理解一下销毁过程:
- 当点击“点我销毁组件”按钮时,会去卸载dom
- 销毁过程
+ 调用componentWillUnmount
->组件即将被销毁
因此在案例1中,点击“点我销毁组件”按钮时就会在控制台打印如下
父子组件的挂载、更新、销毁
案例2
- 如上图 在父组件的state中定义了两个数据-value、num
- value在自身组件中使用
- num传递给了子组件在子组件中使用
- 当点击 “点我修改num” 按钮时会通过setState去修改 父组件的num值;
- 当点击“点我修改value” 按钮时会通过setstate去修改父组件的value值;
- 当点击 “点我强制刷新” 按钮时会重新渲染父组件的组件树;
<div id="test"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
class Fa extends React.Component{
constructor(props){
super(props)
this.state ={ value:111, num:0 }
console.log('父组件-constructor')
}
render(){
console.log('父组件-render')
return (
<div>
<h1>我是父组件</h1>
{this.state.value}
<button onClick={this.editnum}> 点我修改num</button>
<button onClick={this.editvalue}> 点我修改value</button>
<button onClick={this.force}> 点我强制刷新</button>
<Son num={this.state.num}/>
</div>
)
}
componentWillMount(){
console.log('父组件-componentWillMount')
}
componentDidMount(){
console.log('父组件-componentDidMount')
}
shouldComponentUpdate(){
console.log('父组件-shouldComponentUpdate')
return true
}
componentWillUpdate(){
console.log('父组件-componentWillUpdate')
}
componentDidUpdate(){
console.log('父组件-componentDidUpdate')
}
componentWillUnmount(){
console.log('父组件-componentWillUnmount')
}
editnum = ()=>{
const {num} = this.state
this.setState({num:num+1})
}
editvalue = ()=>{
const {value} = this.state
this.setState({value:value+1})
}
force = ()=>{
this.forceUpdate()
// 不会判断需不需要更新-直接更新
}
}
class Son extends React.Component{
constructor(props){
super(props)
console.log('子组件-constructor')
}
render(){
const {num} = this.props
console.log('子组件-render')
return(
<div>
<h1>我是子组件</h1>
<span>{num}</span>
</div>
)
}
componentWillReceiveProps(props){
console.log('接收props', props)
}
componentWillMount(){
console.log('子组件-componentWillMount')
}
componentDidMount(){
console.log('子组件-componentDidMount')
}
shouldComponentUpdate(){
console.log('子组件-shouldComponentUpdate')
return true
}
componentWillUpdate(){
console.log('子组件-componentWillUpdate')
}
componentDidUpdate(){
console.log('子组件-componentDidUpdate')
}
componentWillUnmount(){
console.log('父组件-componentWillUnmount')
}
}
ReactDOM.render(<Fa /> , document.getElementById('test'))
</script>
渲染过程
案例2一进入页面,在控制台打印结果如下:
挂载组件是一个由内向外
的过程.
- [1]
(父)发现标签名首字母大写 -> 去寻找对应的组件ReactDOM.render(<Fa /> , document.getElementById('test'))
- [2] (父)发现组件是一个类组件 -> 通过new关键字实例化对象。在创建实例化对象过程中,会自动调用 构造器(
constructor
) -> 创建实例对象并修改this指向; - 获取到实例化对象后
- [3] (父)通过实例化对象调用
componentWillMount
函数 -> 在此已经初始化数据但是还没有挂载dom - [4] (父)通过实例化对象调用
render
函数 -> 在此获取虚拟dom - [5] (父-子)此时发现虚拟dom中存在组件 -> 去寻找对应的组件
- [6] (子)发现子组件是一个类组件 -> 通过new关键字实例化对象
- (子)在实例化对象过程中,会自动调用 构造器(
constructor
) -> 创建实例对象并修改this指向;
- (子)在实例化对象过程中,会自动调用 构造器(
- 获取到实例化对象后
- [7] (子)通过实例化对象调用
componentWillMount
函数 -> 在此已经初始化数据但是还没有挂载dom - [8] (子)通过实例化对象调用
render
函数 -> 在此获取虚拟dom - [9] (子)挂载dom是一个由内向外的过程,通过实例化对象调用
componetdidMount
函数 -> 在此vdom已经挂载到dom元素上
- [7] (子)通过实例化对象调用
- [10] (父)通过实例化对象调用
componetdidMount
函数 -> 在此vdom已经挂载到dom元素上
- [3] (父)通过实例化对象调用
更新过程1-通过setState去修改数据(num)
更新组件是一个由内向外
的过程.
案例2当点击“点我修改num”按钮时,在控制台打印结果如下:
- (父)判断是否需要更新
- 不需要更新-> 结束(子组件也不会更新了)
- 需要更新 -> 进行更新
- (父)shouldComponentUpdate
- (父)render -> 获取更新后的vdom -> 发现存在子组件
- (子)componentWillReceiveProps -> 组件即将接收props
- (子)判断是否需要更新
- (子)不需要更新-> 子组件更新结束
- (子)需要更新 -> 进行更新
- (子)shouldComponentUpdate
- (子)render -> 获取更新后的vdom
- (子)componentDidUpdate
+(父)componentDidUpdate
更新过程1-通过setState去修改数据(value)
案例2当点击“点我修改value”按钮时,在控制台打印结果如下:
发现value和子组件没有任何关系,当value改变时子组件依旧会重新渲染,也就是说只要是组件通过setState去修改数据,默认情况下会重新渲染整个组件树(包含子组件)。
若是不想重新渲染子组件,可以这样写
shouldComponentUpdate(nextprops){
console.log('子组件-shouldComponentUpdate', nextprops)
return nextprops.num != this.props.num
}
这样就是仅仅在num值改变时才会重新渲染子组件了。
- 若是修改num, 控制台打印结果如下:
- 若是修改value,控制台打印结果如下:
更新过程3-通过forceUpdate强制更新
案例2当点击“点我强制刷新”按钮时,在控制台打印结果如下:
- 当通过forceUpdate强制刷新时
- 父组件是强制刷新
- 子组件还是会走shouldComponentUpdate钩子去判断是否刷新.
销毁过程
销毁组件也是一个由内向外
的过程.
生命周期(新)
生命周期图
在17版本+
不推荐使用:componentWillMount componentWillUpdate componentWillReceiveProps 不推荐使用,使用时会报一个警告
新添加两个生命周期钩子(使用较少):
getDerivedStateFromProps
该函数的参数为props state
该函数的返回值将直接作为state值进行渲染->应用在state的值在任何时候都依赖于props
getSnapshotBeforeUpdate
该函数的参数 preprops prestate 传递组件更改前的一些信息
该函数的返回值将作为 conponentDidUpdate生命周期的第三个参数