7 简单的性能改进

1)谨慎使用 ng-repeat

在视图中使用 ng-repeat 通常会导致性能不佳,尤其是当存在嵌套的 ng-repeat 时。

这超级慢!

<div ng-repeat="user in userCollection">
  <div ng-repeat="details in user">
    {{details}}
  </div>
</div>

尽量避免嵌套重复。提高 ng-repeat 性能的一种方法是使用 track by $index(或其他一些 id 字段)。默认情况下,ng-repeat 跟踪整个对象。使用 track by,Angular 仅通过 $index 或 object id 观察对象。

<div ng-repeat="user in userCollection track by $index">
  {{user.data}}
</div>

**使用其他方法,**如分页虚拟滚动无限滚动限制:尽可能开始,以避免迭代大型集合。

2)绑定一次

Angular 具有双向数据绑定。如果使用太多,它会降低成本。

性能较差

<!-- Default data binding has a performance cost -->
<div>{{ my.data }}</div>

更快的性能 (AngularJS> = 1.3)

<!-- Bind once is much faster -->
<div>{{ ::my.data }}</div>

<div ng-bind="::my.data"></div>

<!-- Use single binding notation in ng-repeat where only list display is needed  -->
<div ng-repeat="user in ::userCollection">
  {{::user.data}}
</div>

使用 bind once 表示法告诉 Angular 在第一系列摘要周期后等待值稳定。Angular 将在 DOM 中使用该值,然后删除所有观察者,使其成为静态值,并且不再绑定到模型。

{{}} 慢得多。

这个 ng-bind 是一个指令,它将在传递的变量上放置一个观察者。因此,当传递的值确实发生变化时,ng-bind 将仅适用。

另一方面,托架将在每个 tihuan 11 中进行脏检查和更新,即使没有必要。

3)范围功能和过滤器需要时间

AngularJS 有一个摘要循环。你的所有功能都在视图中,并且每次摘要周期运行时都会执行过滤器。每当模型更新时都会执行摘要循环,它可能会降低你的应用程序速度(在加载页面之前可以多次点击过滤器)。

避免这个:

<div ng-controller="bigCalulations as calc">
  <p>{{calc.calculateMe()}}</p>
  <p>{{calc.data | heavyFilter}}</p>
</div>

更好的方法

<div ng-controller="bigCalulations as calc">
  <p>{{calc.preCalculatedValue}}</p>
  <p>{{calc.data | lightFilter}}</p>
</div>

控制器可以是:

app.controller('bigCalulations', function(valueService) {
    // bad, because this is called in every digest loop
    this.calculateMe = function() {
        var t = 0;
        for(i = 0; i < 1000; i++) {
            t += i;
        }
        return t;
    }
    // good, because this is executed just once and logic is separated in service to    keep the controller light
    this.preCalulatedValue = valueService.valueCalculation(); // returns 499500
});

4 位观察者

观察者的表现大幅下降。对于更多的观察者,摘要循环将花费更长时间,UI 将减慢。如果观察者检测到变化,它将启动摘要循环并重新渲染视图。

在 Angular 中有三种方法可以手动监视变量的变化。

$watch() - 值得改变

$watchCollection() - 手表收藏变化(手表超过常规 $watch

$watch(..., true) - 尽可能避免这种情况,它将执行深度观察并将降低性能(手表超过 watchCollection

请注意,如果你要在视图中绑定变量以创建新手表 - 请使用 {{::variable}} 来防止创建手表,尤其是在循环中。

因此,你需要跟踪你正在使用的观察者数量。你可以使用此脚本统计观察者(归功于 @Words 像 Jared 观察者数量

(function() {
    var root = angular.element(document.getElementsByTagName('body')),
        watchers = [],
        f = function(element) {
        angular.forEach(['$scope', '$isolateScope'], function(scopeProperty) {
            if(element.data() && element.data().hasOwnProperty(scopeProperty)) {
                angular.forEach(element.data()[scopeProperty].$$watchers, function(watcher) {
                watchers.push(watcher);
                });
            }
        });

        angular.forEach(element.children(), function(childElement) {
            f(angular.element(childElement));
        });
    };
 
    f(root);
 
    // Remove duplicate watchers
    var watchersWithoutDuplicates = [];
    angular.forEach(watchers, function(item) {
        if(watchersWithoutDuplicates.indexOf(item) < 0) {
            watchersWithoutDuplicates.push(item);
        }
    });
    console.log(watchersWithoutDuplicates.length);
})();

5)ng-if / ng-show

这些功能在行为上非常相似。 ng-if 从 DOM 中删除元素,而 ng-show 只隐藏元素但保留所有处理程序。如果你有部分代码不想显示,请使用 ng-if

这取决于使用类型,但通常一个比另一个更合适。

  • 如果不需要该元素,请使用 ng-if

  • 要快速打开/关闭,请使用 ng-show/ng-hide

    <div ng-repeat="user in userCollection">
      <p ng-if="user.hasTreeLegs">I am special<!-- some complicated DOM --></p>
      <p ng-show="user.hasSubscribed">I am awesome<!-- switch this setting on and off --></p>
    </div>
    

如有疑问 - 请使用 ng-if 并测试!

6)禁用调试

默认情况下,绑定指令和作用域在代码中留下额外的类和标记,以协助各种调试工具。禁用此选项意味着你不再在摘要周期中呈现这些不同的元素。

angular.module('exampleApp', []).config(['$compileProvider', function ($compileProvider) {
    $compileProvider.debugInfoEnabled(false);
}]);

7)使用依赖注入来公开你的资源

依赖注入是一种软件设计模式,其中对象被赋予其依赖性,而不是创建它们自身的对象。它是关于删除硬编码的依赖关系,并使其可以在需要时更改它们。

你可能想知道与所有可注入函数的字符串解析相关的性能成本。Angular 通过在第一次之后缓存$ inject 属性来解决这个问题。因此,每次需要调用函数时都不会发生这种情况。

PRO TIP:如果你正在寻找性能最佳的方法,请使用$ inject 属性注释方法。这种方法完全避免了函数定义解析,因为这个逻辑包含在 annotate 函数中的以下检查中:if(!($ inject = fn。$ inject))。如果$ inject 已经可用,则不需要解析!

var app = angular.module('DemoApp', []);

var DemoController = function (s, h) {
    h.get('https://api.github.com/users/angular/repos').success(function (repos) {
        s.repos = repos;
    });
}
// $inject property annotation
DemoController['$inject'] = ['$scope', '$http'];

app.controller('DemoController', DemoController);

专题提示 2:你可以在与 ng-app 相同的元素上添加 ng-strict-di 指令,以选择严格的 DI 模式,只要服务尝试使用隐式注释,就会抛出错误。例:

<html ng-app="DemoApp" ng-strict-di>

或者如果你使用手动引导:

angular.bootstrap(document, ['DemoApp'], {
    strictDi: true
});