前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[笔记]深入理解React生命周期

[笔记]深入理解React生命周期

作者头像
江米小枣
发布2020-06-15 14:51:35
1.3K0
发布2020-06-15 14:51:35
举报
文章被收录于专栏:云前端

[I] 构造React组件的两种方法

  • React.createClass()
  • class MyComponent extends React.Component

[II] React组件的几个生命周期阶段

出生:Mounting

  • 组件被初始化,propsstate被定义和配置
  • 组件及其所有子组件被加载到原生UI栈(DOM或UIView)中
  • 做必要的后期处理
  • 该阶段只发生一次
    • initialize() 或 构造函数
    • getDefaultProps() 或 MyComponent.defaultProps
    • getInitialState() 或 this.state = ...
    • componentWillMount()
    • render()
    • 子组件初始化和生命周期的开始
    • componentDidMount()

成长:Update

  • 取得新的props
  • 改变state
  • 处理用户的互动操作
  • 和各层次组件通信
  • 该阶段重复发生,花费时间最多
    • componentWillReceiveProps()
    • shouldComponentUpdate()
    • componentWillUpdate()
    • render()
    • 子组件对应的生命周期方法
    • componentDidUpdate()

消亡:Unmount

  • 发生在组件实例被从原生UI中卸载时,诸如用户切换页面、组件被隐藏等
  • 该阶段也只发生一次
    • componentWillUnmount()
    • 子组件对应的生命周期方法
    • 实例被销毁,会被垃圾回收

以上方法严格按照顺序执行

[III] Mounting出生阶段

该阶段主要目的就是对组件实例进行初始化配置

3.1 组件 vs 元素

