AngularJS
프로파일 링 및 성능
수색…
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-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-show
및 ng-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();
});
}