React的父子组件生命周期

父子组件生命周期:

  “生命周期”细想之下有点浪漫主义色彩,不知道是不是从lifecycle英译过来的。作为一个前端从业者,如果让我来取,可能会取成“渲染周期”之类的,毕竟是和浏览器打交道的职业,浏览器的layout使dom树具有骨架,paint则让整个页面光亮起来。

  React 的一切都是组件,通过 React.createElement 方法来创建嵌套层级,说白了在内存中构建对象树,据此渲染到浏览器中成为dom树,这个时候一个节点是什么时候真正渲染到页面中就变得重要起来,因为只有这个时候你才能真正和浏览器环境内的对象和方法交互,同样离开的时候也需要清理监听器等防止干扰后续逻辑,因此钩子函数,也可以说是生命周期函数就有了存在的意义。

  先上代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <div id="hollow"></div>
    <script type="text/babel">
      const { Component, Fragment } = React;
      class F extends Component {
        state = { x: 'state before' };
        static getDerivedStateFromProps(getProps, getState) {
          console.log('F get props', { getProps, getState });
        }
        componentDidMount() {
          console.log('F did mount');
          setTimeout(() => {
            console.log('%c %s', 'color:blue', 'start to update state');
            this.setState({ x: 'state after' });
          }, 2000);
        }
        shouldComponentUpdate(nextProps, nextState) {
          console.log('F should update', { nextProps, nextState });
          return true;
        }
        componentDidUpdate(prevProps, prevState) {
          console.log('F did update', { prevProps, prevState });
        }
        componentWillUnmount() {
          console.log('F will unmount', Date.now());
        }
        render() {
          return (
            <div>
              {this.props.x}
              {this.state.x}
            </div>
          );
        }
      }
      class App extends Component {
        state = { x: 'props before ' };
        componentDidMount() {
          console.log('App did mount');
          setTimeout(() => {
            console.log('%c %s', 'color:red', 'start to update props');
            this.setState({ x: 'props after ' });
          }, 2000);
        }
        componentWillUnmount() {
          console.log('App will unmount', Date.now());
        }
        render() {
          return <F {...this.state} />;
        }
      }
      setTimeout(() => {
        ReactDOM.render('unmount Component App at ' + Date.now(), app);
      }, 6000);
      ReactDOM.render(<App />, app);
    </script>
  </body>
</html>

---CodePen在线演示---

Props 和 State 相关

父组件 App 将自身的State传入了子组件 F 内,忽略挂载和卸载,列举生命周期函数:

1,getDerivedStateFromProps --

  通过浏览器打印结果可以看到,不管是组件初始化,还是更新或继承新的 state 或者 props ,最先触发的都是静态钩子函数 getDerivedStateFromProps ;

2,shouldComponentUpdate --

  在 setState 异步赋值了 state 的下一个状态后,整个子组件 F 开始收集和对比新旧状态,将新的状态输入到生命周期函数 shouldComponentUpdate 中,写明 return 的值是 truthy 还是 falsy 可以选择中断更新或者继续更新;

3,componentDidUpdate --

  在 shouldComponentUpdate 顺利进入下一步后,将执行 render 方法,更新虚拟dom树,浏览器完成渲染,在此之后旧的状态将作为参数传给 componentDidUpdate ,可以对比新状态以及是否保留旧状态;

挂载 和 卸载

componentDidMount 和 componentWillUnmount --

  观察父子组件的挂载生命周期函数,可以发现挂载时,子组件的挂载钩子先被触发;卸载时,子组件的卸载钩子后被触发;

  对于挂载钩子,一般来说,应该将子组件从上至下依次挂载到一个 fragment 上,再整体挂载到dom树中,因为频繁操作dom树不仅影响性能甚至可能影响用户体验。

  但是实际情况却并如此,我们经常在挂载函数上注册监听器,说明此时是可以与页面交互的,也就是说其实所有挂载钩子都是在父组件实际挂载到dom树上才触发的,不过是在父组件挂载后依次触发子组件的 componentDidmount ,最后再触发自身的挂载钩子,说白了,componentDidMount 其实是异步钩子。

  相反,卸载的时候父节点先被移除,再从上至下依次触发子组件的卸载钩子;

  但是我们也经常在卸载钩子上卸载监听器,这说明 componentWillUnmount 其实在父组件从dom树上卸载前触发的,先触发自身的卸载钩子,但此时并未从dom树上剥离,然后依次尝试触发所有子组件的卸载钩子,最后,父组件从dom树上完成实际卸载。

猜你喜欢

转载自www.cnblogs.com/lowki/p/11317088.html
今日推荐