5. react - 生命周期
简述react的生命周期:
react 的生命周期分为3个阶段:分别是挂载,运行中(渲染),和卸载
1. 挂载阶段
1.1 构造函数 constructor()
完成数据初始化,接受两个参数 props (传入的属性) 和 context(上下文对象),当在函数内部使用这两个参数必须使用 super(props, context)
注意: 只要使用了constructor()就必须写super(),否则会导致this指向错误
1.2 componentWillMount()
在组件 constructor() 执行完成后执行,数据已经初始化完,但是还没渲染DOM。 很少用,一般用于服务器端渲染
1.3 componentDidMount()
time: 组件第一次渲染完成后,此时DOM节点已经生成。只会初次渲染进入一次。后续更新进入componentDidUpate生命周期
when: 调用 ajax ,使用 setSate 更细元素
1.4 componentWillUnmount()
time:组件将要卸载
when: 组件卸载,数据销毁。【1. 移除定时器,2. 清除事件removeEventListener, 3. 还原标志位】
2. 更新过程
2.1 componentWillReceiveProps(nextProps)
time: 当父组件传入的属性值发生变化时触发
args: nextProps 更新后的props, this.props是旧的props
通过意义示例来演示 componentWillReceiveProps 的触发
when: 当 props 传入的属性发生改变后,需要做其他业务逻辑时。 类似于 Vue 中的监听父组件传给子组件的值。
一般用于更新自己的state,或页面跳转。
// parent.js 中 import React from 'react'; import Children from './children' class Parent extends React.Component { constructor (props) { super(props) this.state = { comment: "我是父组件中的state数据" } this.changeComment = this.changeComment.bind(this) } render () { return ( <div> 我是父组件 <button onClick={this.changeComment}>改变父组件传入子组件的属性</button> <Children comment={this.state.comment} /> </div> ) } // 通过点击事件改变父组件传给子组件的属性值 changeComment () { this.setState((state) => ({ comment: "属性发生了改变" })) } } export default Parent ----------------------------------------------------------------------- // children.js import React from 'react'; class Children extends React.Component { constructor (props, context) { super(props, context) this.state = { } } // 当父组件中点击按钮更新属性时,改方法触发 componentWillReceiveProps (nextProps) { console.log(nextProps, "新的props") console.log(this.props, "旧的props") } render () { return ( <div> <hr /> 我是子组件 <div> 我是父组件state传递过来的数据: {this.props.comment} </div> </div> ) } } export default Children
2.2 shouldComponentUpdate(nextProps,nextState)
args: nextProps 更新后的属性。nextSatte 更新后的 state
time: 组件要更新时触发。控制组件重新渲染,例如 setState 时,会触发组件重新渲染。在此 return false 会阻止组件重新渲染。
when: react 中父组件更新会导致子组件更新,当父组件更新,不需要更新某些子组件时,在这里处理。【主要用于性能优化】
// 组件更新时触发, 更新开关。 false 不更新, true 更新 shouldComponentUpdate (nextProps, nextState) { console.log(nextProps, nextState, "更新后的属性,状态") return false // return true 时会触发更新, return false时,子组件不会更新 }
2.3 componentWillUpdate(nextProps, nextState)
args: nextProps 更新后的属性。nextSatte 更新后的 state
time: 当shouldComponentUpdate生命周期 return true 时触发,组件进入重新渲染状态
when: 一般读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。react 开启异步渲染时,componentWillUpdate获取到的DOM状态不可靠
2.4 componentDidUpdate(prevProps, prevState)
args: prevProps 更新前的属性,prevState 更新前的状态
time: 组件每次重新渲染的时候触发
when: 组件更新完成后,需要处理的逻辑放在这里
2.5 render()
time: 此阶段 jsx 生成 dom。在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染
3. 新版本新增生命周期
3.1 getDerivedStateFromProps(nextProps, prevState)
1. 代替 componentWillReceiveProps(nextProps) + componentWillMount
2. 对比:
1. componentWillReceiveProps(nextProps) 通过对比 this.props 和 props 更新自身的 state 状态。弊端:1. 破坏state数据的单一源。2. 增加页面重绘次数
2. getDerivedStateFromProps(nextProps, prevState) 强制要求对比 nextProps 和 prevState 来更新自身 state。不能访问 this.props
// before componentWillReceiveProps(nextProps) { if (nextProps.isLogin !== this.props.isLogin) { this.setState({ isLogin: nextProps.isLogin, }); } if (nextProps.isLogin) { this.handleClose(); } } // after static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.isLogin !== prevState.isLogin) { return { isLogin: nextProps.isLogin, }; } return null; } componentDidUpdate(prevProps, prevState) { if (!prevState.isLogin && this.props.isLogin) { this.handleClose(); } }
3.2 getSnapshotBeforeUpdate(prevProps, prevState)
1. 代替 componentWillUpdate
2. 对比:
1. React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,导致 componentDidUpdate 中使用 componentWillUpdate中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了
2. getSnapshotBeforeUpdate 会在最终的 render 之前被调用。也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()
4. 执行顺序
import React from 'react'; class Children extends React.Component { constructor (props, context) { super(props, context) this.state = { name: "child" } } // componentWillMount () { // console.log("========0.1 挂载前 componentWillMount========") // } // componentWillReceiveProps (nextProps) { // console.log(nextProps, "新的props") // console.log(this.props, "旧的props") // } // 代替 componentWillReceiveProps static getDerivedStateFromProps (nextProps, prevState) { console.log(nextProps, "新的props") console.log(prevState, "旧的state") console.log("======1. 执行顺序 getDerivedStateFromProps======") return true } //组件更新时触发, 更新开关。 false 不更新, true 更新
shouldComponentUpdate (nextProps, nextState) { console.log(nextProps, nextState, "更新后的属性,状态") console.log("======2. 执行顺序 shouldComponentUpdate======") return true } render () { console.log("======3. 执行顺序 render======") return ( <div> <hr /> 我是子组件 <div> 我是父组件state传递过来的数据: {this.props.comment} </div> </div> ) } getSnapshotBeforeUpdate (prevProps, prevState) { console.log(prevProps, prevState, "更新前的属性,状态") console.log("======4. 执行顺序 getSnapshotBeforeUpdate======") return true } componentDidUpdate (prevProps, prevState) { console.log(prevProps, prevState, "更新前的属性,状态") console.log("======5. 执行顺序 componentDidUpdate======") } // 只执行一次 componentDidMount () { console.log("========6 挂载后 componentDidMount========") } } export default Children
以上代码演示生命周期的执行顺序:
1. 页面初始化加载只执行: getDerivedStateFromProps -> render -> componentDidMount
2. 数据发生变化时执行顺序:
{comment: "属性发生了改变"} "新的props"
children.js:24 {name: "child"} "旧的state"
children.js:25 ======1. 执行顺序 getDerivedStateFromProps======
children.js:31 {comment: "属性发生了改变"} {name: "child"} "更新后的属性,状态"
children.js:32 ======2. 执行顺序 shouldComponentUpdate======
children.js:37 ======3. 执行顺序 render======
children.js:48 {comment: "我是父组件中的state数据"} {name: "child"} "更新前的属性,状态"
children.js:49 ======4. 执行顺序 getSnapshotBeforeUpdate======
children.js:54 {comment: "我是父组件中的state数据"} {name: "child"} "更新前的属性,状态"
children.js:55 ======5. 执行顺序 componentDidUpdate======
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)