zoukankan      html  css  js  c++  java
  • 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,然后卸载不需要的旧组件,最后新组件执行挂载完成的回调。

  • 相关阅读:
    Java+7入门经典 -1 简介
    优化算法动画演示Alec Radford's animations for optimization algorithms
    如何写科技论文How to write a technical paper
    开始学习深度学习和循环神经网络Some starting points for deep learning and RNNs
    用500行Julia代码开始深度学习之旅 Beginning deep learning with 500 lines of Julia
    用10张图来看机器学习Machine learning in 10 pictures
    ICLR 2013 International Conference on Learning Representations深度学习论文papers
    ICLR 2014 International Conference on Learning Representations深度学习论文papers
    卷积神经网络CNN(Convolutional Neural Networks)没有原理只有实现
    卷积神经网络Convolutional Neural Networks
  • 原文地址:https://www.cnblogs.com/huafang/p/14282101.html
Copyright © 2011-2022 走看看