Daha iyi bir kullanıcı deneyimi için değişiklik algılamayı daha hızlı uygulayın.
Angular, değişiklik algılama mekanizmasını düzenli olarak çalıştırır. Böylece, veri modelinde yapılan değişiklikler uygulamanın görünümüne yansıtılır. Değişiklik algılama, manuel olarak veya eşzamansız bir etkinlik (örneğin, bir 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ıldığında 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.
Angular'ın değişiklik algılamasının ayrıntıları
Angular değişiklik algılama özelliğinin işleyiş şeklini anlamak için örnek bir uygulamaya göz atalım.
Uygulamanın kodunu bu GitHub deposunda bulabilirsiniz.
Uygulama, bir şirketteki iki departmanda (satış ve Ar-Ge) çalışanları listeler ve iki bileşenden oluşur:
- 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 bir işletme hesaplamasına iletir ve sonucu ekranda görselleştirir.
Şimdi EmployeeListComponent
uygulamasına 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
, giriş olarak çalışan listesini ve departman adını kabul eder. Kullanıcı bir çalışanı kaldırmaya veya eklemeye çalıştığında bileşen ilgili bir çı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ı yinelemek ve her biri için bir liste öğesi oluşturur. Ayrıca, giriş ile EmployeeListComponent
politikasında bildirilen label
özelliği arasındaki iki yönlü veri bağlaması için bir ngModel
yönergesi de içerir.
Uygulama, iki EmployeeListComponent
örneğiyle 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 örneğin, departmandaki çalışanları temsil eden bir öğe listesi (E1, E2 vb.) vardır.
Kullanıcı, EmployeeListComponent
içindeki giriş kutusuna yeni bir çalışanın adını girmeye başladığında Angular, AppComponent
tarihinden itibaren bileşen ağacının tamamı için değişiklik algılamasını tetikler. Bu, kullanıcı metin girişine yazarken Angular'ın her çalışanla ilişkili sayısal değerleri tekrar tekrar hesaplayarak son kontrolden bu yana değişmediğini doğruladığı 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+Üst Karakter+J" (veya Mac'te "Command+Option+J") tuşlarına basın.
- Performans sekmesini tıklayın.
Şimdi Kayıt'ı tıklayın (Performans panelinin sol üst köşesinde) ve uygulamadaki metin kutularından birine yazmaya başlayın. Kaydı durdurmak için birkaç saniye sonra Kaydet'i tekrar tıklayın. Chrome Geliştirici Araçları, topladığı tüm profil oluşturma verilerini işledikten sonra şuna benzer bir sonuç 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 karelerde düşüşlere yol açarak kötü bir kullanıcı deneyimine yol açabilir.
Bileşen alt ağaçları atlanıyor
Kullanıcı satış EmployeeListComponent
için metin girişini yazarken, Ar-Ge departmanındaki verilerin değişmediğini anlarsınız. Bu nedenle, bileşeninde değişiklik algılamanın çalıştırılması gerekmez. 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 yazdığında, değişiklik algılama yalnızca ilgili departman için tetiklenir:
Orijinal uygulamaya uygulanmış olan bu optimizasyonu burada bulabilirsiniz.
OnPush
değişikliği algılama stratejisi hakkında daha fazla bilgiye resmi Angular dokümanlarından ulaşabilirsiniz.
Bu optimizasyonun etkisini görmek için StackBlitz'deki uygulamaya yeni bir çalışan girin.
Saf boru kullanımı
EmployeeListComponent
için değişiklik algılama stratejisi artık OnPush
olarak ayarlanmış olsa da, kullanıcı ilgili metin girişini yazarken Angular bir departmandaki tüm çalışanlar için sayısal değeri yeniden hesaplamaya devam eder.
Bu davranışı iyileştirmek için saf kanallardan yararlanabilirsiniz. Hem saf hem de karışık ardışık düzenler, girişleri kabul eder ve bir şablonda kullanılabilecek sonuçları döndürür. İkisi arasındaki fark, tam ardışık düzenin, yalnızca önceki çağrısından farklı bir giriş alması halinde 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österilecek bir değer hesapladığını unutmayın. Hesaplamayı yalın bir çizgiye taşırsanız Angular, dikey çizgi ifadesini yalnızca bağımsız değişkenleri değiştiğinde yeniden hesaplar. Çerçeve, referans kontrolü gerçekleştirerek borunun bağımsız değişkenlerinin değişip değişmediğini belirler. Bu durumda, bir çalışanın sayısal değeri güncellenmediği sürece Angular yeniden hesaplama yapmaz.
İş hesaplamasını CalculatePipe
adlı bir kanala nasıl taşıyacağınız aşağıda açıklanmıştır:
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);
}
}
Borunun transform
yöntemi, fibonacci
işlevini çağırır. Borunun saf olduğuna dikkat edin. Angular, aksini belirtmediğiniz sürece tüm boruları saf olarak kabul eder.
Son olarak, EmployeeListComponent
şablonu içinde bulunan 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 departmanla ilişkili metin girişini girdiğinde uygulama her bir çalışanın sayısal değerini yeniden hesaplamaz.
Aşağıdaki uygulamada yazı yazmanın ne kadar akıcı olduğunu görebilirsiniz.
Son optimizasyonun etkisini görmek için StackBlitz'deki bu örneği deneyin.
Orijinal uygulamanın tam kanal optimizasyonunu içeren kodu burada bulabilirsiniz.
Sonuç
Bir Angular uygulamasında çalışma zamanı yavaşlamalarıyla karşılaştığınızda:
- Yavaşlamaların kaynağını görmek için Chrome Geliştirici Araçları'nı kullanarak uygulamanın profilini çıkarın.
- Bir bileşenin alt ağaçlarını budamak için
OnPush
değişikliği algılama stratejisini uygulayın. - Çerçevenin, hesaplanan değerleri önbelleğe almasına izin vermek için ağır hesaplamaları yalın ardışık çizgilere taşıyın.