Cara meningkatkan kinerja aplikasi Angular Anda
Diterbitkan: 2020-04-10Ketika berbicara tentang kerangka kerja frontend terbesar, tidak mungkin untuk tidak menyebutkan Angular. Ini membutuhkan banyak upaya dari programmer untuk mempelajarinya dan menggunakannya dengan bijak. Sayangnya, ada risiko bahwa pengembang yang tidak berpengalaman di Angular dapat menggunakan beberapa fiturnya dengan cara yang tidak efisien.
Salah satu dari banyak hal yang selalu perlu Anda kerjakan sebagai pengembang frontend adalah kinerja aplikasi. Sebagian besar proyek saya sebelumnya berfokus pada aplikasi perusahaan besar yang terus diperluas dan dikembangkan. Kerangka kerja frontend akan sangat berguna di sini, tetapi penting untuk menggunakannya dengan benar dan masuk akal.
Saya telah menyiapkan daftar singkat tentang strategi dan tip peningkatan kinerja paling populer yang dapat membantu Anda meningkatkan kinerja aplikasi Angular Anda secara instan. Harap diingat bahwa semua petunjuk di sini berlaku untuk Angular di versi 8.
ChangeDetectionStrategy dan ChangeDetectorRef
Change Detection (CD) adalah mekanisme Angular untuk mendeteksi perubahan data dan secara otomatis bereaksi terhadapnya. Kami dapat membuat daftar jenis dasar perubahan status aplikasi standar:
- Acara
- Permintaan HTTP
- Timer
Ini adalah interaksi asinkron. Pertanyaannya adalah: bagaimana Angular mengetahui bahwa beberapa interaksi (seperti klik, interval, permintaan http) terjadi dan ada kebutuhan untuk memperbarui status aplikasi?
Jawabannya adalah ngZone , yang pada dasarnya adalah sistem kompleks yang dimaksudkan untuk melacak interaksi asinkron. Jika semua operasi didaftarkan oleh ngZone, Angular tahu kapan harus bereaksi terhadap beberapa perubahan. Tetapi ia tidak tahu apa yang sebenarnya telah berubah dan meluncurkan mekanisme Deteksi Perubahan , yang memeriksa semua komponen secara mendalam.
Setiap komponen di aplikasi Angular memiliki Detektor Perubahannya sendiri, yang menentukan bagaimana komponen ini harus bertindak saat Deteksi Perubahan diluncurkan – misalnya, jika ada kebutuhan untuk merender ulang DOM komponen (yang merupakan operasi yang agak mahal). Saat Angular meluncurkan Deteksi Perubahan, setiap komponen akan diperiksa dan tampilannya (DOM) dapat dirender ulang secara default.
Kita dapat menghindari ini dengan menggunakan ChangeDetectionStrategy.OnPush:
@Komponen({ pemilih: 'foobar', templateUrl: './foobar.component.html', styleUrls: ['./foobar.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush })
Seperti yang Anda lihat pada contoh kode di atas, kita harus menambahkan parameter tambahan ke dekorator komponen. Tetapi bagaimana strategi pendeteksian perubahan baru ini benar-benar bekerja?
Strategi tersebut memberi tahu Angular bahwa komponen tertentu hanya bergantung pada @Inputs()-nya. Juga, semua komponen @Inputs() akan bertindak seperti objek yang tidak dapat diubah (misalnya ketika kita hanya mengubah properti di @Input() objek, tanpa mengubah referensi, komponen ini tidak akan diperiksa). Ini berarti bahwa banyak pemeriksaan yang tidak perlu akan dihilangkan dan itu akan meningkatkan kinerja aplikasi kita.
Komponen dengan ChangeDetectionStrategy.OnPush hanya akan diperiksa dalam kasus berikut:
- @Input() referensi akan berubah
- Suatu peristiwa akan dipicu di templat komponen atau salah satu turunannya
- Dapat diamati dalam komponen akan memicu suatu peristiwa
- CD akan dijalankan secara manual menggunakan layanan ChangeDetectorRef
- pipa async digunakan pada tampilan (pipa async menandai komponen yang akan diperiksa untuk perubahan – ketika aliran sumber akan memancarkan nilai baru, komponen ini akan diperiksa)
Jika tidak ada hal di atas yang terjadi, menggunakan ChangeDetectionStrategy.OnPush di dalam komponen tertentu menyebabkan komponen dan semua komponen bersarang tidak diperiksa setelah CD diluncurkan.
Untungnya, kita masih dapat memiliki kendali penuh untuk bereaksi terhadap perubahan data dengan menggunakan layanan ChangeDetectorRef. Kita harus ingat bahwa dengan ChangeDetectionStrategy.OnPush di dalam batas waktu, permintaan, panggilan balik langganan, kita perlu mengaktifkan CD secara manual jika kita benar-benar membutuhkan ini:
penghitung = 0; konstruktor(perubahan pribadiDetectorRef: ChangeDetectorRef) {} ngOnInit() { setTimeout(() => { this.counter += 1000; this.changeDetectorRef.detectChanges(); }, 1000); }
Seperti yang bisa kita lihat di atas, dengan memanggil this.changeDetectorRef.detectChanges() di dalam fungsi timeout kita, kita bisa memaksa CD secara manual. Jika penghitung digunakan di dalam templat dengan cara apa pun, nilainya akan disegarkan.
Tip terakhir di bagian ini adalah tentang menonaktifkan CD secara permanen untuk komponen tertentu. Jika kami memiliki komponen statis dan kami yakin bahwa statusnya tidak boleh diubah, kami dapat menonaktifkan CD secara permanen:
this.changeDetectorRef.detach()
Kode ini harus dijalankan di dalam metode siklus hidup ngAfterViewInit() atau ngAfterViewChecked() , untuk memastikan bahwa tampilan kita dirender dengan benar sebelum kita menonaktifkan penyegaran data. Komponen ini tidak akan diperiksa lagi selama CD , kecuali jika kita memicu detectChanges() secara manual.
Panggilan fungsi dan getter dalam template
Menggunakan panggilan fungsi di dalam templat menjalankan fungsi ini setiap kali Detektor Perubahan berjalan. Situasi yang sama terjadi dengan getter . Jika memungkinkan, kita harus berusaha menghindarinya. Dalam kebanyakan kasus, kita tidak perlu menjalankan fungsi apa pun di dalam template komponen selama setiap CD dijalankan. Alih-alih itu kita bisa menggunakan pipa murni.
Pipa murni
Pipa murni adalah jenis pipa dengan output yang hanya bergantung pada inputnya, tanpa efek samping. Untungnya, semua pipa di Angular murni secara default.
@Pipa({ nama: 'huruf besar', murni: benar })
Tetapi mengapa kita harus menghindari penggunaan pipa dengan pure: false? Jawabannya adalah Ubah Deteksi lagi. Pipa yang tidak murni dieksekusi di setiap CD yang dijalankan, yang tidak diperlukan dalam banyak kasus dan menurunkan kinerja aplikasi kita. Berikut adalah contoh fungsi yang dapat kita ubah menjadi pipa murni:
transform(nilai: string, batas = 60, elipsis = '...') { if (!nilai || nilai.panjang <= batas) { nilai kembali; } const numberOfVisibleCharacters = nilai.substr(0, batas).lastIndexOf(' '); return `${value.substr(0, numberOfVisibleCharacters)}${ellipsis}`; }
Dan mari kita lihat tampilannya:
<p class="description">memotong(teks, 30)</p>
Kode di atas mewakili fungsi murni – tidak ada efek samping, output hanya bergantung pada input. Dalam hal ini, kita cukup mengganti fungsi ini dengan pipa murni :
@Pipa({ nama: 'memotong', murni: benar }) kelas ekspor TruncatePipe mengimplementasikan PipeTransform { transform(nilai: string, batas = 60, elipsis = '...') { ... } }
Dan akhirnya, dalam tampilan ini, kita mendapatkan kode, yang akan dieksekusi hanya ketika teks diubah, terlepas dari Deteksi Perubahan .
<p class="description">{{ teks | potong: 30 }}</p>
Modul pemuatan dan pramuat yang lambat
Ketika aplikasi Anda memiliki lebih dari satu halaman, Anda pasti harus mempertimbangkan untuk membuat modul untuk setiap bagian logis dari proyek Anda, terutama modul lazy loading . Mari kita pertimbangkan kode router Angular sederhana:

rute const: Rute = [ { jalur: '', komponen: Komponen Rumah }, { jalur: 'foo', loadChildren: ()=> import("./foo/foo.module").then(m => m.FooModule) }, { jalur: 'bar', loadChildren: ()=> import("./bar/bar.module").then(m => m.BarModule) } ] @NgModule({ ekspor: [RouterModule], impor: [RouterModule.forRoot(rute)] }) kelas AppRoutingModule {}
Pada contoh di atas kita dapat melihat bahwa fooModule dengan semua asetnya akan dimuat hanya ketika pengguna mencoba memasukkan rute tertentu (foo atau bar). Angular juga akan menghasilkan potongan terpisah untuk modul ini. Lazy loading akan mengurangi beban awal .
Kita dapat melakukan beberapa optimasi lebih lanjut. Mari kita asumsikan bahwa kita ingin membuat modul pemuatan aplikasi kita di latar belakang. Untuk kasus ini, kita dapat menggunakan preloadingStrategy. Angular secara default memiliki dua jenis PreloadingStrategy:
- Tanpa Pramuat
- PramuatSemuaModul
Dalam kode di atas strategi NoPreloading digunakan secara default. Aplikasi mulai memuat modul tertentu berdasarkan permintaan pengguna (ketika pengguna ingin melihat rute tertentu). Kita dapat mengubah ini dengan menambahkan beberapa konfigurasi tambahan ke Router.
@NgModule({ ekspor: [RouterModule], impor: [RouterModule.forRoot(rute, { preloadingStrategy: PreloadAllModules }] }) kelas AppRoutingModule {}
Konfigurasi ini menyebabkan rute saat ini ditampilkan sesegera mungkin dan setelah itu aplikasi akan mencoba memuat modul lain di latar belakang. Cerdas, bukan? Tapi itu tidak semua. Jika solusi ini tidak sesuai dengan kebutuhan kita, kita cukup menulis strategi kustom kita sendiri .
Mari kita asumsikan bahwa kita ingin memuat hanya modul yang dipilih, misalnya, BarModule. Kami menunjukkan ini dengan menambahkan bidang tambahan untuk bidang data.
rute const: Rute = [ { jalur: '', komponen: Komponen Rumah data: { pramuat: salah } }, { jalur: 'foo', loadChildren: ()=> import("./foo/foo.module").then(m => m.FooModule), data: { pramuat: salah } }, { jalur: 'bar', loadChildren: ()=> import("./bar/bar.module").then(m => m.BarModule), data: { pramuat: benar } } ]
Kemudian kita harus menulis fungsi preloading kustom kita:
@Injeksi() kelas ekspor CustomPreloadingStrategy mengimplementasikan PreloadingStrategy { preload(route: Route, load: () => Observable<any>): Observable<any> { kembali route.data && route.data.preload ? beban() : dari(nol); } }
Dan atur sebagai PreloadingStrategy:
@NgModule({ ekspor: [RouterModule], impor: [RouterModule.forRoot(rute, { PreloadingStrategy: CustomPreloadingStrategy }] }) kelas AppRoutingModule {}
Untuk saat ini hanya rute dengan param { data: { preload: true } } yang akan dimuat sebelumnya. Sisa rute akan bertindak seperti NoPreloading diatur.
Custom preloadingStrategy adalah @Injectable(), jadi itu berarti bahwa kita dapat menyuntikkan beberapa layanan di dalam jika kita perlu dan menyesuaikan preloadingStrategy kita dengan cara lain.
Dengan alat pengembang browser, kami dapat menyelidiki peningkatan kinerja dengan waktu pemuatan awal yang sama dengan dan tanpa Strategi pemuatan awal. Kami juga dapat melihat tab jaringan untuk melihat bahwa potongan untuk rute lain sedang dimuat di latar belakang, sementara pengguna dapat melihat halaman saat ini tanpa penundaan.
fungsi trackBy
Kita dapat mengasumsikan bahwa sebagian besar aplikasi Angular menggunakan *ngFor untuk beralih pada item yang terdaftar di dalam template. Jika daftar berulang juga dapat diedit, trackBy mutlak harus dimiliki.
<ul> <tr *ngFor="biarkan produk produk; trackBy: trackByProductId"> <td>{{ product.title }}</td> </tr> </ul> trackByProductId(indeks: nomor, produk: Produk) { kembali produk.id; }
Dengan menggunakan fungsi trackBy, Angular dapat melacak elemen koleksi mana yang telah diubah (dengan pengenal yang diberikan) dan merender ulang hanya elemen tertentu ini. Saat kami menghilangkan trackBy, seluruh daftar akan dimuat ulang yang dapat menjadi operasi yang sangat intensif sumber daya di DOM.
Kompilasi sebelumnya (AOT)
Mengenai dokumentasi Angular:
“ (…) komponen dan template yang disediakan oleh Angular tidak dapat dipahami oleh browser secara langsung, aplikasi Angular memerlukan proses kompilasi sebelum dapat dijalankan di browser ”
Angular menyediakan dua jenis kompilasi:
- Just-in-Time (JIT) – mengkompilasi aplikasi di browser saat runtime
- Ahead-of-Time (AOT) – mengkompilasi aplikasi pada waktu build
Untuk penggunaan pengembangan, kompilasi JIT harus mencakup kebutuhan pengembang. Namun demikian, untuk build produksi kita pasti harus menggunakan AOT . Kita perlu memastikan flag aot di dalam file angular.json disetel ke true. Manfaat paling penting dari solusi semacam itu termasuk rendering yang lebih cepat, permintaan asinkron yang lebih sedikit, ukuran unduhan kerangka kerja yang lebih kecil, dan peningkatan keamanan.
Ringkasan
Kinerja aplikasi adalah sesuatu yang perlu Anda ingat baik selama pengembangan dan bagian pemeliharaan proyek Anda. Namun, mencari kemungkinan solusi sendiri mungkin memakan waktu dan tenaga. Memeriksa kesalahan yang sering dibuat ini dan mengingatnya selama proses pengembangan tidak hanya akan membantu Anda meningkatkan kinerja aplikasi Angular Anda dalam waktu singkat, tetapi juga membantu Anda menghindari penyimpangan di masa mendatang.
Percayakan Miquido dengan proyek Angular Anda
Hubungi kamiIngin mengembangkan aplikasi dengan Miquido?
Berpikir untuk meningkatkan bisnis Anda dengan aplikasi Angular? Hubungi kami dan pilih layanan pengembangan aplikasi Angular kami.