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
});