• jQuery火箭图标返回顶部代码


    一、Hooks 组件

    函数组件 的本质是函数,没有 state 的概念的,因此不存在生命周期一说,仅仅是一个 render 函数而已。

    但是引入 Hooks 之后就变得不同了,它能让组件在不使用 class 的情况下拥有 state,所以就有了生命周期的概念,所谓的生命周期其实就是 useState、 useEffect() 和 useLayoutEffect() 。

    即:Hooks 组件(使用了Hooks的函数组件)有生命周期,而函数组件(未使用Hooks的函数组件)是没有生命周期的。

    下面,是具体的 class 与 Hooks 的生命周期对应关系:

    为方便记忆,大致汇总成表格如下。

    class 组件Hooks 组件
    constructor useState
    getDerivedStateFromProps useState 里面 update 函数
    shouldComponentUpdate useMemo
    render 函数本身
    componentDidMount useEffect
    componentDidUpdate useEffect
    componentWillUnmount useEffect  里面返回的函数
    componentDidCatch
    getDerivedStateFromError

    二、单个组件的生命周期

    1. 生命周期

    V16.3 之前

    我们可以将生命周期分为三个阶段:

    分开来讲:

    1. 挂载阶段

    1. 组件更新阶段

    1. 卸载阶段

    2. 这种生命周期会存在一个问题,那就是当更新复杂组件的最上层组件时,调用栈会很长,如果在进行复杂的操作时,就可能长时间阻塞主线程,带来不好的用户体验,Fiber 就是为了解决该问题而生。

      V16.3 之后

      Fiber 本质上是一个虚拟的堆栈帧,新的调度器会按照优先级自由调度这些帧,从而将之前的同步渲染改成了异步渲染,在不影响体验的情况下去分段计算更新。

      对于异步渲染,分为两阶段:

      其中,reconciliation 阶段是可以被打断的,所以 reconcilation 阶段执行的函数就会出现多次调用的情况,显然,这是不合理的。

      所以 V16.3 引入了新的 API 来解决这个问题:

      1. static getDerivedStateFromProps:该函数在挂载阶段和组件更新阶段都会执行,即每次获取新的props 或 state之后都会被执行在挂载阶段用来代替componentWillMount;在组件更新阶段配合 componentDidUpdate,可以覆盖 componentWillReceiveProps 的所有用法。

        同时它是一个静态函数,所以函数体内不能访问 this,会根据 nextProps 和 prevState 计算出预期的状态改变,返回结果会被送给 setState返回 null 则说明不需要更新 state,并且这个返回是必须的

      2. getSnapshotBeforeUpdate: 该函数会在 render 之后, DOM 更新前被调用,用于读取最新的 DOM 数据。

        返回一个值,作为 componentDidUpdate 的第三个参数;配合 componentDidUpdate, 可以覆盖componentWillUpdate的所有用法。

      注意:V16.3 中只用在组件挂载或组件 props 更新过程才会调用,即如果是因为自身 setState 引发或者forceUpdate 引发,而不是由父组件引发的话,那么static getDerivedStateFromProps也不会被调用,在 V16.4 中更正为都调用。

      即更新后的生命周期为:

      1. 挂载阶段

      1. 更新阶段

      1. 卸载阶段

      2. 生命周期,误区

    误解一:getDerivedStateFromProps 和 componentWillReceiveProps 只会在 props 改变 时才会调用

    实际上,只要父级重新渲染,getDerivedStateFromProps 和 componentWillReceiveProps 都会重新调用,不管 props 有没有变化。所以,在这两个方法内直接将 props 赋值到 state 是不安全的。

    // 子组件class PhoneInput extends Component {  state = { phone: this.props.phone };  handleChange = e => {    this.setState({ phone: e.target.value });  };  render() {    const { phone } = this.state;    return <input onChange={this.handleChange} value={phone} />;  }  componentWillReceiveProps(nextProps) {    // 不要这样做。    // 这会覆盖掉之前所有的组件内 state 更新!    this.setState({ phone: nextProps.phone });  }}// 父组件class App extends Component {  constructor() {    super();    this.state = {      count: 0    };  }  componentDidMount() {    // 使用了 setInterval,    // 每秒钟都会更新一下 state.count    // 这将导致 App 每秒钟重新渲染一次    this.interval = setInterval(      () =>        this.setState(prevState => ({          count: prevState.count + 1        })),      1000    );  }  componentWillUnmount() {    clearInterval(this.interval);  }  render() {    return (      <>        <p>          Start editing to see some magic happen :)        </p>        <PhoneInput phone='call me!' />         <p>          This component will re-render every second. Each time it renders, the          text you type will be reset. This illustrates a derived state          anti-pattern.        </p>      </>    );  }}
    class PhoneInput extends Component {
      state = { phone: this.props.phone };
     
      handleChange = e => {
        this.setState({ phone: e.target.value });
      };
     
      render() {
        const { phone } = this.state;
        return <input onChange={this.handleChange} value={phone} />;
      }
     
      componentWillReceiveProps(nextProps) {
        // 不要这样做。
        // 这会覆盖掉之前所有的组件内 state 更新!
        this.setState({ phone: nextProps.phone });
      }
    }
     
    // 父组件
    class App extends Component {
      constructor() {
        super();
        this.state = {
          count: 0
        };
      }
     
      componentDidMount() {
        // 使用了 setInterval,
        // 每秒钟都会更新一下 state.count
        // 这将导致 App 每秒钟重新渲染一次
        this.interval = setInterval(
          () =>
            this.setState(prevState => ({
              count: prevState.count + 1
            })),
          1000
        );
      }
     
      componentWillUnmount() {
        clearInterval(this.interval);
      }
     
      render() {
        return (
          <>
            <p>
              Start editing to see some magic happen :)
            </p>
            <PhoneInput phone='call me!' /> 
            <p>
              This component will re-render every second. Each time it renders, the
              text you type will be reset. This illustrates a derived state
              anti-pattern.
            </p>
          </>
        );
      }
    }

    实例可点击这里查看

    当然,我们可以在 父组件App 中 shouldComponentUpdate 比较 props 的 email 是不是修改再决定要不要重新渲染,但是如果子组件接受多个 props(较为复杂),就很难处理,而且 shouldComponentUpdate 主要是用来性能提升的,不推荐开发者操作 shouldComponetUpdate(可以使用 React.PureComponet)。

    我们也可以使用 在 props 变化后修改 state

    class PhoneInput extends Component {  state = {    phone: this.props.phone  };  componentWillReceiveProps(nextProps) {    // 只要 props.phone 改变,就改变 state    if (nextProps.phone !== this.props.phone) {      this.setState({        phone: nextProps.phone      });    }  }  // ...}
      state = {
        phone: this.props.phone
      };
     
      componentWillReceiveProps(nextProps) {
        // 只要 props.phone 改变,就改变 state
        if (nextProps.phone !== this.props.phone) {
          this.setState({
            phone: nextProps.phone
          });
        }
      }
     
      // ...
    }

    但这种也会导致一个问题,当 props 较为复杂时,props 与 state 的关系不好控制,可能导致问题

    解决方案一:完全可控的组件

    function PhoneInput(props) {  return <input onChange={props.onChange} value={props.phone} />;}
      return <input onChange={props.onChange} value={props.phone} />;
    }

    完全由 props 控制,不派生 state

    解决方案二:有 key 的非可控组件

    class PhoneInput extends Component {
    state = { phone: this.props.defaultPhone };
     handleChange = event =>
    {
     this.setState({ phone
    : event.target.value });
    };
     render()
    {
      return <input onChange={this.handleChange
    } value={this.state.phone} />;
    }}<PhoneInput defaultPhone=
    {this.props.user.phone} key={this.props.user.id}/> state = { phone: this.props.defaultPhone }; handleChange = event => { this.setState({ phone: event.target.value }); }; render() { return <input onChange={this.handleChange} value={this.state.phone} />; } } <PhoneInput defaultPhone={this.props.user.phone} key={this.props.user.id} />

    当 key 变化时, React 会创建一个新的而不是更新一个既有的组件

    误解二:将 props 的值直接复制给 state

    应避免将 props 的值复制给 state

    constructor(props) { super(props); // 千万不要这样做 // 直接用 props,保证单一数据源 this.state = { phone: props.phone };}
     super(props);
     // 千万不要这样做
     // 直接用 props,保证单一数据源
     this.state = { phone: props.phone };
    }

    三、多个组件的执行顺序

    1. 父子组件

    1. static getDerivedStateFromProps

    2. shouldComponentUpdate

    第  阶段,此时 DOM 节点已经生成完毕,组件挂载完成,开始后续流程。先依次触发同步子组件以下函数,最后触发父组件的。

    React 会按照上面的顺序依次执行这些函数,每个函数都是各个子组件的先执行,然后才是父组件的执行。

    所以执行顺序是:

    父组件 getDerivedStateFromProps —> 父组件 shouldComponentUpdate —> 子组件 getDerivedStateFromProps —> 子组件 shouldComponentUpdate —> 子组件 getSnapshotBeforeUpdate —>  父组件 getSnapshotBeforeUpdate —> 子组件 componentDidUpdate —> 父组件 componentDidUpdate

    1. getSnapshotBeforeUpdate()

    2. componentDidUpdate()

    卸载阶段

    componentWillUnmount(),顺序为 父组件的先执行,子组件按照在 JSX 中定义的顺序依次执行各自的方法

    注意 :如果卸载旧组件的同时伴随有新组件的创建,新组件会先被创建并执行完 render,然后卸载不需要的旧组件,最后新组件执行挂载完成的回调。

     

    二、单个组件的生命周期

    1. 生命周期

    V16.3 之前

    我们可以将生命周期分为三个阶段:

    分开来讲:

    1. 挂载阶段

    1. 组件更新阶段

    1. 卸载阶段

    640?wx_fmt=png

    这种生命周期会存在一个问题,那就是当更新复杂组件的最上层组件时,调用栈会很长,如果在进行复杂的操作时,就可能长时间阻塞主线程,带来不好的用户体验,Fiber 就是为了解决该问题而生。

    V16.3 之后

    Fiber 本质上是一个虚拟的堆栈帧,新的调度器会按照优先级自由调度这些帧,从而将之前的同步渲染改成了异步渲染,在不影响体验的情况下去分段计算更新。

    对于异步渲染,分为两阶段:

    其中,reconciliation 阶段是可以被打断的,所以 reconcilation 阶段执行的函数就会出现多次调用的情况,显然,这是不合理的。

    所以 V16.3 引入了新的 API 来解决这个问题:

    1. static getDerivedStateFromProps:该函数在挂载阶段和组件更新阶段都会执行,即每次获取新的props 或 state之后都会被执行在挂载阶段用来代替componentWillMount;在组件更新阶段配合 componentDidUpdate,可以覆盖 componentWillReceiveProps 的所有用法。

      同时它是一个静态函数,所以函数体内不能访问 this,会根据 nextProps 和 prevState 计算出预期的状态改变,返回结果会被送给 setState返回 null 则说明不需要更新 state,并且这个返回是必须的

    2. getSnapshotBeforeUpdate: 该函数会在 render 之后, DOM 更新前被调用,用于读取最新的 DOM 数据。

      返回一个值,作为 componentDidUpdate 的第三个参数;配合 componentDidUpdate, 可以覆盖componentWillUpdate的所有用法。

    注意:V16.3 中只用在组件挂载或组件 props 更新过程才会调用,即如果是因为自身 setState 引发或者forceUpdate 引发,而不是由父组件引发的话,那么static getDerivedStateFromProps也不会被调用,在 V16.4 中更正为都调用。

    即更新后的生命周期为:

    1. 挂载阶段

    1. 更新阶段

    1. 卸载阶段

    640?wx_fmt=png

    2. 生命周期,误区

    误解一:getDerivedStateFromProps 和 componentWillReceiveProps 只会在 props 改变 时才会调用

    实际上,只要父级重新渲染,getDerivedStateFromProps 和 componentWillReceiveProps 都会重新调用,不管 props 有没有变化。所以,在这两个方法内直接将 props 赋值到 state 是不安全的。

    1.  
      // 子组件class PhoneInput extends Component {  state = { phone: this.props.phone };  handleChange = e => {    this.setState({ phone: e.target.value });  };  render() {    const { phone } = this.state;    return <input onChange={this.handleChange} value={phone} />;  }  componentWillReceiveProps(nextProps) {    // 不要这样做。    // 这会覆盖掉之前所有的组件内 state 更新!    this.setState({ phone: nextProps.phone });  }}// 父组件class App extends Component {  constructor() {    super();    this.state = {      count: 0    };  }  componentDidMount() {    // 使用了 setInterval,    // 每秒钟都会更新一下 state.count    // 这将导致 App 每秒钟重新渲染一次    this.interval = setInterval(      () =>        this.setState(prevState => ({          count: prevState.count + 1        })),      1000    );  }  componentWillUnmount() {    clearInterval(this.interval);  }  render() {    return (      <>        <p>          Start editing to see some magic happen :)        </p>        <PhoneInput phone='call me!' />         <p>          This component will re-render every second. Each time it renders, the          text you type will be reset. This illustrates a derived state          anti-pattern.        </p>      </>    );  }}
    2.  
      class PhoneInput extends Component {
    3.  
        state = { phonethis.props.phone };
    4.  
       
    5.  
        handleChange = e => {
    6.  
          this.setState({ phone: e.target.value });
    7.  
        };
    8.  
       
    9.  
        render() {
    10.  
          const { phone } = this.state;
    11.  
          return <input onChange={this.handleChange} value={phone} />;
    12.  
        }
    13.  
       
    14.  
        componentWillReceiveProps(nextProps) {
    15.  
          // 不要这样做。
    16.  
          // 这会覆盖掉之前所有的组件内 state 更新!
    17.  
          this.setState({ phone: nextProps.phone });
    18.  
        }
    19.  
      }
    20.  
       
    21.  
      // 父组件
    22.  
      class App extends Component {
    23.  
        constructor() {
    24.  
          super();
    25.  
          this.state = {
    26.  
            count0
    27.  
          };
    28.  
        }
    29.  
       
    30.  
        componentDidMount() {
    31.  
          // 使用了 setInterval,
    32.  
          // 每秒钟都会更新一下 state.count
    33.  
          // 这将导致 App 每秒钟重新渲染一次
    34.  
          this.interval = setInterval(
    35.  
            () =>
    36.  
              this.setState(prevState => ({
    37.  
                count: prevState.count + 1
    38.  
              })),
    39.  
            1000
    40.  
          );
    41.  
        }
    42.  
       
    43.  
        componentWillUnmount() {
    44.  
          clearInterval(this.interval);
    45.  
        }
    46.  
       
    47.  
        render() {
    48.  
          return (
    49.  
            <>
    50.  
              <p>
    51.  
                Start editing to see some magic happen :)
    52.  
              </p>
    53.  
              <PhoneInput phone='call me!' /> 
    54.  
              <p>
    55.  
                This component will re-render every second. Each time it renders, the
    56.  
                text you type will be reset. This illustrates a derived state
    57.  
                anti-pattern.
    58.  
              </p>
    59.  
            </>
    60.  
          );
    61.  
        }
    62.  
      }

    实例可点击这里查看

    当然,我们可以在 父组件App 中 shouldComponentUpdate 比较 props 的 email 是不是修改再决定要不要重新渲染,但是如果子组件接受多个 props(较为复杂),就很难处理,而且 shouldComponentUpdate 主要是用来性能提升的,不推荐开发者操作 shouldComponetUpdate(可以使用 React.PureComponet)。

    我们也可以使用 在 props 变化后修改 state

    1.  
      class PhoneInput extends Component {  state = {    phonethis.props.phone  };  componentWillReceiveProps(nextProps) {    // 只要 props.phone 改变,就改变 state    if (nextProps.phone !== this.props.phone) {      this.setState({        phone: nextProps.phone      });    }  }  // ...}
    2.  
        state = {
    3.  
          phonethis.props.phone
    4.  
        };
    5.  
       
    6.  
        componentWillReceiveProps(nextProps) {
    7.  
          // 只要 props.phone 改变,就改变 state
    8.  
          if (nextProps.phone !== this.props.phone) {
    9.  
            this.setState({
    10.  
              phone: nextProps.phone
    11.  
            });
    12.  
          }
    13.  
        }
    14.  
       
    15.  
        // ...
    16.  
      }

    但这种也会导致一个问题,当 props 较为复杂时,props 与 state 的关系不好控制,可能导致问题

    解决方案一:完全可控的组件

    1.  
      function PhoneInput(props{  return <input onChange={props.onChange} value={props.phone} />;}
    2.  
        return <input onChange={props.onChange} value={props.phone} />;
    3.  
      }

    完全由 props 控制,不派生 state

    解决方案二:有 key 的非可控组件

    1.  
      class PhoneInput extends Component {  state = { phone: this.props.defaultPhone };  handleChange = event => {    this.setState({ phone: event.target.value });  };  render() {    return <input onChange={this.handleChange} value={this.state.phone} />;  }}<PhoneInput  defaultPhone={this.props.user.phone}  key={this.props.user.id}/>
    2.  
        state = { phone: this.props.defaultPhone };
    3.  
       
    4.  
        handleChange = event => {
    5.  
          this.setState({ phone: event.target.value });
    6.  
        };
    7.  
       
    8.  
        render() {
    9.  
          return <input onChange={this.handleChange} value={this.state.phone} />;
    10.  
        }
    11.  
      }
    12.  
       
    13.  
      <PhoneInput
    14.  
        defaultPhone={this.props.user.phone}
    15.  
        key={this.props.user.id}
    16.  
      />

    当 key 变化时, React 会创建一个新的而不是更新一个既有的组件

    误解二:将 props 的值直接复制给 state

    应避免将 props 的值复制给 state

    1.  
      constructor(props) { super(props); // 千万不要这样做 // 直接用 props,保证单一数据源 this.state = { phone: props.phone };}
    2.  
       super(props);
    3.  
       // 千万不要这样做
    4.  
       // 直接用 props,保证单一数据源
    5.  
       this.state = { phone: props.phone };
    6.  
      }

    三、多个组件的执行顺序

    1. 父子组件

    1. static getDerivedStateFromProps

    2. shouldComponentUpdate

    第  阶段,此时 DOM 节点已经生成完毕,组件挂载完成,开始后续流程。先依次触发同步子组件以下函数,最后触发父组件的。

    React 会按照上面的顺序依次执行这些函数,每个函数都是各个子组件的先执行,然后才是父组件的执行。

    所以执行顺序是:

    父组件 getDerivedStateFromProps —> 父组件 shouldComponentUpdate —> 子组件 getDerivedStateFromProps —> 子组件 shouldComponentUpdate —> 子组件 getSnapshotBeforeUpdate —>  父组件 getSnapshotBeforeUpdate —> 子组件 componentDidUpdate —> 父组件 componentDidUpdate

    1. getSnapshotBeforeUpdate()

    2. componentDidUpdate()

    卸载阶段

    componentWillUnmount(),顺序为 父组件的先执行,子组件按照在 JSX 中定义的顺序依次执行各自的方法

    注意 :如果卸载旧组件的同时伴随有新组件的创建,新组件会先被创建并执行完 render,然后卸载不需要的旧组件,最后新组件执行挂载完成的回调。

  • 相关阅读:
    css文档流
    gitolite搭建
    Packets out of order. Expected 1 received 27...
    前端常见跨域解决方案
    跨时代的分布式数据库 – 阿里云DRDS详解
    Redis持久化机制
    redis实现消息队列
    队列
    ide-helper
    Bitmap 位操作相关
  • 原文地址:https://www.cnblogs.com/huafang/p/14282101.html
Copyright © 2020-2023  润新知