• React学习(三)----- 组件的生命周期


    1、生命周期的引入

    1、组件从创建到死亡它会经历一些特定的阶段。

    2、React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。

    3、我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

    2、React生命周期(旧)

    • 图解:

        

    • 初始化阶段:由ReactDOM.render()触发 ----- 初次渲染(顺序为
      constructor(构造器) ----- 
      componentWillMount (组件将要挂载的钩子)----- 
      render(初次渲染) ----- 
      componentDidMount(组件挂载完毕的钩子:常用的钩子一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息等
    • 更新阶段:由组件内部this.setSate()或父组件重新render触发
      • 组件内部的setState正常更新触发(顺序为
        shouldComponentUpdate(控制组件更新的 “阀门” ,这个钩子如果不写默认返回true,如果写了必须有返回值,返回false时无法更新页面)-----
        componentWillUpdate(组件将要更新的钩子)----- 
        render(初次渲染) ----- 
        componentDidUpdate(oldProps,oldState)(组件更新完毕的钩子)
      • forceUpdate强制更新触发(顺序为
        componentWillUpdate(组件将要更新的钩子)----- 
        render(初次渲染) ----- 
        componentDidUpdate(oldProps,oldState)(组件更新完毕的钩子)
        
        
         <button onClick={this.force}>不更改状态中的任何数据强制更新</button>  

        //
        强制更新 force = () =>{ this.forceUpdate() }
      • 父组件重新render触发(顺序为
        componentWillReceiveProps(组件将要接收新的props的钩子  首次渲染不执行,这是一个坑,所以好多人呼吁应该重新命名为 "componentWillReceiveNewProps")-----
        shouldComponentUpdate(控制组件更新的 “阀门” ,这个钩子如果不写默认返回true,如果写了必须有返回值,返回false时无法更新页面)-----
        componentWillUpdate(组件将要更新的钩子)----- 
        render(初次渲染) ----- 
        componentDidUpdate(oldProps,oldState)(组件更新完毕的钩子)
    • 卸载组件:由ReactDOM.unmountComponentAtNode()触发(顺序为
      componentWillUnmount(组件将要卸载的钩子:常用的钩子一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息等
    •  父子组件生命周期钩子函数的执行顺序
      • 父组件
        class Parent extends React.Component{
                  // 构造器
                  constructor(props){
                      super(props)
                      console.log('Parent-constructor')
                      this.state = {
                          age:55
                      }
                  }
                  // 组件将要挂载的钩子
                  componentWillMount(){
                      console.log('Parent-componentWillMount')
                  }
                  // 组件挂载完毕的钩子
                  componentDidMount(){
                      console.log('Parent-componentDidMount')
                  }
                  componentWillReceiveProps(){
                    console.log('Parent-componentWillReceiveProps')
                  }
                  // 相当于一个 “阀门” ,这个钩子如果不写默认返回true,如果写了
                  shouldComponentUpdate(){
                      console.log('Parent-shouldComponentUpdate')
                      return true
                  }
                  // 组件将要更新的钩子
                  componentWillUpdate(){
                      console.log('Parent-componentWillUpdate')
                  }
                  // 组件更新完毕的钩子
                  componentDidUpdate(){
                      console.log('Parent-componentDidUpdate')
                  }
                  // 组件将要卸载的钩子
                  componentWillUnmount(){
                      console.log('Parent-componentWillUnmount')
                  }
                  // 组件渲染
                  render(){
                    console.log('Parent-render')
                    let {age} = this.state
                    return (
                      <div>
                        <div style={{'300px',height:'200px',border:'1px solid #ccc'}}>
                          <h2>我是爸爸,我今年{age}岁了!</h2>
                          <button onClick={this.handlerChange}>点我修改年龄</button>  
                        </div>
                        <Children {...this.state}/>
                      </div>
                    )
                  }
                  handlerChange = () => {
                    let {age} = this.state;
                    this.setState({
                      age : age + 1
                    })
                  }
                }
        View Code

        子组件

         class Children extends React.Component{
                  // 构造器
                  constructor(props){
                      super(props)
                      console.log('Children-constructor')
                      this.state = {
                        
                      }
                  }
                  // 组件将要挂载的钩子
                  componentWillMount(){
                      console.log('Children-componentWillMount')
                  }
                  // 组件挂载完毕的钩子
                  componentDidMount(){
                      console.log('Children-componentDidMount')
                  }
                  // 组件将要接收新的props的钩子 首次渲染不执行
                  componentWillReceiveProps(props){
                    console.log('Children-componentWillReceiveProps',props)
                  }
                  // 相当于一个 “阀门” ,这个钩子如果不写默认返回true,如果写了
                  shouldComponentUpdate(){
                      console.log('Children-shouldComponentUpdate')
                      return true
                  }
                  // 组件将要更新的钩子
                  componentWillUpdate(){
                      console.log('Children-componentWillUpdate')
                  }
                  // 组件更新完毕的钩子
                  componentDidUpdate(){
                      console.log('Children-componentDidUpdate')
                  }
                  // 组件将要卸载的钩子
                  componentWillUnmount(){
                      console.log('Children-componentWillUnmount')
                  }
                  // 组件渲染
                  render(){
                    console.log('Children-render')
                    let {age} = this.props
                    return (
                      <div style={{'300px',height:'200px',border:'1px solid #ccc',marginTop:'10px'}}>
                        <h2>我是女儿~~~我的爸爸今年{age}了~~~</h2>
                      </div>
                    )
                  }
                }
        View Code

        渲染

        // 2、将组件渲染到页面上
        ReactDOM.render(<Parent />,document.getElementById('box'))
      • 加载渲染的时候
        Parent-constructor -----> Parent-componentWillMount -----> Parent-render -----> Children-constructor ----->  Children-componentWillMount -----> Children-render -----> Children-componentDidMount -----> Parent-componentDidMount
      • 父组件更改传递给子组件的props时
        Parent-shouldComponentUpdate -----> Parent-componentWillUpdate -----> Parent-render -----> Children-componentWillReceiveProps  -----> Children-shouldComponentUpdate -----> Children-componentWillUpdate -----> Children-render -----> Children-componentDidUpdate -----> Parent-componentDidUpdate
      • 子组件更新state的时候
        Children-shouldComponentUpdate -----> Children-componentWillUpdate -----> Children-render -----> Children-componentDidUpdate
    • 生命周期钩子函数的执行次数
      1)react生命周期函数中有哪些生命周期函数只会执行一次???
          constructor -----> componentWillMount -----> componentDidMount -----> componentWillUnMount
      2)react生命周期函数中有哪些生命周期函数会执行多次?
          componentWillRecevieProps -----> shouldComponentUpdate -----> componentWillUpdate -----> render -----> componentDidUpdate
      3)key值变化时执行的生命周期函数
          componentWillUnmount -----> componentWillMount -----> render -----> componentDidMount

    3、React生命周期(新)

    • 新旧生命周期钩子函数的对比
      • 引入的js文件版本为:17.0.2
        https://cdn.bootcdn.net/ajax/libs/react/17.0.2/umd/react.development.js
        https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js
      • 有三个钩子函数即将废弃,会出现警告,解决方法是
        1)UNSAFE_componentWillMount
        2)UNSAFE_componentWillUpdate
        3)UNSAFE_componentWillReceiveProps

      • 新增了两个钩子函数
        • getDerivedStateFromProps(获取一种派生状态 ----- 即让state的值在任何时候都取决于props)没啥意义
          // 若state的值在任何时候都取决于props,那么可以使用,而且不可以被修改了
          static getDerivedStateFromProps(props,state){
              console.log('MyComponent-getDerivedStateFromProps',props,state)
              return props
          }
        • getSnapshotBeforeUpdate(在更新之前获取“快照”)
          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
              .list{
                 200px;
                height: 150px;
                background-color: aquamarine;
                overflow: auto;
              }
              .news{
                height: 30px;
              }
            </style>
          </head>
          <body>
             <!-- 容器 -->
              <div id="box"></div>
          
              <!-- 引入React的核心库 -->
              <script src="../js/react.development.js"></script>
              <!-- 引入React的扩展库,用来渲染虚拟DOM -->
              <script src="../js/react-dom.development.js"></script>
              <!-- 引入babel 将jsx转换为浏览器可以识别的js文件 并且自动开启严格模式-->
              <script src="../js/babel.min.js"></script>
              <!-- 引入propTypes 对组件标签属性的类型、必要性、默认值进行控制 -->
              <script src="../js/prop-types.js"></script>
          
              <script type="text/babel">
                  // 1、创建组件
                  class NewsList extends React.Component{
                      // 定义在类MyComponent上
                      static propTypes = {
          
                      }
                      static defaultProps = {
          
                      }
                      // 构造器
                      constructor(props){
                          console.log('MyComponent-constructor')
                          super(props)
                          this.state = {
                            newsList :[]
                          }
                      }
                      componentDidMount(){
                        this.timer = setInterval(()=>{
                          // 获取原列表
                          let {newsList} = this.state;
                          // 模拟一条新闻
                          const news = '新闻' + (newsList.length + 1)
                          // 更新状态
                          this.setState({
                            newsList : [news,...newsList]
                          })
                        },500)
                      }
                      getSnapshotBeforeUpdate(){
                        console.log('getSnapshotBeforeUpdate')
                        const {list} = this.refs;
                        return list.scrollHeight
                      }
                      componentDidUpdate(oldProps,oldState,snapshotValue){
                        console.log('componentDidUpdate',oldProps,oldState,snapshotValue)
                        this.refs.list.scrollTop += this.refs.list.scrollHeight - snapshotValue
                      }
                      render(){
                        let {newsList} = this.state
                        return (
                          <div className="list" ref="list">
                            {
                              newsList.map((el,index)=>{
                                return <div className="news" key={index}>{el}</div>
                              })
                            }
                          </div>
                        )
                      }
                  }
                  // 2、将组件渲染到页面上
                  ReactDOM.render(<NewsList />,document.getElementById("box"))
              </script>
          </body>
          </html>
          View Code
      • 初始化阶段:由ReactDOM.render()触发 ----- 初次渲染(顺序为:
        constructor -----
        getDerivedStateFromProps -----
        render -----
        componentDidMount(组件挂载完毕的钩子:常用的钩子一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息等
      • 更新阶段
        • 组件内部的setState正常更新触发(顺序为
          getDerivedStateFromProps -----
          shouldComponentUpdate(控制组件更新的 “阀门” ,这个钩子如果不写默认返回true,如果写了必须有返回值,返回false时无法更新页面)-----
          render(初次渲染) ----- 
          getSnapshotBeforeUpdate ----- 
          componentDidUpdate(oldProps,oldState,snapshotValue)(组件更新完毕的钩子)
        • forceUpdate强制更新触发(顺序为
          render(初次渲染) ----- 
          getSnapshotBeforeUpdate ----- 
          componentDidUpdate(oldProps,oldState,snapshotValue)(组件更新完毕的钩子)
        • 父组件重新render触发(顺序为
          getDerivedStateFromProps -----
          shouldComponentUpdate(控制组件更新的 “阀门” ,这个钩子如果不写默认返回true,如果写了必须有返回值,返回false时无法更新页面)-----
          render(初次渲染) ----- 
          getSnapshotBeforeUpdate ----- 
          componentDidUpdate(oldProps,oldState,snapshotValue)(组件更新完毕的钩子)
      • 销毁阶段:由ReactDOM.unmountComponentAtNode()触发(顺序为
        componentWillUnmount(组件将要卸载的钩子常用的钩子一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息等
    • 图解

             

    4、补充知识(DOM的Diff算法)

    •  index作为key可能产生的问题
      • 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
        • 示例
          <!--
           * @Descripttion: 
           * @version: 
           * @Author: 北栀女孩儿
           * @Date: 2021-08-30 09:28:15
           * @LastEditTime: 2021-09-01 09:26:30
          -->
          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
          </head>
          <body>
            <!-- 容器 -->
              <div id="box"></div>
          
              <!-- 引入React的核心库 -->
              <script src="./js/react.development.js"></script>
              <!-- 引入React的扩展库,用来渲染虚拟DOM -->
              <script src="./js/react-dom.development.js"></script>
              <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 -->
              <script src="./js/babel.min.js"></script>
              <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 -->
              <script src="./js/prop-types.js"></script>
          
              <script type="text/babel">
              /*
                  经典面试题:
                    1) vue/react中的key有什么作用???(key 的内部原理是什么???)
                    2) 为什么遍历列表时,key最好不要用index
                  1.虚拟 DOM 中key的作用
                    1) 简单的说:key是虚拟 DOM 对象的标识,在更新显示时key起着极其重要的作用;
                    2) 复杂的说:当状态中的数据发生变化时,React会根据【新数据】生成【新的虚拟 DOM 】
                                随后React进行【新虚拟 DOM 】与【旧虚拟 DOM 】的diff比较,比较规则如下:
                                
                              a) 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的key:
                                (1)若虚拟 DOM 中内容没变,直接使用之前的真实DOM; 
                                (2)若虚拟 DOM 中内容变了,则生成新的真实DOM ,随后替换掉页面中之前的真实DOM
                              b) 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的key:根据数据创建新的真实DOM ,随后渲染到页面上
                  
          
                  2、用index作为key可能引发的问题???
                      1) 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
                      2) 如果结构中还包含输入类的DOM :会产生错误DOM更新 ===> 界面有问题
                      3) 注意:如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
          
          
                  3、开发中如何选择key???
                      1) 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
                      2) 如果确定只是简单的展示数据,用index也是可以的
              */
                  // 1、创建组件
                  class Person extends React.Component{
                      // 对标签属性的类型、必要性进行控制
                      static propTypes = {
          
                      }
                      // 对标签属性的默认值进行控制
                      static defaultProps = {
          
                      }
                      // 初始化数据
                      state = {
                         persons : [
                           {
                             id:'1',
                             name:'jerry',
                             age:'18'
                           },
                           {
                             id:'2',
                             name:'tom',
                             age:'22'
                           }
                         ]
                      }
                      componentDidMount(){
                        
                      }
                      render(){
                          let {persons} = this.state
                          {/*
                             慢动作回放:使用index索引值作为key
                                初始数据:
                                    {id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
                                初始的虚拟DOM:
                                    <li key=0>名字:jerry ----- 年龄:18</li>
                                    <li key=1>名字:tom----- 年龄:22</li>
                                更新后的数据:
                                    {id:'3',name:'wxh',age:'25'},{id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
                                新的虚拟DOM:
                                    <li key=0>名字:wxh ----- 年龄:25</li>
                                    <li key=1>名字:jerry ----- 年龄:18</li>
                                    <li key=2>名字:tom----- 年龄:22</li>
                                
                          
                          */}
                          return(
                              <div>
                                <h1>使用index作为key</h1>
                                <h2>展示人员信息</h2>
                                <button onClick={this.handlerClick}>添加一个人:wxh</button>
                                <ul>
                                  {
                                    persons.map((el,index)=>{
                                      return <li key={index}>名字:{el.name} ----- 年龄:{el.age}</li>
                                    })
                                  }
                                </ul>
                                <hr />
                                <h1>使用id作为key</h1>
                                <h2>展示人员信息</h2>
                                <button onClick={this.handlerClick}>添加一个人:wxh</button>
                                <ul>
                                  {
                                    persons.map((el,index)=>{
                                      return <li key={el.id}>名字:{el.name} ----- 年龄:{el.age}</li>
                                    })
                                  }
                                </ul>
                              </div>
                          )
                      }
                      handlerClick = () => {
                        let person = {
                          id:'3',
                          name:'wxh',
                          age:'25'
                        }
                        this.setState({
                          persons : [person,...this.state.persons]
                        })
                        console.log(this.state.persons)
                      }
                  }
                  // 2、将组件渲染到页面上
                  ReactDOM.render(<Person />,document.getElementById("box"))
              </script>
          </body>
          </html>
          View Code
        • 慢动作回放
          慢动作回放:使用index索引值作为key
                初始数据:
                    {id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
                初始的虚拟DOM:
                    <li key=0>名字:jerry ----- 年龄:18</li>
                    <li key=1>名字:tom----- 年龄:22</li>
                更新后的数据:
                    {id:'3',name:'wxh',age:'25'},{id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
                新的虚拟DOM:
                    <li key=0>名字:wxh ----- 年龄:25</li>
                    <li key=1>名字:jerry ----- 年龄:18</li>
                    <li key=2>名字:tom----- 年龄:22</li>
      • 如果结构中还包含输入类的DOM :会产生错误DOM更新 ===> 界面有问题
        • 示例
          View Code

          将前面显示的数据填入input之后,页面效果为:

                                  

                                   点击按钮加一个人,页面效果为:

                                 

        •   慢动作回放
          慢动作回放:使用index索引值作为key
                初始数据:
                    {id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
                初始的虚拟DOM:
                    <li key=0>名字:jerry ----- 年龄:18<input type="text" /></li>
                    <li key=1>名字:tom----- 年龄:22<input type="text" /></li>
                更新后的数据:
                    {id:'3',name:'wxh',age:'25'},{id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
                新的虚拟DOM:
                    <li key=0>名字:wxh ----- 年龄:25<input type="text" /></li>
                    <li key=1>名字:jerry ----- 年龄:18<input type="text" /></li>
                    <li key=2>名字:tom----- 年龄:22<input type="text" /></li>
    • id作为key
      • 慢动作回放
        慢动作回放:使用index索引值作为key
              初始数据:
                  {id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
              初始的虚拟DOM:
                  <li key=1>名字:jerry ----- 年龄:18</li>
                  <li key=2>名字:tom----- 年龄:22</li>
              更新后的数据:
                  {id:'3',name:'wxh',age:'25'},{id:'1',name:'jerry',age:'18'},{id:'2',name:'tom',age:'22'}
              新的虚拟DOM:
                  <li key=3>名字:wxh ----- 年龄:25</li>
                  <li key=1>名字:jerry ----- 年龄:18</li>
                  <li key=2>名字:tom----- 年龄:22</li>
                               
    北栀女孩儿
  • 相关阅读:
    QT学习笔记2
    QT学习笔记1
    图像中区域生长算法的详解和实现
    图像识别中的深度学习 转
    Appium基础环境搭建(windows)---基于python
    selenium实现文件上传方法汇总(AutoIt、win32GUI、sengkeys)---基于python
    selenium中的三种等待方式(显示等待WebDriverWait()、隐式等待implicitly()、强制等待sleep())---基于python
    水仙花数---基于python
    九九乘法表实现---基于python
    selenium元素定位不到之iframe---基于python
  • 原文地址:https://www.cnblogs.com/wxh0929/p/15206567.html
Copyright © 2020-2023  润新知