反应componentDidMount计时

时间:2018-09-05 12:40:03

标签: javascript reactjs performance

componentDidMount生命周期方法是否与同级组件无关?从下面的示例看来,只有在安装了所有同级组件之后,才会调用它。

假设我们有一个顶层组件,它呈现2个子组件,第一个具有简单的render(),另一个具有相对较慢的render()

要复制的样本:https://codesandbox.io/s/j43klml9py?expanddevtools=1

TL; DR:


class SlowComponent extends Component {
  componentDidMount() {
    // perf mark
  }

  render() {
    // Simulate slow render
    // Takes 50ms
    return <h3>Slow component</h3>;
  }
}

class FastComponent extends Component {
  componentDidMount() {
    // perf mark
  }

  render() {
    return <h3>Fast component</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    // perf mark start
  }

  componentDidMount() {
    // perf mark
    // measure all marks and print
  }

  render() {
    return (
      <div>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

我希望componentDidMount的时间像这样:

  1. FastComponent 10毫秒
  2. SlowComponent 50毫秒
  3. App 52毫秒

但是实际上,我得到的是同时触发快速和慢速组件componentDidMount回调,即

  1. FastComponent 50毫秒
  2. SlowComponent 51毫秒
  3. App 52毫秒

当前的示例代码和repro代码使用挂载回调,但对componentDidUpdate也适用。

完整来源:

import ReactDOM from "react-dom";
import React, { Component } from "react";

class SlowComponent extends Component {
  componentDidMount() {
    performance.mark("slow-mounted");
  }

  render() {
    // Simulate slow render
    for (var i = 0; i < 10000; i++) {
      for (var j = 0; j < 100; j++) {
        const b = JSON.parse(
          JSON.stringify({
            test: "test" + i,
            test1: i * i * i
          })
        );
      }
    }
    return <h3>Slow component</h3>;
  }
}

class FastComponent extends Component {
  componentDidMount() {
    performance.mark("fast-mounted");
  }

  render() {
    return <h3>Fast component</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    performance.mark("init");
  }

  componentDidMount() {
    performance.mark("app-mounted");

    performance.measure("slow", "init", "slow-mounted");
    performance.measure("fast", "init", "fast-mounted");
    performance.measure("app", "init", "app-mounted");

    console.clear();

    console.log(
      "slow",
      Math.round(performance.getEntriesByName("slow")[0].duration)
    );
    console.log(
      "fast",
      Math.round(performance.getEntriesByName("fast")[0].duration)
    );
    console.log(
      "app",
      Math.round(performance.getEntriesByName("app")[0].duration)
    );

    performance.clearMarks();
    performance.clearMeasures();
  }

  render() {
    return (
      <div>
        <h1>Demo</h1>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

4 个答案:

答案 0 :(得分:5)

我假设您使用的是最新的React版本(16.5.0)。

假设将两个组件(一个慢速组件和一个快速组件)包装在父组件中,则执行流程如下:

  1. 父组件调用其UNSAFE_componentWillMount方法
  2. 按照在其render方法中定义的顺序迭代所有子项,并调用其UNSAFE_componentWillMount方法
  3. 遍历所有子级并调用其render方法(此处将使用耗时缓慢的昂贵组件的渲染方法)
  4. 遍历所有孩子并调用他们的componentDidMount方法
  5. 父组件调用其componentDidMount方法

这整个过程是同步进行的。

回到您的示例,这就是为什么两个组件的计时几乎相同的原因,因为所有组件的工作一旦完成,所有组件生命周期方法就会被调用。

通常,从概念上讲,React由两个阶段组成,即“渲染”阶段和“提交”阶段。 “提交”阶段是React应用任何更改的阶段,在您的示例中,生命周期方法componentDidMount在此阶段被调用。

您可以通过调试React并查看以下堆栈跟踪来遵循此流程:

componentDidMount
commitLifeCycles
commitAllLifeCycles
callCallback
invokeGuardedCallbackDev
invokeGuardedCallback
commitRoot

答案 1 :(得分:1)

简单答案

所有渲染完成后,所有组件都会触发componentDidMount方法。因为在将元素安装到DOM之前,我们需要创建它们。

答案 2 :(得分:1)

根据您的生命周期顺序,将在挂载组件后调用componentDidMount(顾名思义,这本身就是暗示)。这将在DOM挂载之后进行,这将调用您的渲染。

现在假设是否没有I / O,即在任何地方的子组件中都发生了异步事件。

(即使发生任何异步事件,这些函数也会被执行,但调用堆栈将在事件循环中保持执行。但这是一个单独的讨论)

-- Parent
  -- Child-1
  -- Child-2

执行顺序

Parent constructor()
Child 1 constructor()
Child 1 render()
Child 1 componentDidMount()
Child 2 constructor()
Child 2 render()
Child 2 componentDidMount()
Parent componentDidMount()

根据ReactJS文档,安装触发器的顺序为

  • constructor()
  • 静态getDerivedStateFromProps()
  • render()
  • componentDidMount()

参考React文档Mounting React Docs中的安装顺序

在旁注中,另有详细说明了执行顺序。 Understanding React — Component life-cycle

答案 3 :(得分:0)

是的,ComponentDidMount生命周期方法独立于同级组件。这取决于渲染所花费的时间,因为它会在渲染完成后调用。但是组件的安装顺序与父组件中的安装顺序相同。例如-在上面的示例中,首先安装快速组件,然后安装第二个组件。

您无法计算函数迭代一个循环所花费的时间。因此,这里您对计算慢速组件的ComponentDidMount生命周期方法的时间的假设是错误的。如果不进行概要分析,则无法期望基于迭代次数的特定时间估计。