Bagaimana untuk mengoptimumkan Angular? Artikel berikut akan memberitahu anda tentang pengoptimuman prestasi dalam Angular saya harap ia akan membantu anda!
Artikel ini akan membincangkan tentang pengoptimuman prestasi Angular, dan terutamanya memperkenalkan pengoptimuman yang berkaitan dengan masa jalan. Sebelum bercakap tentang cara mengoptimumkan, pertama sekali kita perlu menjelaskan jenis halaman yang mempunyai masalah prestasi? Apakah ukuran prestasi yang baik? Apakah prinsip di sebalik pengoptimuman prestasi? Jika soalan ini menarik minat anda, sila baca. [Cadangan tutorial berkaitan: "tutorial sudut"]
Tukar mekanisme pengesanan
Berbeza daripada pengoptimuman penghantaran rangkaian, pengoptimuman masa jalan lebih menumpukan pada mekanisme pengendalian Angular dan cara membuat kod untuk mengelakkan masalah prestasi (amalan terbaik) dengan berkesan. Untuk memahami mekanisme pengendalian Angular, anda perlu terlebih dahulu memahami mekanisme pengesanan perubahannya (juga dikenali sebagai semakan kotor) - cara untuk memaparkan semula perubahan keadaan ke dalam paparan. Cara menggambarkan perubahan dalam status komponen pada paparan juga merupakan masalah yang perlu diselesaikan oleh ketiga-tiga rangka kerja hadapan. Penyelesaian daripada rangka kerja yang berbeza mempunyai idea yang sama tetapi juga mempunyai ciri tersendiri.
Pertama sekali, kedua-dua Vue dan React menggunakan DOM maya untuk melaksanakan kemas kini paparan, tetapi masih terdapat perbezaan dalam pelaksanaan khusus:
Untuk React:
Dengan menggunakan setState
atau forceUpdate
untuk mencetuskan kaedah render
untuk mengemas kini paparan
Apabila komponen induk mengemas kini paparan, ia juga akan menentukan sama ada re-render
komponen anak
dan menggunakan data
. untuk menukar semua sifat ini menjadi Object.defineProperty
dan getter
setter
yang sepadan, yang akan merekodkan sifat sebagai kebergantungan watcher
dipanggil, ia akan memberitahu setter
untuk mengira semula, supaya komponen yang berkaitan boleh dikemas kini watcher
dalam penyemak imbas: setTimeout
let originalSetTimeout = window.setTimeout; window.setTimeout = function(callback, delay) { return originalSetTimeout(Zone.current.wrap(callback), delay); } Zone.prototype.wrap = function(callback) { // 获取当前的 Zone let capturedZone = this; return function() { return capturedZone.runGuarded(callback, this, arguments); }; };
kaedah: Promise.then
let originalPromiseThen = Promise.prototype.then; // NOTE: 这里做了简化,实际上 then 可以接受更多参数 Promise.prototype.then = function(callback) { // 获取当前的 Zone let capturedZone = Zone.current; function wrappedCallback() { return capturedZone.run(callback, this, arguments); }; // 触发原来的回调在 capturedZone 中 return originalPromiseThen.call(this, [wrappedCallback]); };
Untuk butiran, sila rujuk konfigurasi berikut: Zone.fork()
Zone.current.fork(zoneSpec) // zoneSpec 的类型是 ZoneSpec // 只有 name 是必选项,其他可选 interface ZoneSpec { name: string; // zone 的名称,一般用于调试 Zones 时使用 properties?: { [key: string]: any; } ; // zone 可以附加的一些数据,通过 Zone.get('key') 可以获取 onFork: Function; // 当 zone 被 forked,触发该函数 onIntercept?: Function; // 对所有回调进行拦截 onInvoke?: Function; // 当回调被调用时,触发该函数 onHandleError?: Function; // 对异常进行统一处理 onScheduleTask?: Function; // 当任务进行调度时,触发该函数 onInvokeTask?: Function; // 当触发任务执行时,触发该函数 onCancelTask?: Function; // 当任务被取消时,触发该函数 onHasTask?: Function; // 通知任务队列的状态改变 }
: onInvoke
let logZone = Zone.current.fork({ name: 'logZone', onInvoke: function(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) { console.log(targetZone.name, 'enter'); parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source) console.log(targetZone.name, 'leave'); } }); logZone.run(function myApp() { console.log(Zone.current.name, 'queue promise'); Promise.resolve('OK').then((value) => {console.log(Zone.current.name, 'Promise', value) }); });
applicatoin_ref.ts, apabila dibina, ia melanggan acara panggil balik bahawa baris gilir microtask kosong dan ia memanggil kaedah ApplicationRef
( iaitu pengesanan perubahan): tick
Kedua, dalam kaedah checkStable, ia akan dinilai bahawa acara onMicrotaskEmpty
akan dicetuskan apabila baris gilir microtask dikosongkan (secara gabungan, ia bersamaan dengan mencetuskan pengesanan perubahan):
Akhir sekali, tempat yang boleh mencetuskan panggilan kaedah checkStable adalah dalam tiga fungsi cangkuk Zone.js, iaitu onInvoke
, onInvokeTask
dan onHasTask
:
Contohnya, onHasTask
- cangkuk dicetuskan apabila kehadiran atau ketiadaan ZoneTask
dikesan :
Selain itu, tugas tak segerak dalam Zone.js dibahagikan kepada tiga kategori:
Tugas Mikro: dicipta oleh Promise
dan lain-lain, native
Promise
akan dilaksanakan sebelum gelung acara semasa tamat dan Promise
yang ditampal juga akan dilaksanakan sebelum gelung acara tamat.
Tugas Makro: dicipta oleh setTimeout
dsb., dan native
daripada setTimeout
akan diproses pada masa akan datang.
Tugas Acara: dicipta oleh addEventListener
dsb. task
ini mungkin dicetuskan beberapa kali atau mungkin tidak pernah dicetuskan.
Malah, dari perspektif penyemak imbas, Tugas Acara sebenarnya boleh dianggap sebagai tugas makro Dalam erti kata lain, semua acara atau API tak segerak boleh difahami sebagai tugas makro atau mikro Salah satu daripadanya, dan perintah pelaksanaannya telah dianalisis secara terperinci dalam artikel sebelumnya Ringkasnya:
(1) Selepas utas utama dilaksanakan, mikropemproses akan diperiksa. pertama. Sama ada masih terdapat tugasan yang perlu dilaksanakan dalam baris gilir tugasan
(2) Selepas pengundian pertama selesai, ia akan menyemak sama ada masih terdapat tugasan untuk dilaksanakan dalam baris gilir tugasan makro. semak sama ada masih terdapat tugasan untuk dilaksanakan dalam senarai tugas mikro, dan kemudian Proses ini akan diulang
Prinsip Pengoptimuman Prestasi
Yang paling banyak cara intuitif untuk menilai prestasi halaman adalah untuk melihat sama ada respons halaman lancar dan responsif. Respons halaman pada asasnya ialah proses memaparkan semula perubahan keadaan halaman pada halaman Dari perspektif yang agak makro, pengesanan perubahan Angular sebenarnya hanya satu bahagian daripada keseluruhan kitaran tindak balas acara. Semua interaksi antara pengguna dan halaman dicetuskan melalui peristiwa dan keseluruhan proses respons adalah kira-kira seperti berikut:
Jika anda mempertimbangkan untuk mengoptimumkan kelajuan respons halaman, anda boleh mulakan daripada setiap peringkat:
(1) Untuk peringkat peristiwa pencetus, pencetusan peristiwa boleh dikurangkan untuk mengurangkan bilangan keseluruhan pengesanan perubahan dan pemaparan semula
(2) Untuk Acara Peringkat logik pelaksanaan pengendali, kod kompleks boleh dioptimumkan Logik untuk mengurangkan masa pelaksanaan
(3) Untuk pengikatan data pengesanan Perubahan dan peringkat pengemaskinian DOM, bilangan pengiraan data pengesanan dan templat perubahan boleh dikurangkan kepada kurangkan masa pemaparan
(4) Untuk Dalam fasa pemaparan penyemak imbas, anda mungkin perlu mempertimbangkan untuk menggunakan penyemak imbas lain atau menambah baik konfigurasi perkakasan
Kami tidak akan membincangkan terlalu banyak tentang pengoptimuman fasa kedua dan keempat di sini Digabungkan dengan Sudut yang disebutkan di atas untuk tak segerak Pengelasan tugas boleh diperjelaskan lagi untuk kaedah pengoptimuman peringkat pertama dan ketiga:
(1) Untuk permintaan penggabungan tugas Makro, cuba untuk kurangkan bilangan kutu
(2) Tandakan penggabungan tugas Mikro
(3) Kurangkan pencetus dan pendaftaran acara untuk tugasan Acara
(4) Tandakan ialah dibahagikan kepada dua fasa: semak dan render, mengurangkan pengiraan dan rendering yang tidak perlu dalam fasa semak
前面有提到,大多数情况通过观察页面是否流畅可以判断页面的是否存在性能问题。虽然这种方式简单、直观,但也相对主观,并非是通过精确的数字反映页面的性能到底如何。换言之,我们需要用一个更加有效、精确的指标来衡量什么样的页面才是具备良好性能的。而 Angular 官方也提供了相应的方案,可以通过开启 Angular 的调试工具,来实现对变更检测循环(完成的 tick
)的时长监控。
首先,需要使用 Angular 提供的 enableDebugTools
方法,如下:
之后只需要在浏览器的控制台中输入 ng.profiler.timeChangeDetection()
,即可看到当前页面的平均变更检测时间:
从上面可以看出,执行了 692 次变更检测循环(完整的事件响应周期)的平均时间为 0.72 毫秒。如果多运行几次,你会发现每次运行的总次数是不一样、随机的。
官方提供了这样一个判断标准:理想情况下,分析器打印出的时长(单次变更检测循环的时间)应该远低于单个动画帧的时间(16 毫秒)。一般这个时长保持在 3 毫秒下,则说明当前页面的变更检测循环的性能是比较好的。如果超过了这个时长,则就可以结合 Angular 的变更检测机制分析一下是否存在重复的模板计算和变更检测。
性能优化方案
在理解 Angular 优化原理的基础上,我们就可以更有针对性地去进行相应的性能优化:
(1)针对异步任务 ——减少变更检测的次数
(2)针对 Event Task —— 减少变更检测的次数
如上图,防抖动处理只是保证了代码逻辑不会重复运行,但是 valueChanges 的事件却随着 value 的改变而触发(改变几次,就触发几次),而只要有事件触发就会相应触发变更检测。
(3)使用 Pipe ——减少变更检测中的计算次数
将 pipe 定义为 pure pipe(@Pipe
默认是 pure pipe,因此也可以不用显示地设置 pure: true
)
import { Piep, PipeTransform } from '@angular/core'; @Pipe({ name: 'gender', pure, }) export class GenderPiep implements PipeTransform { transform(value: string): string { if (value === 'M') return '男'; if (value === 'W') return '女'; return ''; } }
关于 Pure/ImPure Pipe:
Pure Pipe: 如果传入 Pipe 的参数没有改变,则会直接返回之前一次的计算结果
ImPure Pipe: 每一次变更检测都会重新运行 Pipe 内部的逻辑并返回结果。(简单来说, ImPure Pipe 就等价于普通的 formattedFunction,如果一个页面触发了多次的变更检测,那么 ImPure Pipe 的逻辑就会执行多次)
(4)针对组件 ——减少不必要的变更检测
@Component({ ... changeDetection: ChangeDetectionStrategy.OnPush, }) export class XXXComponent { .... }
在 Angular 中 显示的设置 @Component
的 changeDetection
为 ChangeDetectionStrategy.OnPush
即开启 onPush 模式(默认不开启),用 OnPush 可以跳过某个组件或者某个父组件以及它下面所有子组件的变化检测,如下所示:
(5)针对模板 ——减少不必要的计算和渲染
(6) Cadangan pengoptimuman pengekodan lain
Ringkasan.
(1) Terangkan secara ringkas cara Angular menggunakan Zone .js untuk melaksanakan pengesanan perubahan
(2) Atas dasar memahami pengesanan perubahan Angular, kami menjelaskan lagi prinsip pengoptimuman prestasi sudut dan kriteria untuk menilai sama ada halaman mempunyai prestasi yang baik
(3) Sediakan beberapa penyelesaian pengoptimuman prestasi masa jalan yang disasarkan
Untuk lebih banyak pengetahuan berkaitan pengaturcaraan, sila lawati: Pengenalan kepada Pengaturcaraan! !
Atas ialah kandungan terperinci Bagaimanakah Angular mengoptimumkan? Analisis ringkas penyelesaian pengoptimuman prestasi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!