
이 문서에는 각도 응용 분야의 성능을 향상시키는 데 도움이되는 관행 목록이 포함되어 있습니다. "Angular Performance Checklist"는 응용 프로그램의 서버 측 사전 렌더링 및 번들링에서 런타임 성능 및 프레임 워크에서 수행하는 변경 감지 최적화에 이르기까지 다양한 주제를 다룹니다.
문서는 두 가지 주요 섹션으로 나뉩니다.
일부 관행은 두 범주 모두에 영향을 미치므로 약간의 교차로가있을 수 있지만 사용 사례의 차이와 그 의미는 명시 적으로 언급됩니다.
대부분의 하위 섹션은 특정 관행과 관련된 도구를 목록으로하여 개발 흐름을 자동화하여보다 효율적으로 만들 수 있습니다.
대부분의 관행은 HTTP/1.1 및 HTTP/2에 모두 유효합니다. 예외를 만드는 관행은 어떤 버전의 프로토콜을 적용 할 수 있는지 지정하여 언급됩니다.
enableProdMode 사용하십시오ChangeDetectionStrategy.OnPush*ngFor 지침trackBy 옵션을 사용하십시오이 섹션의 일부 도구는 여전히 개발 중이며 변경 될 수 있습니다. Angular Core Team은 가능한 한 많은 응용 프로그램의 빌드 프로세스를 자동화하기 위해 노력하고 있으므로 많은 일이 투명하게 발생할 것입니다.
번들링은 사용자가 요청한 응용 프로그램을 제공하기 위해 브라우저가 수행 해야하는 요청 수를 줄이기위한 표준 사례입니다. 본질적으로, Bundler는 입력으로 진입 점 목록을 입력하고 하나 이상의 번들을 생성합니다. 이러한 방식으로 브라우저는 각 개별 리소스를 개별적으로 요청하는 대신 몇 가지 요청 만 수행하여 전체 응용 프로그램을 얻을 수 있습니다.
애플리케이션이 자라면서 모든 것을 단일 큰 번들로 묶는 것은 다시 비생산적 일 것입니다. 웹 팩을 사용하여 코드 분할 기술을 탐색하십시오.
서버 푸시 기능으로 인해 추가 HTTP 요청은 HTTP/2와 관련이 없습니다.
압형
응용 프로그램을 효율적으로 묶을 수있는 도구는 다음과 같습니다.
자원
이러한 관행을 통해 응용 프로그램의 페이로드를 줄임으로써 대역폭 소비를 최소화 할 수 있습니다.
압형
자원
비록 우리는 공백 문자 ( s regex와 일치하는 문자)를 보지 못하지만 여전히 네트워크를 통해 전송되는 바이트로 표시됩니다. 템플릿에서 공백을 최소로 줄이면 AOT 코드의 번들 크기를 더욱 떨어 뜨릴 수 있습니다.
고맙게도, 우리는 수동으로 이것을 할 필요가 없습니다. ComponentMetadata 인터페이스는 기본적으로 기본적으로 값 false 의미를 갖는 속성 preserveWhitespaces 제공합니다. 기본적으로 Angular 컴파일러는 공백을 다듬어 응용 프로그램의 크기를 더욱 줄입니다. 우리가 속성을 true Angular로 설정하면 공백을 보존합니다.
응용 프로그램의 최종 버전의 경우 일반적으로 Angular 및/또는 타사 라이브러리, 심지어 우리가 작성한 라이브러리에서 제공하는 전체 코드를 사용하지 않습니다. ES2015 모듈의 정적 특성 덕분에 앱에서 참조되지 않은 코드를 제거 할 수 있습니다.
예
// foo.js
export foo = ( ) => 'foo' ;
export bar = ( ) => 'bar' ;
// app.js
import { foo } from './foo' ;
console . log ( foo ( ) ) ; 일단 우리가 트리 쉐이크와 번들 app.js 얻으면 우리는 다음을 얻을 수 있습니다.
let foo = ( ) => 'foo' ;
console . log ( foo ( ) ) ; 이는 사용하지 않은 수출 bar 최종 번들에 포함되지 않음을 의미합니다.
압형
참고 : GCC는 아직 export * 지원하지 않으며, 이는 "배럴"패턴을 많이 사용하기 때문에 각도 응용 프로그램을 구축하는 데 필수적입니다.
자원
Angular 버전 6이 출시 된 이후, Angular Team은 서비스가 나무를 흔들릴 수 있도록 새로운 기능을 제공했습니다. 즉, 다른 서비스 나 구성 요소가 사용하지 않는 한 서비스가 최종 번들에 포함되지 않음을 의미합니다. 이렇게하면 번들에서 사용되지 않은 코드를 제거하여 번들 크기를 줄일 수 있습니다.
@Injectable() 데코레이터를 사용할 때 제공된 위치를 정의하여 providedIn 속성을 사용하여 서비스 트리를 할 수있게 할 수 있습니다. 그런 다음 NgModule 선언의 providers 속성과 다음과 같이 가져 오기 명령문에서 제거해야합니다.
전에:
// app.module.ts
import { NgModule } from '@angular/core'
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'
import { environment } from '../environments/environment'
import { MyService } from './app.service'
@ NgModule ( {
declarations : [
AppComponent
] ,
imports : [
...
] ,
providers : [ MyService ] ,
bootstrap : [ AppComponent ]
} )
export class AppModule { } // my-service.service.ts
import { Injectable } from '@angular/core'
@ Injectable ( )
export class MyService { }후에:
// app.module.ts
import { NgModule } from '@angular/core'
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'
import { environment } from '../environments/environment'
@ NgModule ( {
declarations : [
AppComponent
] ,
imports : [
...
] ,
providers : [ ] ,
bootstrap : [ AppComponent ]
} )
export class AppModule { } // my-service.service.ts
import { Injectable } from '@angular/core'
@ Injectable ( {
providedIn : 'root'
} )
export class MyService { } MyService 구성 요소/서비스에 주입되지 않으면 번들에 포함되지 않습니다.
자원
야생 도구 (예 : GCC, 롤업 등)에서 사용 가능한 문제는 각도 구성 요소의 HTML 유사 템플릿이며, 기능으로 분석 할 수 없습니다. 이렇게하면 템플릿 내에서 어떤 지시문이 참조되는지 확실하지 않기 때문에 나무 흔들리는 지원이 덜 효율적입니다. AoT 컴파일러는 각도 HTML 유사 템플릿을 ES2015 모듈 가져 오기로 JavaScript 또는 TypeScript로 전달합니다. 이런 식으로 우리는 번들링 중에 트리 쉐이크를 효율적으로 쉐이크하고 각도, 타사 라이브러리 또는 스스로 정의 된 사용되지 않은 모든 지침을 제거 할 수 있습니다.
자원
대역폭 사용 감소를위한 응답의 페이로드 표준 실습 압축. 헤더 Accept-Encoding 의 값을 지정하면 브라우저는 클라이언트 시스템에서 압축 알고리즘을 사용할 수있는 서버를 힌트합니다. 반면, 서버는 응답의 Content-Encoding 헤더의 값을 설정하여 응답 압축을 위해 알고리즘이 선택한 브라우저에 선택한 브라우저를 알려줍니다.
압형
여기의 툴링은 각도 별이 아니며 전적으로 우리가 사용하는 웹/애플리케이션 서버에 따라 다릅니다. 일반적인 압축 알고리즘은 다음과 같습니다.
자원
리소스 사전 가져 오기는 사용자 경험을 향상시키는 좋은 방법입니다. 자산 (이미지, 스타일, 게으름하게로드하려는 모듈 등) 또는 데이터를 미리 가져 오는 데 사용할 수 있습니다. 사전 가져 오는 전략이 다르지만 대부분은 응용 프로그램의 세부 사항에 따라 다릅니다.
대상 애플리케이션에 수백 개의 종속성이있는 거대한 코드 기반이있는 경우, 위에 나열된 관행이 번들을 합리적인 크기로 줄이는 데 도움이되지 않을 수 있습니다 (합리적인 것은 100k 또는 2m 일 수 있습니다.
이러한 경우 좋은 솔루션은 애플리케이션 모듈 중 일부를 게으르게로드하는 것일 수 있습니다. 예를 들어, 전자 상거래 시스템을 구축한다고 가정 해 봅시다. 이 경우 사용자를 향한 UI와 독립적으로 관리자 패널을로드 할 수 있습니다. 관리자가 신제품을 추가해야하면 필요한 UI를 제공하고자합니다. 사용 사례/비즈니스 요구 사항에 따라 "제품 페이지 추가"또는 전체 관리자 패널 일 수 있습니다.
압형
다음과 같은 라우팅 구성이 있다고 가정 해 봅시다.
// Bad practice
const routes : Routes = [
{ path : '' , redirectTo : '/dashboard' , pathMatch : 'full' } ,
{ path : 'dashboard' , loadChildren : ( ) => import ( './dashboard.module' ) . then ( mod => mod . DashboardModule ) } ,
{ path : 'heroes' , loadChildren : ( ) => import ( './heroes.module' ) . then ( mod => mod . HeroesModule ) }
] ; URL을 사용하여 사용자가 처음으로 응용 프로그램을 엽니 다 : https://example.com/ 그들은 Path dashboard 로 Lazy-Route를 트리거하는 /dashboard 로 리디렉션됩니다. Module의 부트 스트랩 구성 요소를 렌더링하기 위해 Angular를 주문하려면 파일 dashboard.module 및 모든 종속성을 다운로드해야합니다. 나중에 파일은 JavaScript VM에 의해 구문 분석되어 평가되어야합니다.
추가 HTTP 요청을 트리거하고 초기 페이지로드 중에 불필요한 계산을 수행하는 것은 초기 페이지 렌더링이 느려지기 때문에 나쁜 관행입니다. 기본 페이지 경로를 게으른 것으로 선언하는 것을 고려하십시오.
캐싱은 한 자원이 최근에 요청되면 가까운 시일 내에 다시 요청 될 수 있다는 휴리스틱을 이용하여 응용 프로그램을 가속화하기위한 또 다른 일반적인 관행입니다.
캐싱 데이터의 경우 일반적으로 사용자 정의 캐싱 메커니즘을 사용합니다. 정적 자산을 캐싱하기 위해 표준 브라우저 캐싱 또는 캐시 스토리지 API와 함께 서비스 작업자를 사용할 수 있습니다.
응용 프로그램의 인식 된 성능을 더 빨리 만들려면 응용 프로그램 쉘을 사용하십시오.
응용 프로그램 쉘은 응용 프로그램이 곧 전달 될 것임을 나타 내기 위해 사용자에게 표시하는 최소 사용자 인터페이스입니다. 애플리케이션 쉘을 동적으로 생성하려면 중고 렌더링 플랫폼에 따라 조건부로 요소를 표시하는 사용자 정의 지침과 함께 각도 범용을 사용할 수 있습니다 (즉, platform-server 사용할 때 앱 쉘을 제외한 모든 것을 숨기십시오).
압형
자원
서비스 작업자를 브라우저에있는 HTTP 프록시로 생각할 수 있습니다. 클라이언트로부터 보낸 모든 요청은 먼저 서비스 작업자가 차단하거나 네트워크를 통해 전달할 수 있습니다.
ng add @angular/pwa 실행하여 각도 프로젝트에 서비스 작업자를 추가 할 수 있습니다.
압형
자원
이 섹션에는 초당 60 프레임 (FPS)의 매끄러운 사용자 경험을 제공하기 위해 적용 할 수있는 사례가 포함됩니다.
enableProdMode 사용하십시오개발 모드에서 Angular는 변화를 수행하는 것이 바인딩에 대한 추가 변경을 초래하지 않는지 확인하기 위해 추가 점검을 수행합니다. 이런 식으로 프레임 워크는 단방향 데이터 흐름이 따랐다는 것을 보장합니다.
생산에 대한 이러한 변경 사항을 비활성화하려면 enableProdMode 호출하는 것을 잊지 마십시오.
import { enableProdMode } from '@angular/core' ;
if ( ENV === 'production' ) {
enableProdMode ( ) ;
}AOT는 트리 쉐이킹을 수행하여보다 효율적인 번들링을 달성 할뿐만 아니라 응용 프로그램의 런타임 성능을 향상시키는 데 도움이 될 수 있습니다. AOT의 대안은 JIT (Just-In-Time Compilation)이므로 런타임이 수행되므로 빌드 프로세스의 일부로 컴파일을 수행하여 응용 프로그램 렌더링에 필요한 계산량을 줄일 수 있습니다.
압형
ng serve --prod 사용하는 Angular-Cli자원
일반적인 단일 페이지 응용 프로그램 (SPA)의 일반적인 문제는 코드가 일반적으로 단일 스레드로 실행된다는 것입니다. 즉, 60fps로 원활한 사용자 경험을 달성하려면 개별 프레임간에 실행할 수있는 최대 16ms가 렌더링되고 있으며 그렇지 않으면 절반으로 떨어질 것입니다.
거대한 구성 요소 트리가있는 복잡한 응용 분야에서 변경 감지가 매 초마다 수백만 건의 점검을 수행 해야하는 경우 프레임을 떨어 뜨리기가 어렵지 않습니다. Angular의 불가지론과 DOM 아키텍처에서 분리 된 덕분에 웹 워커에서 전체 애플리케이션 (변경 감지 포함)을 실행하고 주요 UI 스레드를 렌더링에만 책임지게 할 수 있습니다.
압형
자원
전통적인 스파의 큰 문제는 초기 렌더링에 필요한 전체 자바 스크립트를 사용할 수있을 때까지 렌더링 할 수 없다는 것입니다. 이것은 두 가지 큰 문제로 이어집니다.
서버 측 렌더링은 서버의 요청 된 페이지를 사전 렌더링하고 초기 페이지로드 중에 렌더링 된 페이지의 마크 업을 제공 하여이 문제를 해결합니다.
압형
자원
각 비동기 이벤트에서 각도는 전체 구성 요소 트리에서 변경 감지를 수행합니다. 변경 사항을 감지하는 코드는 인라인 캐싱에 최적화되어 있지만 이는 여전히 복잡한 응용 프로그램에서 큰 계산이 될 수 있습니다. 변화 감지의 성능을 향상시키는 방법은 최근 조치에 따라 변경되지 않아야하는 하위 트리에 대해 수행하지 않는 것입니다.
ChangeDetectionStrategy.OnPush OnPush Change Detection 전략을 통해 구성 요소 트리의 하위 트리에 대한 변경 감지 메커니즘을 비활성화 할 수 있습니다. 변경 감지 전략을 구성 요소로 설정함으로써 값 ChangeDetectionStrategy.OnPush 구성 요소가 다른 입력을 수신 한 경우 에만 변경 감지를 수행하게합니다. Angular는 입력을 참조별로 이전 입력과 비교할 때 입력을 다른 것으로 간주하며 참조 점검 결과는 false 입니다. 불변의 데이터 구조와 함께 OnPush 이러한 "순수한"구성 요소에 큰 성능을 가져올 수 있습니다.
자원
사용자 정의 변경 감지 메커니즘을 구현하는 또 다른 방법은 구성 요소에 대한 변경 검출기 (CD)를 detach 하고 reattach 하는 것입니다. CD Angular를 detach 하면 전체 구성 요소 하위 트리를 확인하지 않습니다.
이 관행은 일반적으로 사용자 조치 또는 외부 서비스와의 상호 작용이 필요한 것보다 변경 감지를 더 자주 트리거 할 때 사용됩니다. 이러한 경우 변경 검출기 분리를 고려하고 변경 감지를 수행 할 때만이를 재발하는 것을 고려할 수 있습니다.
Angular의 변화 감지 메커니즘은 Zone.js 덕분에 트리거되고 있습니다. Zone.js Monkey는 브라우저의 모든 비동기 API를 패치하고 Async 콜백의 실행이 끝날 때 변경 감지를 트리거합니다. 드문 경우 , 우리는 주어진 코드가 각도 영역의 컨텍스트 외부에서 실행되기를 원할 수 있습니다. 이 경우 NgZone 인스턴스의 runOutsideAngular 방법을 사용할 수 있습니다.
예
아래 스 니펫에서는이 연습을 사용하는 구성 요소에 대한 예를 볼 수 있습니다. _incrementPoints 메소드가 호출되면 구성 요소는 10ms마다 _points 속성을 증가시키기 시작합니다 (기본적으로). 증분은 애니메이션의 환상을 만듭니다. 이 경우 전체 구성 요소 트리의 변경 감지 메커니즘을 트리거하고 싶지 않기 때문에 10ms마다 각도 영역의 컨텍스트 밖에서 _incrementPoints 실행하고 수동으로 DOM을 업데이트 할 수 있습니다 ( points 세터 참조).
@ Component ( {
template : '<span #label></span>'
} )
class PointAnimationComponent {
@ Input ( ) duration = 1000 ;
@ Input ( ) stepDuration = 10 ;
@ ViewChild ( 'label' ) label : ElementRef ;
@ Input ( ) set points ( val : number ) {
this . _points = val ;
if ( this . label ) {
this . label . nativeElement . innerText = this . _pipe . transform ( this . points , '1.0-0' ) ;
}
}
get points ( ) {
return this . _points ;
}
private _incrementInterval : any ;
private _points : number = 0 ;
constructor ( private _ngZone : NgZone , private _pipe : DecimalPipe ) { }
ngOnChanges ( changes : any ) {
const change = changes . points ;
if ( ! change ) {
return ;
}
if ( typeof change . previousValue !== 'number' ) {
this . points = change . currentValue ;
} else {
this . points = change . previousValue ;
this . _ngZone . runOutsideAngular ( ( ) => {
this . _incrementPoints ( change . currentValue ) ;
} ) ;
}
}
private _incrementPoints ( newVal : number ) {
const diff = newVal - this . points ;
const step = this . stepDuration * ( diff / this . duration ) ;
const initialPoints = this . points ;
this . _incrementInterval = setInterval ( ( ) => {
let nextPoints = Math . ceil ( initialPoints + diff ) ;
if ( this . points >= nextPoints ) {
this . points = initialPoints + diff ;
clearInterval ( this . _incrementInterval ) ;
} else {
this . points += step ;
}
} , this . stepDuration ) ;
}
}경고 :이 연습을 제대로 사용하지 않으면 DOM의 일관되지 않은 상태로 이어질 수 있기 때문에이 관행을 매우 신중하게 사용하십시오. 또한 위의 코드는 웹 워즈에서 실행되지 않을 것입니다. 웹 워커 호환을 위해서는 Angular의 렌더러를 사용하여 레이블의 값을 설정해야합니다.
Angular는 Zone.js를 사용하여 응용 프로그램에서 발생한 이벤트를 가로 채고 자동으로 변경 감지를 실행합니다. 기본적으로 이는 브라우저의 마이크로 워스 큐가 비어있을 때 발생하며 경우에 따라 중복주기를 호출 할 수 있습니다. V9에서 Angular는 ngZoneEventCoalescing 켜서 이벤트 변경 탐지를 합산하는 방법을 제공합니다.
platformBrowser ( )
. bootstrapModule ( AppModule , { ngZoneEventCoalescing : true } ) ; 위의 구성은 마이크로스크 큐에 연결하는 대신 requestAnimationFrame 으로 변경 감지를 예약하여 검사를 덜 자주 실행하고 계산주기를 적게 소비합니다.
경고 : NGZONEEVENTCOALESCING : True는 지속적으로 변경 감지를 실행할 때 릴레이하는 기존 앱을 중단 할 수 있습니다.
자원
인수로서 @Pipe 데코레이터는 다음 형식의 객체 문자 그대로 수락합니다.
interface PipeMetadata {
name : string ;
pure : boolean ;
}순수한 플래그는 파이프가 글로벌 상태에 의존하지 않으며 부작용을 생성하지 않음을 나타냅니다. 이는 파이프가 동일한 입력으로 호출 될 때 동일한 출력을 반환 함을 의미합니다. 이러한 방식으로 Angular는 파이프가 호출 된 모든 입력 매개 변수의 출력을 캐시하고 각 평가에서이를 재편 처리 할 필요가 없도록 재사용 할 수 있습니다.
pure 속성의 기본값은 true 입니다.
*ngFor 지침 *ngFor 지시문은 컬렉션을 렌더링하는 데 사용됩니다.
trackBy 옵션을 사용하십시오 기본적으로 *ngFor 객체 고유성을 참조별로 식별합니다.
즉, 개발자가 항목의 내용을 업데이트하는 동안 객체에 대한 참조를 깨뜨릴 때 기존 객체를 제거하고 새 객체의 추가로 취급합니다. 이것은 목록에서 오래된 DOM 노드를 파괴하고 그 자리에 새로운 DOM 노드를 추가하는 데 영향을 미칩니다.
개발자는 객체 고유성 *ngFor 식별 trackBy 방법에 대한 힌트를 제공 할 수 있습니다. 추적 함수는 index 와 item 두 가지 인수를 취합니다. Angular는 추적 함수에서 반환 된 값을 사용하여 항목 아이덴티티를 추적합니다. 특정 레코드의 ID를 고유 키로 사용하는 것이 매우 일반적입니다.
예
@ Component ( {
selector : 'yt-feed' ,
template : `
<h1>Your video feed</h1>
<yt-player *ngFor="let video of feed; trackBy: trackById" [video]="video"></yt-player>
`
} )
export class YtFeedComponent {
feed = [
{
id : 3849 , // note "id" field, we refer to it in "trackById" function
title : "Angular in 60 minutes" ,
url : "http://youtube.com/ng2-in-60-min" ,
likes : "29345"
} ,
// ...
] ;
trackById ( index , item ) {
return item . id ;
}
} DOM 요소를 렌더링하는 것은 일반적으로 UI에 요소를 추가 할 때 가장 비싼 작업입니다. 주요 작업은 일반적으로 요소를 DOM에 삽입하고 스타일을 적용함으로써 발생합니다. *ngFor 많은 요소를 렌더링하면 브라우저 (특히 오래된 브라우저)가 속도가 느려지고 모든 요소의 렌더링을 완료하는 데 더 많은 시간이 필요할 수 있습니다. 이것은 Angular에만 국한되지 않습니다.
렌더링 시간을 줄이려면 다음을 시도하십시오.
*ngFor 섹션에서 렌더링 된 DOM 요소의 양을 줄입니다. 일반적으로 불필요한/미사용 DOM 요소는 템플릿을 계속해서 확장하여 발생합니다. 그 구조를 다시 생각하면 아마도 상황이 훨씬 쉬워 질 것입니다.ng-container 사용하십시오자원
*ngFor 대한 공식 문서각도는 모든 변경 감지주기 후 템플릿 표현식을 실행합니다. 변경 감지주기는 약속 해상도, HTTP 결과, 타이머 이벤트, 키 프레스 및 마우스 이동과 같은 많은 비동기 활동에 의해 트리거됩니다.
표현식은 신속하게 완료되거나 사용자 경험이 특히 느린 장치에서 드래그 할 수 있습니다. 계산이 비싸면 캐싱 값을 고려하십시오.
자원
실습 목록은 새로운/업데이트 된 관행으로 시간이 지남에 따라 동적으로 발전합니다. 누락 된 것을 발견하거나 어떤 관행이 개선 될 수 있다고 생각하는 경우, 주저하지 말고 문제를 해결하거나 PR을 해고하십시오. 자세한 내용은 아래의 "기여"섹션을 살펴보십시오.
누락, 불완전하거나 부정확 한 것을 발견하면 풀 요청에 큰 감사를드립니다. 문서에 포함되지 않은 관행에 대한 논의는 문제를 열어주십시오.
MIT