수색…


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 필드)로 track by $index 을 사용하는 것입니다. 기본적으로 ng-repeat 는 전체 객체를 추적합니다. track by 사용하면 Angular는 $index 또는 object id로만 객체를 감시합니다.

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

페이지 매김 , 가상 스크롤 , 무한 스크롤 또는 limitTo 와 같은 다른 접근법을 사용하십시오 . 가능한 한 큰 컬렉션을 반복하지 않도록하십시오.


2) 한 번 바인딩

각도에는 양방향 데이터 바인딩이 있습니다. 너무 많이 사용하면 속도가 느려지는 비용이 따릅니다.

느린 성능

<!-- 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 는 지시문이며 전달 된 변수에 watcher를 배치합니다. 따라서 ng-bind 는 전달 된 값이 실제로 변경 될 때만 적용됩니다.

반면에 괄호는 불필요한 경우에도 $digest 마다 더러워지고 새로 고쳐질 것입니다.


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 ).

$watch(..., true) - 가능한 한 많이 사용 하지 watchCollection , "deep watch"를 수행하고 성능을 떨어 watchCollection 보다 많은 시계)

뷰에 변수를 바인딩 할 때 새로운 시계를 생성하는 경우 {{::variable}} 를 사용하여 특히 루프에서 시계를 만들지 않도록하십시오.

결과적으로 사용중인 감시자 수를 추적해야합니다. 이 스크립트로 관찰자를 셀 수 있습니다 ( @Words와 같은 수의 관측자 )

(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-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 - ng-if 및 test를 사용하십시오!


6) 디버깅 사용 안함

기본적으로 바인딩 지시문과 범위는 다양한 디버깅 도구를 지원하기 위해 코드에 추가 클래스와 마크 업을 남깁니다. 이 옵션을 비활성화하면 다이제스트주기 동안 더 이상 이러한 다양한 요소를 렌더링하지 않습니다.

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

7) 의존성 삽입을 사용하여 리소스 노출

Dependency Injection은 오브젝트가 오브젝트 자체를 작성하지 않고 오브젝트의 종속성을 제공하는 소프트웨어 설계 패턴입니다. 하드 코딩 된 종속성을 제거하고 필요할 때마다이를 변경할 수 있습니다.

모든 주입 기능의 문자열 분석과 관련된 성능 비용에 대해 궁금해 할 것입니다. Angular는 $ inject 속성을 처음으로 캐싱하여이를 처리합니다. 따라서 함수를 호출해야 할 때마다 이런 일이 발생하지 않습니다.

PRO 팁 : 최상의 성능으로 접근 방식을 찾고 있다면 $ 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);

PRO TIP 2 : ng-app 와 동일한 요소에 ng-strict-di 지시문을 추가하여 엄격한 DI 모드를 선택하면 서비스에서 암시 적 주석을 사용하려고 할 때마다 오류가 발생합니다. 예:

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

또는 수동 부트 스트랩을 사용하는 경우 :

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

한 번 묶어 라.

Angular는 멋진 양방향 데이터 바인딩을 사용하여 평판이 좋습니다. 기본적으로 Angular는 모델 또는 뷰 구성 요소에서 데이터가 변경 될 때마다 모델 및 뷰 구성 요소 사이의 바운드 값을 지속적으로 동기화합니다.

너무 많이 사용하면 약간의 비용이 들게됩니다. 그러면 성능이 크게 향상됩니다.

실적이 좋지 않음 : {{my.data}}

일회성 바인딩을 사용하려면 변수 이름 앞에 두 개의 콜론 :: 추가하십시오. 이 경우 my.data가 정의 된 후에 만 ​​값이 업데이트됩니다. 데이터 변경 사항을 감시하지 않도록 명시 적으로 지정했습니다. Angular는 값 확인을 수행하지 않으므로 각 다이제스트 사이클에서 평가되는 표현이 줄어 듭니다.

