React几个重要的生命周期

1,638 阅读7分钟

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及函数。

  • 细节与特点:

    1. 这个生命周期钩子在组件只需要初始化props时可以省略不写,但是只要手动写了这个钩子,就必须写完整:extendsconstructor(props)super(props)

      class myComponent extends from React.Component{
          constructor(props){
              super(props)
              this.state = {...}
          }
      }
      
    2. 组件内部函数的定义其实是在 constructor 中定义的。但是由于比较麻烦(具体回看React组件基础相关部分),React提供了一个语法糖,让开发者不需要把函数写在constructor内部。

    3. 不要在这个阶段使用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的定义及细节:

    1. render 返回的是一个类似 DOM元素的东西(<div>....</div>),但这不是DOM元素,而是表示DOM元素的对象,称为:虚拟DOM。
    2. 虚拟DOM可以 log 出来。
    3. 虚拟DOM,或者说 render 返回的内容,必须且只有一个闭合的根标签。正常来说我们会用一个<div>作为根标签,如果想不把那个根标签渲染到DOM树上怎么办呢?React提供了一个标签占位符<React.Fragment>...</React.Fragment>,简写:<>...</>,这个占位就模拟了一个根标签,但是并不会渲染到DOM树上
    4. 可以用一个变量来存储虚拟DOM,如:message = <div>哈哈</div>
    5. 在虚拟DOM中使用变量或者JS表达式return ( {message} <button>+1</button> ),用 {} 包裹变量名,{}不仅可以包裹变量名,还能写JS表达式,如:return ({ this.state.n%2===0?<div>偶数</div>:<div>奇数</div> } <button>+1</button>)
  • render的使用经验:

    1. 既然这是一个(生命周期)函数,那么我们其实可以在这里面做其他想做的事情,而不是仅仅只能渲染视图,比如:在函数中 log 一下查看这个函数是否被调用了。

    2. render的选择性渲染

      当我们需要根据某些数据的特性,渲染不同的内容时,我们可以通过if else,或三元表达式来选择 return 不同的虚拟DOM,或则将虚拟DOM保存在某个变量中。这样我们就能根据数据的不同来渲染不同的内容。

      如: return ({ this.state.n%2===0?<div>偶数</div>:<div>奇数</div> } <button>+1</button>)

    3. 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元素的技巧:

    1. 通过选择器:document.querySelector() or document.getElementById()

    2. 通过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的使用技巧:

    1. 官方推荐Ajax加载数据的请求在这个阶段发起
    2. 首次渲染就会执行一次这个生命周期函数。

componentDidUpdate(preProps,preState,snapshot )

  • 用途:

    视图更新后执行钩子函数。

  • 使用经验:

    1. 这里也可以进行Ajax请求,一般是更新数据的请求。

    2. 首次渲染不会执行该生命周期函数。

    3. 在这个生命周期函数中执行 setState 要特别注意不要引起死循环

    4. 当生命周期 shouldComponentUpdate 返回 false 后就不会执行这个生命周期函数。

  • 参数:

    该函数有3个参数。常用的是前两个,第一个是之前的Props,第二个是之前的State,而第三个参数用得比较少。

componentWillUnmount()

  • 用途:

    在组件被移出页面然后被销毁之前执行的代码。注意:移出页面然后被销毁,是2个动作,移除页面是移除DOM树,销毁指删除其在内存中的数据。 unmount过后的组件不会再次mount

  • 使用场景:

    清除一些具有持久性影响的操作。如:事件监听,创建的Timer,发送的Ajax请求,这些操作因为组件或元素的移除与销毁都会变得没有意义,但是仍占据内存,一个优秀的前端要释放这些没用的内存。