代码语言:javascript
复制
class MyComponent extends React.Component {
   render() {        return <div id={this.props.id}>Hello World!</div>;
   }
};var ele = React.createElement(MyComponent, {abc:123});var ins = new MyComponent;ReactDOM.render(
   <MyComponent id="123" />, //相当于ele的构造方式
   document.getElementById('mount-point')
);console.log(    MyComponent.prototype, //ReactComponent {}
   ele instanceof MyComponent, //false
   MyComponent.prototype.isPrototypeOf(ele), //false
   Object.getPrototypeOf(ele), //Object
   ins instanceof MyComponent, //true
   MyComponent.prototype.isPrototypeOf(ins), //true
   ele.type, //function MyComponent
   ele.props, //{abc:123}
   ele.key, //null
   ele.ref //null);
  • 开发者常常将被加载的组件象误认为是类似new MyComponnet的实例
  • 实际上虚拟dom中的元素,是由React.createElement()创建的
  • 元素是一种轻量对象描述,包含type, props, key, ref四个属性

3.2 应用首次render()

  • 开发者最熟悉的方法,用jsx来写布局
  • 首次render()比较特殊,会将整个应用加载到原生UI中
  • 对应于ReactDOM.render(), 在该方法第二个参数中传递根元素,以告知React加载内容的位置
  • 在此次调用中,React开始处理传递来的元素,并生成组件实例
  • 该元素的type属性指向组件,用来生成实例,并向其传递props

3.3 初始化和构造函数

  • 在从元素初始化组件的过程中,propsstate被定义
  • getDefaultProps() 或 MyComponent.defaultProps

3.4 State初始值

  • 一旦props被定义完毕,组件就开始初始化state
  • getInitialState() 或 this.state = ...
  • 如果没有初始值,定义为{}而非不定义,否则会报错

3.5 在componentWillMount()中预加载

  • 设置完propsstate,就进入了生命周期方法的领域
  • componentWillMount() 是第一个真正的生命周期方法
  • 该方法仅在初始化渲染之前被调用一次
  • 因为是在render()之前调用,所以无法访问DOM等原生UI
  • 因为子元素等尚未创建,也无法访问refs
  • 可以对this.propsthis.state进行操作了,比如对props计算后调用setState()
  • 是注册和监听全局事件的好机会,比如window resizeFlux store

3.6 组件渲染render()

  • 不同于其他生命周期方法,render()贯穿了出生和成长阶段
  • render()应该没有副作用,这意味着不能进行 调用setState()、访问原生UI(如ReactDOM.findDOMNode) 等一切可能引起状态改变的动作;否则会触发另一次render(),引起死循环

3.7 管理子组件并加载

经过首次渲染,render()返回了一个根元素,该元素可能会包含若干层级的子元素

  • 对于一棵可能有N层的元素树,每个元素都会经历其自身的一个完整生命周期
  • 与其父元素一样,React为每个子元素创建一个新实例,并经过构造函数、默认props、初始state、componentWillMount()和render()

3.8 在componentDidMount()中的后期加载

  • 出生阶段的最后一个方法
  • 该方法只在组件实例及所有其子元素被加载到原生UI后被调用一次
  • 在该方法中可访问原生UI,或通过refs访问子元素了,所以有可能会触发一次新的渲染过程;可以通过 this.setState()forceUpdate()触发,并需要注意多次渲染引起的潜在问题
  • 在元素树中,不同于出生阶段其他方法是从上至下发生的,componentDidMount()是从下至上发生的
  • 这种执行顺序保证了父元素能够访问到其自身和所有子元素的原生UI
  • 类似基于原生UI布局的变化(如CSS对DOM的计算)改变当前状态,或者使用第三方UI库(日期选择器等)的任务,都适合此时执行

[IV] Update成长阶段

改变props、改变state,或调用forceUpdate(), 都会触发update

4.1 发起更新

4.1.1 更改Props

  • props对组件自身来说是不可变的(immutable),内部写this.props.xxx = ...会引发报错
  • 当父元素或根元素传递了新的属性值后,才会触发更新

4.1.2 setState()

  • 对大部分开发者而言,首要和现实的挑战就是在组件中管理状态
  • 改变部分状态时,并非替换整个state,React使用一个队列系统,更新其对应的一块
  • setState()应被视为异步操作;一个常见的错误就是在一个方法里setState后尝试立即用this.state.xxx访问那个值,这容易引起bug
  • React构造了一个更改队列,用来管理在方法链中对状态的多次更改;一旦状态更改被添加到队列中,React就会确保组件被添加到脏队列(dirty queue),以跟踪组件实例的改变,React也就据此了解到哪些组件将进入update阶段
  • 虽然理论上外部可以操作组件的state,但那将让系统变得异常脆弱,应该由组件实例自身在内部setState()

4.1.3 forceUpdate()

  • 强制组件进入更新阶段

4.2 更新过程和componentWillReceiveProps()

向组件实例传递props后,成长周期中的首个生命周期方法componentWillReceiveProps(nextProp)就可能会被调用

  • 参数nextProp可以用来和this.prop比较,以做出决策并setState()
  • 该方法被调用,并非意味着props一定发生了变化;比如一个数组属性增加了新元素,此时该属性仍是同一个数组对象,React在不做深度比较的情况下无法轻易判断其是否更改,为了避免错误,仍会调用componentWillReceiveProps()
  • 当只更改了state时,该方法会被略过,不做调用

4.3 使用shouldComponentUpdate()

  • 参数为nextProps, nextState
  • 总是返回true,就会在每次更新中都重新渲染
  • 返回false,就可以退出当前更新过程
  • 该方法是一个有效的优化工具,PureRenderMixin(https://facebook.github.io/react/docs/pure-render-mixin.html)正是发挥此作用的,它会比较新老props和state,不同时才会返回true允许渲染
  • 上述插件其实是使用了===来比较对象,回到数组的例子,遇到数据结构改变而对象不变时还是不能准确判断;所以Redux中的reducers纯函数返回新对象,而Immutable.js(https://facebook.github.io/immutable-js/)每次操作都返回新的不可变数据结构,这些方法都确保了可以准确验证props和state的改变
  • 如果使用了Immutable.js, 可以直接使用 ImmutableRenderMixin(https://github.com/jurassix/react-immutable-render-mixin)
  • 对一个组件作了这项优化后,就可以避免其所有层次的子组件同时触发不必要的渲染,从而显著改善性能

4.4 用forceUpdate()抢先一步

  • 类似于componentWillReceiveProps(),也可以跳过shouldComponentUpdate()
  • 上述原理是,forceUpdate()后,组件被打上一个标记,添加到脏队列后,shouldComponentUpdate()就被忽略掉了
  • 不合理使用该方法极易引起死循环,当三思而后行

4.5 切入componentWillUpdate()

  • 参数为nextProps, nextState
  • 一旦确定了需要重新渲染,该方法就被调用了
  • 该方法和componentWillMount()类似,都在render()之前调用; 二者的目的及任务也类似,区别在于每次更新过程,该方法都会被调用
  • 因为此时重新渲染尚未完成,所以组件可以访问到旧的UI和即将过期的refs,可以在此时发起CSS动画等,也是调度事件的好时机
  • 可以比较新老props和state,但不能在此调用setState(),因为那将引发新一次的componentWillUpdate(),从而陷入死循环

4.6 重新渲染和子组件更新

  • 一旦重回render(),就可以根据更新后的props和state重新应用于内容和子组件
  • 不同于首次渲染的是,React对生成的元素采用不同的管理方式,最大的区别就是组件的初始化和子元素
  • 根据render()返回的元素树结构,React将其和旧结构进行比较;根据每个元素上生成或指定的keys(https://facebook.github.io/react/docs/lists-and-keys.html),判断其是新建、删除还是需要更新
  • 对于keys相同的元素,改变其props以启动更新
  • 对于新元素或keys改变的元素,创建新实例并使其进入出生阶段

4.7 在componentDidUpdate()中处理后期渲染

对应于出生阶段的componentDidMount(),在omponentDidUpdate()中也可以访问原生UI、取得refs或在必要的时候发起另一轮更新和渲染

  • 参数为prevProps, prevState
  • 与之前的方法不同的是,现在this.propsthis.state获取的是当前的值了
  • componentDidMount()一样,在所有层次的子组件更新完毕之后,该方法才会被调用
  • 该方法中最常见的应用场景就是用第三方库操作原生UI,比如在props中的数据发生变化时更新图表
  • 如果需要根据最新的尺寸、样式等setState()发起新一轮渲染,则务必小心行事,比如判断获取的高度值是否是变化过的,否则会陷入渲染死循环

[V] Unmount消亡阶段

  • 从原生UI中卸载,并等待垃圾回收
  • 发生在UI改变,并且元素树中不再有匹配组件的key时

5.1 使用componentWillUnmount()

  • 真正从UI中移除之前,可以在此做一些清理工作;比如把在componentWillMount()componentDidMount()中设置的东西清除掉,停止监听全局时间、销毁第三方库元素等;从而避免内存泄露
  • React会从上到下逐次销毁元素,先调用目标元素的componentWillUnmount(),然后是其子元素,直到该节点下所有东西清理干净

[VI] 总结

  1. 首次渲染:
    • constructor(props)
    • componentWillMount() //安全的setState()
    • render() //禁止setState()
    • componentDidMount() //慎用setState()
  2. Props改变:
    • componentWillReceiveProps(nextProps) //安全的setState()
    • shouldComponentUpdate(nextProps, nextState)
    • componentWillUpdate(nextProps, nextState) //禁止setState()
    • render() //禁止setState()
    • componentDidUpdate(prevProps, prevState) //慎用setState()
  3. State改变:
    • shouldComponentUpdate(nextProps, nextState) -- componentWillUpdate(nextProps, nextState) //禁止setState()
    • render()
    • componentDidUpdate(prevProps, prevState) //慎用setState()
  4. 强制刷新:
    • forceUpdate()也要谨慎使用
  5. 卸载:
    • componentWillUnmount()

原文:https://www.gitbook.com/book/developmentarc/react-indepth/details

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [I] 构造React组件的两种方法
  • [II] React组件的几个生命周期阶段
    • 出生:Mounting
      • 成长:Update
        • 消亡:Unmount
        • [III] Mounting出生阶段
          • 3.1 组件 vs 元素
            • 3.2 应用首次render()
              • 3.3 初始化和构造函数
                • 3.4 State初始值
                  • 3.5 在componentWillMount()中预加载
                    • 3.6 组件渲染render()
                      • 3.7 管理子组件并加载
                        • 3.8 在componentDidMount()中的后期加载
                        • [IV] Update成长阶段
                          • 4.1 发起更新
                            • 4.1.1 更改Props
                            • 4.1.2 setState()
                            • 4.1.3 forceUpdate()
                          • 4.2 更新过程和componentWillReceiveProps()
                            • 4.3 使用shouldComponentUpdate()
                              • 4.4 用forceUpdate()抢先一步
                                • 4.5 切入componentWillUpdate()
                                  • 4.6 重新渲染和子组件更新
                                    • 4.7 在componentDidUpdate()中处理后期渲染
                                    • [V] Unmount消亡阶段
                                      • 5.1 使用componentWillUnmount()
                                      • [VI] 总结
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档