Home >Web Front-end >JS Tutorial >How does Angular optimize? A brief analysis of performance optimization solutions
How to optimize Angular? The following article will let you know about the performance optimization in Angular, I hope it will be helpful to you!
This article will talk about the performance optimization of Angular, and mainly introduce the optimization related to runtime. Before talking about how to optimize, first we need to clarify what kind of pages have performance problems? What are the measures of good performance? What is the principle behind performance optimization? If these questions interest you, then please read on. [Related tutorial recommendations: "angular tutorial"]
##Change detection mechanism
Different from network transmission optimization, runtime optimization focuses more on Angular's operating mechanism and how to code to effectively avoid performance problems (best practices). To understand the operating mechanism of Angular, you first need to understand its change detection mechanism (also known as dirty checking) - how to re-render state changes into the view. How to reflect changes in component status to the view is also a problem that all three front-end frameworks need to solve. Solutions from different frameworks have similar ideas but also have their own characteristics. First of all, Vue and React both use virtual DOM to implement view updates, but there are still differences in the specific implementation: For React:setState or
forceUpdate to trigger the
render method to update the view
re-render subcomponent
data All properties of the object, and use
Object.defineProperty to convert all these properties into wrapped
getter and
setter
watcher instance object, which will record the properties as dependencies during the component rendering process
setter is called, it will notify
watcher to recalculate, so that its associated components can be updated
setTimeout in the browser:
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); }; };or
Promise.then method:
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]); };Zone.js is loading When , all asynchronous interfaces are encapsulated. Therefore, all asynchronous methods executed in Zone.js will be treated as a Task and uniformly supervised by it, and corresponding hook functions (hooks) are provided to perform some additional operations before and after the execution of the asynchronous task or at a certain stage. Therefore, Zone.js can easily implement functions such as logging, monitoring performance, and controlling the timing of asynchronous callback execution. These hook functions (hooks) can be set through the
Zone.fork() method. For details, please refer to the following configuration:
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; // 通知任务队列的状态改变 }Give one
A simple example of 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) }); });Final execution result: After understanding the principles of Zone.js, by reading the source code of Angular, you can Know that Zone.js is used in Angular to implement change detection whenever an asynchronous method or event is called. It's roughly as follows: First, in the
applicatoin_ref.ts file, when ApplicationRef is built, it subscribes to the callback event that the microtask queue is empty, and it calls
tick Method (i.e. change detection):
Secondly, in the checkStable method, it will be judged that the onMicrotaskEmpty
event will be triggered when the microtask queue is emptied (in combination, it is equivalent to triggering change detection):
Finally, the places that can trigger the call of the checkStable method are in the three hook functions of Zone.js, which are onInvoke
, onInvokeTask
and onHasTask
:
onHasTask—— Hooks triggered when presence or absence of
ZoneTask is detected:
Micro Task: Created by Promise etc. The
Promise of
native is required before the end of the current event loop executed, and the patched
Promise will also be executed before the end of the event loop.
Macro Task: Created by setTimeout etc.,
native’s
setTimeout will be Time is processed.
Event Task: Created by addEventListener etc. These
task may be triggered multiple times or may never be triggered.
Event Task can actually be regarded as a macro task. In other words, all events or asynchronous APIs can be understood as macro tasks or micro tasks. One of them, and their execution order is analyzed in detail in the previous article. Simply put:
(1) After the main thread is executed, the microprocessor will be checked first. Whether there are still tasks to be executed in the task queue(2) After the first polling is completed, it will check whether there are still tasks to execute in the macro task queue. After the execution, check whether there are still tasks to execute in the micro task list, and then This process will be repeatedPerformance Optimization Principle
The most intuitive way to judge the performance of a page is to see whether the page response is smooth and responsive. Be quick. Page response is essentially the process of re-rendering page state changes to the page. From a relatively macro perspective, Angular's change detection is actually only one part of the entire event response cycle. All interactions between users and the page are triggered through events, and the entire response process is roughly as follows: If you consider optimizing the page response speed, you can start from each stage:(1) For the trigger event stage, the triggering of events can be reduced to reduce the overall number of change detections and re-rendering(2) For the Event Handler execution logic stage, complex code can be optimized Logic to reduce execution time (3) For the Change Detection detection data binding and update DOM stage, you can reduce the number of calculations of change detection and template data to reduce rendering time (4) For In the browser rendering stage, you may need to consider using a different browser or improving the hardware configuration.I will not discuss too much about the optimization of the second and fourth stages here. Combined with the Angular mentioned above for asynchronous The classification of tasks can be further clarified for the optimization methods of the first and third stages: (1) For Macro task merging requests, try to reduce the number of ticks (2) For Micro task merging tick(3) Reduce event triggering and registration events for Event task(4) Tick is divided into two phases: check and render, reducing calculations and unnecessary rendering in the check phase前面有提到,大多数情况通过观察页面是否流畅可以判断页面的是否存在性能问题。虽然这种方式简单、直观,但也相对主观,并非是通过精确的数字反映页面的性能到底如何。换言之,我们需要用一个更加有效、精确的指标来衡量什么样的页面才是具备良好性能的。而 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) Other coding optimization suggestions
##Summary
(1) Briefly explain how Angular uses Zone .js to implement change detection(2) On the basis of understanding Angular’s change detection, further clarify the principles of Angular performance optimization and the criteria for judging whether a page has good performance(3) Provide some targeted runtime performance optimization solutionsFor more programming-related knowledge, please visit:Introduction to Programming! !
The above is the detailed content of How does Angular optimize? A brief analysis of performance optimization solutions. For more information, please follow other related articles on the PHP Chinese website!