Mengenai deskripsi indikator kinerja front-end, industri memiliki pendapatnya sendiri. Singkatnya, ini terkait dengan kinerja layar pertama dan kelancaran halaman perspektif kelancaran halaman. [Rekomendasi tutorial terkait: "Tutorial Sudut"]
Apa yang dimaksud dengan kefasihan halaman?
Kefasihan halaman ditentukan oleh frame rate FPS (Frames Per Second - frame yang dikirimkan per detik). Umumnya, kecepatan refresh layar browser mainstream adalah 60Hz (disegarkan 60 kali per detik), dan kecepatan frame optimal adalah 60 FPS kecepatan bingkai, semakin halus halamannya. 60Hz berarti tampilan layar akan disegarkan setiap 16,6 md, yang berarti setiap rendering halaman harus diselesaikan dalam waktu 16,6 md, jika tidak, halaman akan kehilangan bingkai dan terhenti.根因在于:浏览器中的JavaScript 执行和页面渲染会相互阻塞
.
Di devtools Chrome, kita dapat menjalankan Cmd+Shift+P dan masuk ke show fps untuk membuka panel fps dengan cepat, seperti yang ditunjukkan pada gambar berikut:
Dengan mengamati panel FPS, kita dapat dengan mudah memantau kelancaran halaman saat ini.
1 Faktor-faktor yang mempengaruhi kinerja halaman
Lancar atau tidaknya interaksi halaman bergantung pada lancar tidaknya respons halaman, dan respons halaman pada dasarnya adalah proses merender ulang perubahan status halaman ke halaman.
Proses respons halaman kira-kira sebagai berikut:
Secara umum, logika pemrosesan peristiwa Event Handler tidak memakan terlalu banyak waktu, sehingga faktor yang memengaruhi kinerja Angular terutama terletak pada异步事件触发
dan变更检测
. Secara umum, logika pemrosesan peristiwa Event Handler tidak memakan terlalu banyak waktu, sehingga faktor-faktor yang memengaruhi kinerja Angular terutama terletak pada pemicuan peristiwa asinkron dan deteksi perubahan.
Untuk Angular, proses rendering halaman adalah proses deteksi perubahan. Dapat dipahami bahwa deteksi perubahan Angular harus diselesaikan dalam waktu 16,6 ms untuk menghindari kehilangan dan kelambatan bingkai halaman.
Performa respons halaman dapat dioptimalkan dari tiga aspek berikut.
(1) Untuk tahap kejadian pemicu, pemicuan kejadian asinkron dapat dikurangi untuk mengurangi jumlah keseluruhan deteksi perubahan dan rendering ulang;
(2) Untuk tahap logika eksekusi Event Handler, waktu eksekusi dapat dikurangi dengan mengoptimalkan kompleks logika kode;
(3) Untuk tahap pengikatan data deteksi Deteksi Perubahan dan pembaruan DOM, jumlah penghitungan deteksi perubahan dan data templat dapat dikurangi untuk mengurangi waktu rendering;
untuk (2) Pengendali Peristiwa, masalah spesifik perlu dianalisis detail dan tidak akan dibahas. Terutama fokus pada (1) (3) ) Optimasi
2 Rencana Optimasi
2.1 Mengurangi pemicu peristiwa asinkron
Angular Dalam mode deteksi perubahan default, peristiwa asinkron akan memicu deteksi perubahan global akan sangat meningkatkan kinerja Angular.
Peristiwa asinkron mencakup peristiwa Tugas Makro dan peristiwa Tugas Mikro
Optimalisasi kejadian asinkron terutama untuk kejadian mendengarkan dokumen. Misalnya, mendengarkan klik, gerakan mouse, gerakan mouse... dan kejadian lainnya pada dokumen.
Skenario peristiwa mendengarkan:
Renderer2.listen(document,…)
fromEvent(document,…)
document.addEventListener(…)
Peristiwa mendengarkan DOM harus dihapus jika tidak perlu dipicu.
Misalnya:
skenario penggunaan perintah kotak prompt [pop]: memfilter kolom tabel, mengklik di tempat lain selain ikon, atau menggulir halaman, menyembunyikan kotak pop-up filter kolom.
Metode sebelumnya adalah memantau langsung peristiwa klik dan menggulir acara dokumen dalam perintah pop. Dengan cara ini: Satu-satunya kelemahan adalah kotak prompt tidak ditampilkan, tetapi masih ada peristiwa pemantauan, yang sangat tidak masuk akal.
Solusi yang masuk akal: Hanya dengarkan acara klik dan gulir saat kotak prompt ditampilkan, dan hapus acara mendengarkan saat disembunyikan.
Untuk kejadian mendengarkan DOM yang sering dipicu, Anda dapat menggunakan operator rjx untuk mengoptimalkan kejadian. Lihat Operator Rjx untuk detailnya. Kelereng RxJS.
2.2 Deteksi Perubahan
Apa itu deteksi perubahan?
Untuk memahami deteksi perubahan, kita dapat mencari jawaban dari tujuan deteksi perubahan. Tujuan deteksi perubahan sudut adalah untuk menjaga model (kode TypeScript) dan templat (HTML) tetap sinkron. Oleh karena itu, deteksi perubahan dapat dipahami sebagai: mendeteksi perubahan model dan memperbarui template ( DOM ) secara bersamaan .
Bagaimana proses deteksi perubahan?
Dengan melakukan deteksi perubahan secara top-down pada pohon komponen, yaitu deteksi perubahan dilakukan pada komponen induk terlebih dahulu, baru kemudian pada komponen turunan. Pertama periksa perubahan data komponen induk, lalu perbarui templat komponen induk. Saat memperbarui templat, ketika menemukan komponen anak, itu akan memperbarui nilai yang terikat ke komponen anak, dan kemudian memasukkan komponen anak untuk melihat apakah komponen tersebut. indeks nilai input @Input telah berubah. Jika berubah, tandai subkomponen sebagai kotor, yang memerlukan deteksi perubahan selanjutnya. Setelah menandai subkomponen, lanjutkan memperbarui template di belakang subkomponen di komponen induk telah diperbarui, lakukan perubahan pada subkomponen deteksi.
2.2.1 Prinsip deteksi perubahan sudut
Dalam mode default deteksi perubahan default, prinsip kejadian asinkron yang memicu deteksi perubahan Angular adalah bahwa sudut memanggil metode tick() dari ApplicationRef saat memproses kejadian asinkron menggunakan Zone.js untuk dieksekusi dari komponen root ke sub-komponen. Selama proses inisialisasi aplikasi Angular, sebuah zona (NgZone) dibuat, dan kemudian semua logika dijalankan di objek _inner dari objek tersebut.
Zone.js mengimplementasikan kelas-kelas berikut:
Prinsip proses deteksi kira-kira sebagai berikut:
operasi pengguna memicu kejadian asinkron (misalnya: kejadian DOM, permintaan antarmuka...)
=> Kelas ZoneTask menangani kejadian. Metode zona runTask() dipanggil dalam fungsi invokeTask(). Dalam metode runTask, zona meneruskan atribut instance _zoneDelegate dan memanggil kait ZoneSpec
=> tiga kait ZoneSpec (onInvokeTask, onInvoke, onHasTask) meneruskan checkStable () function Trigger zone.onMicrotaskEmpty.emit(null) notifikasi
=> Komponen root memanggil tick() setelah mendengarkan onMicrotaskEmpty, dan memanggil detectChanges()
dalam metode tick untuk memulai deteksi dari komponen root
=> ··· refreshView()
memanggil executeTemplate()
, dalam metode executeTemplate
Panggil templateFn()
untuk memperbarui nilai yang terikat pada templat dan subkomponen (这时候会去检测子组件的
输入引用是否改变,如果有改变,会将子组件标记为
Kotor ,也就是该子组件需要变更检测
)
Diagram alir prinsip deteksi perubahan terperinci:
Sederhanakan prosesnya:
picu kejadian asynchronous
=> ZoneTask menangani kejadian tersebut
=> ZoneDelegate memanggil hook ZoneSpec untuk memicu notifikasi onMicrotaskEmpty
=> komponen root menerima notifikasi onMicrotaskEmpty, mengeksekusi tick(), dan mulai mendeteksi dan memperbarui dom
Seperti yang bisa dilihat dari kode di atas,当微任务为空的时候才会触发变更检测
.
Diagram alir prinsip deteksi perubahan sederhana:
Analisis kode sumber sudut blog referensi Zone.js.
2.2.2 Solusi optimasi deteksi perubahan
1) Gunakan
prinsip mode OnPush: Mengurangi waktu yang memakan waktu untuk satu deteksi perubahan.
Perbedaan antara mode OnPush dan mode Default adalah peristiwa mendengarkan DOM, peristiwa pengatur waktu, dan janji tidak akan memicu deteksi perubahan. Status komponen dalam mode Default selalu CheckAlways, artinya komponen harus diuji setiap siklus deteksi.
Dalam mode OnPush: Situasi berikut akan memicu perubahan deteksi
S1 dan referensi @Input komponen untuk berubah.
S2. Peristiwa yang terikat pada DOM komponen, termasuk peristiwa yang terikat pada DOM subkomponennya, seperti klik, kirim, dan arahkan mouse. @HostListener()
Catatan:
Peristiwa DOM yang dipantau melalui renderer2.listen() tidak akan memicu deteksi perubahan.
Metode mendengarkan asli dom.addEventListener() tidak akan memicu deteksi perubahan.
Peristiwa langganan S3 dan Observable juga disetel pada saat yang sama.
S4. Gunakan metode berikut untuk memicu deteksi perubahan secara manual:
ChangeDetectorRef.detectChanges(): Memicu deteksi perubahan untuk komponen saat ini dan subkomponen non-OnPush.
ChangeDetectorRef.markForCheck(): Menandai tampilan saat ini dan semua leluhurnya sebagai kotor, dan deteksi akan dipicu pada siklus deteksi berikutnya.
ApplicationRef.tick(): Tidak akan memicu deteksi perubahan
2)
Prinsip penggunaan NgZone.runOutsideAngular(): Kurangi jumlah deteksi perubahan
dan tulis pemantauan peristiwa dom global dalam panggilan balik metode NgZone.runOutsideAngular() tidak memicu sudut. Jika komponen saat ini belum diperbarui, Anda dapat menjalankan hook deteksiChanges() dari ChangeDetectorRef dalam fungsi panggilan balik untuk memicu deteksi perubahan komponen saat ini secara manual.
Contoh: komponen ikon dinamis app-icon-react
2.2.3 Metode debug
1: Anda dapat menggunakan plug-in Angular DevTools di konsol browser untuk melihat peristiwa DOM tertentu dan status deteksi sudut:
Metode 2: Anda dapat langsung memasukkan: ng.profiler.timeChangeDetection() di konsol untuk melihat waktu deteksi. Referensi blog Profiling Deteksi Perubahan Sudut
2.3 Optimasi Template (HTML)
2.3.1 Mengurangi rendering DOM: ngFor plus trackDengan
Menggunakan atribut trackBy dari *ngFor, Angular hanya mengubah dan merender ulang entri yang diubah tanpa harus memuat ulang seluruh daftar entri.
Misalnya: skenario pengurutan tabel. Jika trackBy ditambahkan ke ngFor, hanya elemen dom baris yang akan dipindahkan saat tabel dirender. Jika trackBy tidak ditambahkan, elemen dom tabel yang ada akan dihapus terlebih dahulu, baru kemudian elemen dom baris akan ditambahkan. Tentunya performa hanya memindahkan elemen dom saja akan jauh lebih baik.
2.3.2 Jangan gunakan fungsi dalam ekspresi templat.
Jangan gunakan pemanggilan fungsi dalam ekspresi templat Angular. Anda bisa menggunakan pipa, atau Anda bisa menggunakan variabel setelah penghitungan manual. Saat fungsi digunakan dalam templat, terlepas dari apakah nilainya berubah atau tidak, fungsi tersebut akan dijalankan setiap kali deteksi perubahan dilakukan, yang akan memengaruhi performa.
Skenario penggunaan fungsi dalam templat:
2.3.3 Mengurangi penggunaan ngFor.
Menggunakan ngFor akan mempengaruhi kinerja ketika jumlah data besar.
Contoh:
Menggunakan ngFor:
Tidak menggunakan ngFor: kinerja meningkat sekitar 10 kali lipat