Daha iyi kullanıcı deneyimi için daha hızlı değişiklik algılama uygulayın.
Angular, veri modelindeki değişikliklerin uygulamanın görünümüne yansıtılması için değişiklik algılama mekanizmasını düzenli olarak çalıştırır. Değişiklik algılama, manuel olarak veya eşzamansız bir etkinlik (örneğin, kullanıcı etkileşimi veya XHR tamamlaması) aracılığıyla tetiklenebilir.
Değişiklik algılama güçlü bir araçtır, ancak çok sık çalıştırılırsa birçok hesaplamayı tetikleyebilir ve ana tarayıcı iş parçacığını engelleyebilir.
Bu gönderide, uygulamanızın bazı bölümlerini atlayarak ve değişiklik algılamayı yalnızca gerektiğinde çalıştırarak değişiklik algılama mekanizmasını nasıl kontrol edeceğinizi ve optimize edeceğinizi öğreneceksiniz.
Inside Angular değişiklik algılama
Angular'ın değişiklik algılama özelliğinin nasıl çalıştığını anlamak için örnek bir uygulamaya göz atalım.
Uygulamanın kodunu bu GitHub kod deposunda bulabilirsiniz.
Uygulama, bir şirketin iki farklı departmanında (satış ve Ar-Ge) çalışanları listeliyor ve iki bileşenden oluşuyor:
- Uygulamanın kök bileşeni olan
AppComponent
ve - Biri satış, diğeri Ar-Ge için olmak üzere iki
EmployeeListComponent
örneği.
AppComponent
şablonunda EmployeeListComponent
öğesinin iki örneğini görebilirsiniz:
<app-employee-list
[data]="salesList"
department="Sales"
(add)="add(salesList, $event)"
(remove)="remove(salesList, $event)"
></app-employee-list>
<app-employee-list
[data]="rndList"
department="R&D"
(add)="add(rndList, $event)"
(remove)="remove(rndList, $event)"
></app-employee-list>
Her çalışanın bir adı ve sayısal bir değeri vardır. Uygulama, çalışanın sayısal değerini iş hesaplamasına iletir ve sonucu ekranda görselleştirir.
Şimdi EmployeeListComponent
ürününe göz atın:
const fibonacci = (num: number): number => {
if (num === 1 || num === 2) {
return 1;
}
return fibonacci(num - 1) + fibonacci(num - 2);
};
@Component(...)
export class EmployeeListComponent {
@Input() data: EmployeeData[];
@Input() department: string;
@Output() remove = new EventEmitter<EmployeeData>();
@Output() add = new EventEmitter<string>();
label: string;
handleKey(event: any) {
if (event.keyCode === 13) {
this.add.emit(this.label);
this.label = '';
}
}
calculate(num: number) {
return fibonacci(num);
}
}
EmployeeListComponent
, çalışan listesini ve departman adını giriş olarak kabul eder. Kullanıcı bir çalışanı kaldırmaya veya eklemeye çalıştığında bileşen, ilgili çıkışı tetikler. Bileşen, iş hesaplamasını uygulayan calculate
yöntemini de tanımlar.
EmployeeListComponent
için şablon:
<h1 title="Department">{{ department }}</h1>
<mat-form-field>
<input placeholder="Enter name here" matInput type="text" [(ngModel)]="label" (keydown)="handleKey($event)">
</mat-form-field>
<mat-list>
<mat-list-item *ngFor="let item of data">
<h3 matLine title="Name">
{{ item.label }}
</h3>
<md-chip title="Score" class="mat-chip mat-primary mat-chip-selected" color="primary" selected="true">
{{ calculate(item.num) }}
</md-chip>
</mat-list-item>
</mat-list>
Bu kod listedeki tüm çalışanlar için yinelenir ve her biri için bir liste öğesi oluşturur. Ayrıca, giriş ile EmployeeListComponent
içinde açıklanan label
özelliği arasındaki iki yönlü veri bağlamaya ilişkin bir ngModel
yönergesi içerir.
Uygulama, iki EmployeeListComponent
örneğinde aşağıdaki bileşen ağacını oluşturur:
AppComponent
, uygulamanın kök bileşenidir. Alt bileşenleri, EmployeeListComponent
öğesinin iki örneğidir. Her örnekte, departmandaki çalışanları temsil eden bir öğe listesi (E1, E2 vb.) bulunur.
Kullanıcı EmployeeListComponent
içindeki giriş kutusuna yeni bir çalışanın adını yazmaya başladığında Angular, AppComponent
tarihinden itibaren bileşen ağacının tamamı için değişiklik algılamayı tetikler. Bu, kullanıcı metin girişini yazarken Angular'ın son kontrolden beri değişmediğini doğrulamak için her çalışanla ilişkilendirilmiş sayısal değerleri sürekli olarak yeniden hesapladığı anlamına gelir.
Bunun ne kadar yavaş olabileceğini görmek için StackBlitz'de projenin optimize edilmemiş sürümünü açın ve bir çalışan adı girmeyi deneyin.
Örnek projeyi oluşturup Chrome Geliştirici Araçları'nın Performans sekmesini açarak yavaşlamanın fibonacci
işlevinden geldiğini doğrulayabilirsiniz.
- Geliştirici Araçları'nı açmak için "Control+Shift+J" (veya Mac'te "Command+Option+J") tuşlarına basın.
- Performans sekmesini tıklayın.
tıklayın. Chrome Geliştirici Araçları, topladığı tüm profil oluşturma verilerini işledikten sonra şuna benzer bir şey görürsünüz:
Listede çok sayıda çalışan varsa bu işlem tarayıcının kullanıcı arayüzü iş parçacığını engelleyebilir ve çerçevelerin düşmesine, dolayısıyla kötü bir kullanıcı deneyimine yol açabilir.
Bileşen alt ağaçlarını atlama
Kullanıcı satış EmployeeListComponent
için metin girişini yazarken Ar-Ge departmanındaki verilerin değişmediğini bilirsiniz. Bu nedenle, bileşeninde değişiklik algılama özelliğinin çalıştırılmasına gerek yoktur. Ar-Ge örneğinin değişiklik algılamayı tetiklemediğinden emin olmak için EmployeeListComponent
öğesinin changeDetectionStrategy
değerini OnPush
olarak ayarlayın:
import { ChangeDetectionStrategy, ... } from '@angular/core';
@Component({
selector: 'app-employee-list',
template: `...`,
changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['employee-list.component.css']
})
export class EmployeeListComponent {...}
Artık kullanıcı bir metin girişi girdiğinde değişiklik algılama yalnızca ilgili departman için tetikleniyor:
Orijinal uygulamaya uygulanan bu optimizasyonu burada bulabilirsiniz.
OnPush
değişikliği algılama stratejisi hakkında daha fazla bilgiyi resmi Angular dokümanlarında bulabilirsiniz.
Bu optimizasyonun etkisini görmek için StackBlitz uygulamasına yeni bir çalışan girin.
Sade boru kullanma
EmployeeListComponent
için değişiklik algılama stratejisi artık OnPush
olarak ayarlanmış olsa bile, kullanıcı ilgili metin girişini yazdığında Angular, bir departmandaki tüm çalışanlar için sayısal değeri yeniden hesaplar.
Bu davranışı iyileştirmek için sadece kanaldan yararlanabilirsiniz. Hem saf hem de karışımlı ardışık düzenler, girişleri kabul eder ve şablonda kullanılabilecek sonuçları döndürür. Bu ikisi arasındaki fark, saf ardışık düzenin, yalnızca önceki çağrıdan farklı bir giriş alırsa sonucunu yeniden hesaplamasıdır.
Uygulamanın, EmployeeListComponent
içinde tanımlanan calculate
yöntemini çağırarak çalışanın sayısal değerine göre görüntülenecek bir değer hesapladığını unutmayın. Hesaplamayı yalın ardışık düzene taşırsanız Angular, düz çizgi ifadesini yalnızca bağımsız değişkenleri değiştiğinde yeniden hesaplar. Çerçeve, referans kontrolü gerçekleştirerek kanal argümanlarının değişip değişmediğini belirler. Bu, bir çalışanın sayısal değeri güncellenmediği sürece Angular'ın yeniden hesaplama yapmayacağı anlamına gelir.
İşletme hesaplamasını CalculatePipe
adlı bir dikey çizgiye şu şekilde taşıyabilirsiniz:
import { Pipe, PipeTransform } from '@angular/core';
const fibonacci = (num: number): number => {
if (num === 1 || num === 2) {
return 1;
}
return fibonacci(num - 1) + fibonacci(num - 2);
};
@Pipe({
name: 'calculate'
})
export class CalculatePipe implements PipeTransform {
transform(val: number) {
return fibonacci(val);
}
}
Düz çizginin transform
yöntemi fibonacci
işlevini çağırır. Borunun saf olduğuna dikkat edin. Siz aksini belirtmedikçe Angular tüm boruları saf olarak kabul eder.
Son olarak, EmployeeListComponent
şablonu için içindeki ifadeyi güncelleyin:
<mat-chip-list>
<md-chip>
{{ item.num | calculate }}
</md-chip>
</mat-chip-list>
İşte bu kadar. Artık kullanıcı herhangi bir bölümle ilişkili metin girişini yazdığında uygulama, sayısal değeri çalışanlar için ayrı ayrı yeniden hesaplamaz.
Aşağıdaki uygulamada, yazmanın ne kadar daha akıcı olduğunu görebilirsiniz!
Son optimizasyonun etkisini görmek için StackBlitz'de bu örneği deneyin.
Orijinal uygulamaya ait salt ardışık düzen optimizasyonunu içeren kodu burada bulabilirsiniz.
Sonuç
Angular uygulamasında çalışma zamanı yavaşlamalarıyla karşılaştığınızda:
- Yavaşlamaların nereden geldiğini görmek için Chrome Geliştirici Araçları'nı kullanarak uygulamanın profilini oluşturun.
- Bir bileşenin alt ağaçlarını ayıklamak için
OnPush
değişiklik algılama stratejisini tanıtın. - Çerçevenin, hesaplanan değerleri önbelleğe almasını sağlamak için ağır hesaplamaları salt ardışık düzenlere taşıyın.