일회성 바인딩을 사용한 좋은 성능 예제

{{::my.data}}
<span ng-bind="::my.data"></span>
<span ng-if="::my.data"></span>
<span ng-repeat="item in ::my.data">{{item}}</span>
<span ng-class="::{ 'my-class': my.data }"></div>

참고 : 이렇게하면 my.data 에 대한 양방향 데이터 바인딩이 제거되므로 응용 프로그램에서이 필드가 변경 될 때마다 동일한 내용이 뷰에 자동으로 반영되지 않습니다. 그러므로 응용 프로그램의 수명 기간 동안 변경되지 않는 값에만 사용하십시오 .

범위 기능 및 필터

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>

컨트롤러 샘플은 다음과 같습니다.

.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 = t + i;
    }
    return t;
  }
  //good, because it is executed just once and logic is separated in service to    keep the controller light
  this.preCalulatedValue = valueService.caluclateSumm(); // returns 499500
});

당직자

당직자는 어떤 가치를 지켜야하며이 가치가 바뀌는 것을 감지해야했습니다.

$watch() 또는 $watchCollection 호출 한 후 new watcher가 현재 범위의 내부 감시자 컬렉션에 추가됩니다.

그래서, 감시자는 무엇입니까?

Watcher는 모든 다이제스트주기마다 호출되는 간단한 함수이며 값을 반환합니다. Angular는 이전 호출과 같지 않은 경우 반환 된 값을 검사합니다. 함수 $watch() 또는 $watchCollection 에 두 번째 매개 변수로 전달 된 콜백이 실행됩니다.

