如何在Angular中测试渲染速度

15
我们正在构建一个Angular应用程序,想找出如何获得渲染各种页面所需时间的基准。我在这里了解了performance.timing的相关信息(链接),但由于当我们在应用程序中导航到新视图时计时数值不会改变,因此它似乎只对非单页应用程序有用。
理想情况下,我们希望能插入一些代码,获取各个视图的渲染时间,并将其发布到我们的Big Query服务。
有任何关于如何获取Angular应用程序视图的计时信息的想法吗?
编辑:
更具体地说,您访问一个路由,加载一个大型的ng-repeat列表(这对性能不是最优的),窗口在实际渲染列表项之前有很长的延迟。我们想看到从大空白视图到呈现列表项所需的时间。

1
您想要测量什么?有一些内置事件,例如$routeChangeStart$routeChangeSuccess。您可以订阅它们并使用用户计时API记录时间。 - runTarm
我需要看看这些事件到底捕获了什么。我在上面添加了更多具体信息的编辑。 - EmptyArsenal
可能类似于 - https://dev59.com/3XRC5IYBdhLWcg3wYf8p#1975103 - vsync
通过你的参考,我找到了$viewContentLoaded,这可能是我正在寻找的! - EmptyArsenal
仍未解决。$viewContentLoaded在我们转到路由时立即触发,而不是当内容实际呈现时触发。 - EmptyArsenal
3个回答

14

TypeScript / Angular (2+)答案:

为了测试Angular渲染组件树所花费的时间,请在变更检测之前和之后进行测量。这将计算Angular创建所有子组件并评估ngIf / ngFor等所需的时间。

有时候,当变更检测运行时,没有任何更改,并且计时间隔很小。只需查找最长的时间间隔,那里可能是所有工作都在进行的地方。此外,最好使用OnPush变更检测策略。请参见http://blog.angular-university.io/onpush-change-detection-how-it-works/

在您的@Component({...}) class上面声明此计时器实用程序:

class Timer {
  readonly start = performance.now();

  constructor(private readonly name: string) {}

  stop() {
    const time = performance.now() - this.start;
    console.log('Timer:', this.name, 'finished in', Math.round(time), 'ms');
  }
}

将此内容添加到您的组件类中:

在组件类中内部加入以下内容:

private t: Timer;

// When change detection begins
ngDoCheck() {
  this.t = new Timer('component 1 render');
}

// When change detection has finished:
// child components created, all *ngIfs evaluated
ngAfterViewChecked() {
  this.t.stop();  // Prints the time elapsed to the JS console.
}

请注意,Angular渲染与屏幕上的浏览器渲染(绘制)是分开的。这也需要时间。当您在Chrome时间轴工具(javascript开发者控制台 -> 性能 -> 记录)中看到带有许多“checkAndUpdateView”和“callViewAction”调用的javascript(黄色)时,那就是Angular渲染发生的时候。当您在Chrome时间轴工具中看到紫色标注为“渲染”时,则代表实际的浏览器渲染。

10
在做了一些实验后,我找到了一种方法来近似计算视图渲染所需的时间。
场景如下:
  1. 路由到 Angular 应用程序中的页面
  2. 进行 AJAX 调用以获取一个较长的项目列表
  3. 将项目列表传递到 ng-repeatul
  4. 查看已呈现的列表
在 Angular 应用程序中,ng-repeat 是一个臭名昭著的性能杀手,但它非常方便。这里是有关性能的讨论这里
假设我们想要近似计算从 #3 到 #4 所需的时间,因为测试 AJAX 调用性能非常简单。

这里是一些上下文:

<ul>
  <li ng-repeat="item in items">
    {{item.name}}
    <!-- More content here -->
  </li>
</ul>

在Angular控制器内部:

// Get items to populate ng-repeater
MyApiService.getItems(query)
  .then( function (data) {
    $scope.items = data;
    // When callback finishes, Angular will process items
    // and prepare to load into DOM
  }, function (reason) {
    console.log(reason);
  });

提到的事件@runTarm,$routeChangeStart、$routeChangeSuccess和$viewContentLoaded会在路由加载后立即触发,但在DOM渲染项目之前,因此它们不能解决问题。然而,通过实验,我发现一旦AJAX回调完成并设置了$scope.items,Angular就会开始一个阻塞操作来处理items并准备将ng-repeat ul插入到DOM中。因此,如果您在AJAX调用完成后获取时间,并在回调中指定setTimeout,那么setTimeout回调将被排队等待,直到Angular完成重复器过程并在DOM呈现之前的一刹那获取到您的时间,从而为您提供最接近的呈现时间近似值。这不是实际的呈现时间,但对我们来说,慢的部分不是DOM在执行其工作,而是Angular,这正是我们要测量的。下面是修改后的示例:
// Get items to populate ng-repeater
MyApiService.getItems(query)
  .then( function (data) {
    var start = new Date();
    $scope.items = data;
    // When callback finishes, Angular will process items
    // and prepare to load into DOM
    setTimeout( function () {
      // Logs when Angular is done processing repeater
      console.log('Process time: ' + (new Date() - start));
    }); // Leave timeout empty to fire on next tick
  }, function (reason) {
    console.log(reason);
  });

4
除了自己记录时间外,您可能会发现Chrome Devtools的Flame chart功能非常有用!https://developer.chrome.com/devtools/docs/cpu-profiling - runTarm

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接