(function() {
  angular.module("app", []).controller("ctrl", function($scope) {
    $scope.value = 10;
    $scope.$watch(
      function() { return $scope.value; },
      function() { console.log("value changed"); }
    );
  }
})();

당직자는 성과 살인자입니다. 당신이 가진 관찰자가 많을수록 다이제스트 루프를 만들기까지 더 오래 걸립니다. 느린 UI입니다. 감시자가 변경 사항을 감지하면 다이제스트 루프를 시작합니다 (모든 화면에서 다시 계산)

Angular의 변수 변경을 수동으로 감시하는 세 가지 방법이 있습니다.

$watch() - 값 변경을 감시합니다.

$watchCollection() - 콜렉션의 변경을 감시합니다 (일반 $ watch보다 많은 시계).

$watch(..., true) - 가능한 한 많이 사용 하지 말고, "deep watch"를 수행하고 성능을 떨어 뜨립니다 (watchCollection보다 많은 시계)

뷰에서 변수를 바인딩하는 경우 새로운 관찰자를 생성합니다. 특히 {{::variable}} 을 사용하여 감시자를 만들지 마십시오. 특히 루프에서

따라서 얼마나 많은 관찰자를 사용하고 있는지 추적해야합니다. 이 스크립트로 관중을 셀 수 있습니다 ( Jared처럼 @Words에 대한 신용 - 페이지에서 총 시계 수를 계산하는 방법?

(function() {
  var root = angular.element(document.getElementsByTagName("body")),
      watchers = [];

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

})();

나만의 스크립트를 만들고 싶지 않으면 ng-stats 라는 오픈 소스 유틸리티가 있습니다.이 유틸리티는 페이지에 삽입 된 실시간 차트를 사용하여 Angular가 관리하는 시계의 수를 파악하고 시간에 따른 다이제스트주기의 빈도 및 지속 시간. 이 유틸리티는 showAngularStats 라는 전역 함수를 제공 showAngularStats 함수를 호출하여 차트의 작동 방식을 구성 할 수 있습니다.

showAngularStats({
  "position": "topleft",
  "digestTimeThreshold": 16,
  "autoload": true,
  "logDigest": true,
  "logWatches": true
});

위의 예제 코드는 다음 차트를 자동으로 페이지에 표시합니다 ( 대화 형 데모 ).

ng-stats 차트의 스크린 샷

ng-show 대 ng-show

이 함수는 동작이 매우 비슷합니다. 차이점은 ng-if 가 DOM에서 요소를 제거한다는 것입니다. 표시되지 않는 코드의 상당 부분이있는 ng-if 가 가야합니다. ng-show 는 요소를 숨기지 만 모든 핸들러는 유지합니다.

ng-if

ngIf 지시문은 표현식을 기반으로 DOM 트리의 일부를 제거하거나 다시 만듭니다. ngIf에 할당 된 표현식이 거짓 값으로 평가되면 요소가 DOM에서 제거되고, 그렇지 않으면 요소의 복제본이 DOM에 다시 삽입됩니다.

ngShow 지시문은 ngShow 속성에 제공된 표현식을 기반으로 지정된 HTML 요소를 표시하거나 숨 깁니다. 요소는 ng-hide CSS 클래스를 제거하거나 요소에 추가하여 표시하거나 숨 깁니다.

<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 aweosme
    <!-- switch this setting on and off -->
  </p>
</div>

결론

사용법에 따라 달라 지지만 종종 하나가 다른 것보다 더 적합합니다 (예 : 요소가 필요하지 않은 시간의 95 %가 ng-if 사용하면 DOM 요소의 가시성을 전환해야하는 경우 ng-show ).

의심 ng-if 하여 테스트하십시오!

참고 : ng-if 는 새 격리 된 범위를 만들고 ng-showng-hide 는 생성하지 않습니다. 상위 범위 속성에 직접 액세스 할 수없는 경우 $parent.property 사용하십시오.

모델 디 바운스

<div ng-controller="ExampleController">
    <form name="userForm">
        Name:
        <input type="text" name="userName"
           ng-model="user.name"
           ng-model-options="{ debounce: 1000 }" />
            <button ng-click="userForm.userName.$rollbackViewValue();   user.name=''">Clear</button><br />
    </form>
    <pre>user.name = </pre>
</div>

위의 예에서는 1 초인 1000 밀리 초의 디 바운스 값을 설정합니다. 이것은 상당한 지연이지만, 반복적으로 탈곡에서 입력을 방지 할 수 ng-model 다수와 $digest 주기를.

입력 필드 및 기타 즉시 업데이트가 필요하지 않은 곳에서 debounce를 사용하면 각도 앱의 성능을 상당히 향상시킬 수 있습니다. 시간에 따라 지연 될 수있을뿐만 아니라 작업이 트리거 될 때 지연 될 수도 있습니다. 모든 키 입력에서 모델을 업데이트하지 않으려면 흐림도 업데이트 할 수 있습니다.

현재 범위 이외의 다른 범위에 등록 된 리스너를 항상 등록 취소합니다.

아래 표시된 것처럼 현재 범위 외의 범위는 항상 등록 취소해야합니다.

//always deregister these
$rootScope.$on(...);
$scope.$parent.$on(...);

각도가 그것을 처리 할 것이므로 현재 스코프에서리스트를 등록 취소 할 필요는 없습니다 :

//no need to deregister this
$scope.$on(...);

다른 컨트롤러로 이동하면 리스너의 $rootScope.$on 이 메모리에 남아 있습니다. 컨트롤러가 범위를 벗어나면 메모리 누수가 발생합니다.

하지마.

angular.module('app').controller('badExampleController', badExample);
badExample.$inject = ['$scope', '$rootScope'];

function badExample($scope, $rootScope) {
    $rootScope.$on('post:created', function postCreated(event, data) {});
}

해야 할 것

angular.module('app').controller('goodExampleController', goodExample);
goodExample.$inject = ['$scope', '$rootScope'];

function goodExample($scope, $rootScope) {
    var deregister = $rootScope.$on('post:created', function postCreated(event, data) {});

    $scope.$on('$destroy', function destroyScope() {
        deregister();
    });
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow