Heim  >  Artikel  >  Web-Frontend  >  Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

青灯夜游
青灯夜游nach vorne
2021-10-11 09:55:472604Durchsuche

Wie optimiert man Angular? Der folgende Artikel informiert Sie über die Leistungsoptimierung in Angular, ich hoffe, er wird Ihnen hilfreich sein!

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

In diesem Artikel geht es um die Leistungsoptimierung von Angular und hauptsächlich um die Optimierung im Zusammenhang mit der Laufzeit. Bevor wir über die Optimierung sprechen, müssen wir zunächst klären, bei welchen Seiten Leistungsprobleme auftreten. Was sind die Maßstäbe für gute Leistung? Was ist das Prinzip der Leistungsoptimierung? Wenn Sie diese Fragen interessieren, dann lesen Sie bitte weiter. [Verwandte Tutorial-Empfehlungen: „Angular Tutorial“]

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

Änderungserkennungsmechanismus

Anders als die Netzwerkübertragungsoptimierung konzentriert sich die Laufzeitoptimierung mehr auf den Betriebsmechanismus von Angular und darauf, wie man codiert, um Leistungsfragen effektiv zu vermeiden ( Best Practices). Um den Funktionsmechanismus von Angular zu verstehen, müssen Sie zunächst den Änderungserkennungsmechanismus (auch als Dirty Checking bezeichnet) verstehen – wie Zustandsänderungen in der Ansicht erneut gerendert werden. Die Art und Weise, wie Änderungen im Komponentenstatus in der Ansicht widergespiegelt werden, ist ebenfalls ein Problem, das alle drei Front-End-Frameworks lösen müssen. Lösungen aus unterschiedlichen Frameworks haben ähnliche Ideen, weisen aber auch eigene Merkmale auf.

Zuallererst verwenden Vue und React beide virtuelles DOM, um Ansichtsaktualisierungen zu implementieren, aber es gibt immer noch Unterschiede in der spezifischen Implementierung:

Für React:

  • Durch die Verwendung von setState oder forceUpdate , um die Methode render auszulösen, um die Ansicht zu aktualisierensetState forceUpdate 来触发 render 方法更新视图

  • 父组件更新视图时,也会判断是否需要 re-render 子组件

对于 Vue:

  • Vue 会遍历 data 对象的所有属性,并使用 Object.defineProperty 把这些属性全部转为经过包装的 gettersetter

  • 每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖

  • 当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而使它关联的组件得以更新

而 Angular 则是通过引入  Zone.js 对异步操作的 API 打补丁,监听其触发来进行变更检测。关于 Zone.js 的原理在之前的一篇文章中有详细的介绍。简单来说,Zone.js 通过 Monkey patch (猴补丁)的方式,暴力地将浏览器或 Node 中的所有异步 API 进行了封装替换。

比如浏览器中的 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);
  };
};

或者 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]);
};

Zone.js 在加载时,对所有异步接口进行了封装。因此所有在 Zone.js 中执行的异步方法都会被当做为一个 Task 被其统一监管,并且提供了相应的钩子函数(hooks),用来在异步任务执行前后或某个阶段做一些额外的操作。因此通过 Zone.js 可以很方便地实现记录日志、监控性能、控制异步回调执行的时机等功能。

而这些钩子函数(hooks),可以通过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)
  });
});

最终执行结果:

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

理解了 Zone.js 的原理之后,通过走读 Angular 的源码,可以知道 Zone.js 在 Angular 被用来实现只要有异步方法或事件的调用,就会触发变更检测。大体如下:

首先,在 applicatoin_ref.ts 文件中,当 ApplicationRef 构建时就订阅了微任务队列为空的回调事件,其调用了 tick

Wenn die übergeordnete Komponente die Ansicht aktualisiert, bestimmt sie auch, ob sie neu render die untergeordnete Komponente2-Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung li>

Für Vue: 🎜🎜🎜🎜Vue durchläuft alle Eigenschaften des data-Objekts und verwendet Object.defineProperty, um alle diese Eigenschaften in Pakete umzuwandeln. Jede Komponenteninstanz von <code>getter und setter🎜🎜🎜 verfügt über eine entsprechende watcher-Instanz Objekt, das gerendert wird, wenn die Komponente Während des Prozesses werden die Eigenschaften als Abhängigkeiten aufgezeichnet🎜🎜🎜Wenn der Setter der Abhängigkeit aufgerufen wird, wird der Watcher wird zur Neuberechnung benachrichtigt, damit die zugehörigen Komponenten aktualisiert werden können 🎜🎜 Angular hat Zone.js eingeführt, um die API für asynchrone Vorgänge zu patchen und auf ihre Trigger zu warten, um eine Änderungserkennung durchzuführen. Über das Prinzip von Zone.js im vorherigen Eine ausführliche Einführung gibt es in einem Artikel 🎜. Um es einfach auszudrücken: Zone.js kapselt und ersetzt alle asynchronen APIs im Browser oder Node durch Monkey-Patching gewaltsam. 🎜🎜Zum Beispiel setTimeout im Browser: 🎜
import { Piep, PipeTransform } from &#39;@angular/core&#39;;

@Pipe({
  name: &#39;gender&#39;,
  pure,
})
export class GenderPiep implements PipeTransform {
  transform(value: string): string {
    if (value === &#39;M&#39;) return &#39;男&#39;;
    if (value === &#39;W&#39;) return &#39;女&#39;;
    return &#39;&#39;;
  }
}
🎜 oder Promise.then Methode: 🎜
@Component({
  ...
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XXXComponent {
	....
}
🎜Zone.js kapselt beim Laden alle asynchronen Schnittstellen. Daher werden alle in Zone.js ausgeführten asynchronen Methoden als Aufgabe behandelt und von dieser einheitlich überwacht. Außerdem werden entsprechende Hook-Funktionen (Hooks) bereitgestellt, um einige zusätzliche Vorgänge vor und nach der Ausführung der asynchronen Aufgabe oder in einem bestimmten Stadium auszuführen . Daher kann Zone.js problemlos Funktionen wie Protokollierung, Leistungsüberwachung und Steuerung des Timings der asynchronen Rückrufausführung implementieren. 🎜🎜Diese Hook-Funktionen (Hooks) können über die Methode Zone.fork() festgelegt werden. Einzelheiten finden Sie in der folgenden Konfiguration: 🎜rrreee🎜Geben Sie ein einfaches Beispiel für onInvoke Beispiel: 🎜rrreee🎜Endgültiges Ausführungsergebnis: 🎜🎜<img src="https://img.php.cn/upload/image/862/659/987/163391656587125Wie%20optimiert%20Angular?%20Eine%20kurze%20Analyse%20von%20L%C3%B6sungen%20zur%20Leistungsoptimierung" title="163391656587125Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung" alt=" Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung">🎜🎜Nachdem wir die Prinzipien von Zone.js verstanden haben, können wir durch Lesen des Quellcodes von Angular erkennen, dass Zone.js in Angular verwendet wird, um die Änderungserkennung zu implementieren, solange ein Aufruf vorliegt zu einer asynchronen Methode oder einem asynchronen Ereignis. Es ist ungefähr wie folgt: 🎜🎜Zunächst einmal in _ref .ts%23L522" target="_blank" rel="nofollow noopener noreferrer" title="https://github.com/angular/angular/blob/fc6f48185c3a546b130296d3d5dce86fdf334115/packages /core/src/application_ref.ts#L522" ref= "nofollow noopener noreferrer">applicatoin_ref.ts🎜 Datei, als <code>ApplicationRef erstellt wurde, abonnierte sie das Rückrufereignis, dass die Mikrotask-Warteschlange leer war, und rief die tick-Methode (d. h. Änderungserkennung) auf: 🎜🎜🎜🎜

Zweitens wird in der Methode checkStable bestimmt, dass das Ereignis onMicrotaskEmpty ausgelöst wird, wenn die Mikrotask-Warteschlange geleert wird (in Kombination entspricht dies dem Auslösen der Änderungserkennung): onMicrotaskEmpty 事件(结合上来看,等价于会触发变更检测):

2-Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

最后,能够触发  checkStable 方法的调用的地方分别在 Zone.js 的三个钩子函数中,分别是 onInvokeonInvokeTaskonHasTask

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

比如 onHasTask —— 检测到有或无 ZoneTask 时触发的钩子:

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

另外 Zone.js 中对于异步任务总共分为三类:

Micro Task(微任务):Promise 等创建, nativePromise 是在当前事件循环结束前就要执行的,而打过补丁的 Promise 也会在事件循环结束前执行。

Macro Task (宏任务):setTimeout 等创建,nativesetTimeout 会在将来某个时间被处理。

Event Task :addEventListener 等创建,这些 task


Schließlich können Sie checkStable-Methode wird in drei Hook-Funktionen von Zone.js aufgerufen, nämlich onInvoke, onInvokeTask und onHasTask
Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

Zum Beispiel onHasTask – ein Hook, der ausgelöst wird, wenn das Vorhandensein oder Fehlen von ZoneTask erkannt wird:

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

Außerdem sind asynchrone Aufgaben in Zone.js unterteilt in drei Kategorien eingeteilt: Mikroaufgabe: Erstellt von Promise usw. Das Promise von native wird vor dem Ende des aktuellen Ereignisses ausgeführt Schleife, und das gepatchte Promise wird auch vor dem Ende der Ereignisschleife ausgeführt.

Makroaufgabe:

Erstellt von setTimeout usw. natives setTimeout wird irgendwann in der Zukunft verarbeitet. Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

Ereignisaufgabe:

Erstellt von addEventListener usw. Diese Aufgabe kann mehrmals oder nie ausgelöst werden.

Tatsächlich kann

Ereignisaufgabe

aus Sicht des Browsers tatsächlich als Makroaufgabe betrachtet werden. Mit anderen Worten, alle Ereignisse oder asynchronen APIs können als eine von Makroaufgaben oder Mikroaufgaben und deren Ausführung verstanden werden Die Sequenz wird in einem vorherigen

Artikel

im Detail analysiert. Einfach ausgedrückt:

(1) Nachdem der Hauptthread ausgeführt wurde, prüft er zunächst, ob sich Aufgaben in der Mikrotask-Warteschlange befinden, die ausgeführt werden müssen

(2) Beim ersten Mal wird geprüft, ob in der Makroaufgabenwarteschlange noch auszuführende Aufgaben vorhanden sind. Nach der Ausführung wird geprüft, ob in der Mikroaufgabenliste noch auszuführende Aufgaben vorhanden sind Danach

Prinzip der Leistungsoptimierung

Die Qualität der Seitenleistung. Die intuitivste Beurteilung besteht darin, festzustellen, ob die Seitenreaktion reibungslos und schnell erfolgt. Bei der Seitenreaktion handelt es sich im Wesentlichen um den Prozess des erneuten Renderns von Seitenstatusänderungen auf der Seite. Aus relativ makroökonomischer Sicht ist die Änderungserkennung von Angular eigentlich nur ein Teil des gesamten Ereignisreaktionszyklus. Alle Interaktionen zwischen Benutzern und der Seite werden durch Ereignisse ausgelöst und der gesamte Reaktionsprozess läuft ungefähr wie folgt ab: 🎜🎜🎜🎜🎜Wenn Sie die Geschwindigkeit der Seitenreaktion optimieren möchten, können Sie mit jeder Phase beginnen: 🎜🎜 (1) Für In der Phase „Trigger-Ereignis“ können Sie das Auslösen von Ereignissen reduzieren, um die Gesamtzahl der Änderungserkennungen und erneuten Renderings zu reduzieren (3) Erkennen Sie für die Änderungserkennung die Datenbindung und in der DOM-Aktualisierungsphase kann die Anzahl der Änderungserkennungs- und Vorlagendatenberechnungen reduziert werden, um die Renderzeit zu verkürzen🎜🎜(4) Für die Browser-Rendering-Phase müssen Sie möglicherweise die Verwendung in Betracht ziehen Ein anderer Browser oder eine Verbesserung der Hardwarekonfiguration. 🎜🎜Zum zweiten wird hier nicht allzu viel auf die relevante Optimierung der vier Phasen eingegangen. In Kombination mit der oben erwähnten Klassifizierung asynchroner Aufgaben durch Angular werden die Optimierungsmethoden für die erste und dritte Phase verwendet kann weiter geklärt werden: 🎜🎜 (1) Versuchen Sie bei Zusammenführungsanforderungen für Makroaufgaben, die Anzahl der Ticks zu minimieren 🎜🎜 (2) Ticks für Mikroaufgaben zusammenführen 🎜🎜 (3) Reduzieren Sie Ereignisauslöse- und Registrierungsereignisse für Ereignisaufgaben 🎜🎜 ( 4) Tick ist in zwei Phasen unterteilt: Überprüfen und Rendern, wodurch die Berechnung und Nichteinhaltung der erforderlichen Rendering-Phase der Prüfphase reduziert wird 🎜

前面有提到,大多数情况通过观察页面是否流畅可以判断页面的是否存在性能问题。虽然这种方式简单、直观,但也相对主观,并非是通过精确的数字反映页面的性能到底如何。换言之,我们需要用一个更加有效、精确的指标来衡量什么样的页面才是具备良好性能的。而 Angular 官方也提供了相应的方案,可以通过开启 Angular 的调试工具,来实现对变更检测循环(完成的 tick)的时长监控。

首先,需要使用 Angular 提供的 enableDebugTools 方法,如下:

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

之后只需要在浏览器的控制台中输入 ng.profiler.timeChangeDetection() ,即可看到当前页面的平均变更检测时间:

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

从上面可以看出,执行了 692 次变更检测循环(完整的事件响应周期)的平均时间为 0.72 毫秒。如果多运行几次,你会发现每次运行的总次数是不一样、随机的。

官方提供了这样一个判断标准:理想情况下,分析器打印出的时长(单次变更检测循环的时间)应该远低于单个动画帧的时间(16 毫秒)。一般这个时长保持在 3 毫秒下,则说明当前页面的变更检测循环的性能是比较好的。如果超过了这个时长,则就可以结合 Angular 的变更检测机制分析一下是否存在重复的模板计算和变更检测。

性能优化方案

在理解 Angular 优化原理的基础上,我们就可以更有针对性地去进行相应的性能优化:

(1)针对异步任务 ——减少变更检测的次数

  • 使用 NgZone 的 runOutsideAngular 方法执行异步接口
  • 手动触发 Angular 的变更检测

(2)针对 Event Task —— 减少变更检测的次数

  • 将 input 之类的事件换成触发频率更低的事件
  • 对 input valueChanges 事件做的防抖动处理,并不能减少变更检测的次数

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

如上图,防抖动处理只是保证了代码逻辑不会重复运行,但是 valueChanges 的事件却随着 value 的改变而触发(改变几次,就触发几次),而只要有事件触发就会相应触发变更检测。

(3)使用 Pipe ——减少变更检测中的计算次数

  • 将 pipe 定义为 pure pipe(@Pipe 默认是 pure pipe,因此也可以不用显示地设置 pure: true

    import { Piep, PipeTransform } from &#39;@angular/core&#39;;
    
    @Pipe({
      name: &#39;gender&#39;,
      pure,
    })
    export class GenderPiep implements PipeTransform {
      transform(value: string): string {
        if (value === &#39;M&#39;) return &#39;男&#39;;
        if (value === &#39;W&#39;) return &#39;女&#39;;
        return &#39;&#39;;
      }
    }

关于  Pure/ImPure Pipe:

  • Pure Pipe: 如果传入 Pipe 的参数没有改变,则会直接返回之前一次的计算结果

  • ImPure Pipe: 每一次变更检测都会重新运行 Pipe 内部的逻辑并返回结果。(简单来说, ImPure Pipe 就等价于普通的 formattedFunction,如果一个页面触发了多次的变更检测,那么 ImPure Pipe 的逻辑就会执行多次)

(4)针对组件 ——减少不必要的变更检测

  • 组件使用 onPush 模式
    • 只有输入属性发生变化时,该组件才会检测
    • 只有该组件或者其子组件中的 DOM 事件触发时,才会触发检测
    • 非 DOM 事件的其他异步事件,只能手动触发检测
    • 声明了 onPush 的子组件,如果输入属性未变化,就不会去做计算和更新
@Component({
  ...
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XXXComponent {
	....
}

在 Angular 中 显示的设置 @ComponentchangeDetection 为  ChangeDetectionStrategy.OnPush 即开启 onPush 模式(默认不开启),用 OnPush 可以跳过某个组件或者某个父组件以及它下面所有子组件的变化检测,如下所示:

Wie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung

(5)针对模板 ——减少不必要的计算和渲染

  • Verwenden Sie trackBy für die Schleifenwiedergabe der Liste.
  • Versuchen Sie, zwischengespeicherte Werte zu verwenden, und vermeiden Sie die Verwendung von Methodenaufrufen und Abrufen von Attributaufrufen.
  • Wenn es in der Vorlage eine Stelle gibt, an der wirklich eine Funktion aufgerufen werden muss, und es mehrere davon gibt Bei Aufrufen können Sie den Vorlagencache verwenden.
  • ngIf-Steuerung Die Anzeige von Komponenten sollte dort gesteuert werden, wo die Komponente aufgerufen wird verursacht viel Zeitaufwand (Aufzeichnen einer großen Menge an Stapelinformationen usw.)
Übermäßige Animationen führen zu Verzögerungen beim Laden der Seite

Sie können virtuelles Scrollen für lange Listen verwenden

Verzögern Sie das Laden für das Vorladen so weit wie möglich Modul, da die Anzahl der gleichzeitigen HTTP-Anforderungsthreads des Browsers begrenzt ist. Sobald das Limit überschritten wird, werden nachfolgende Anforderungen blockiert und hängen bleiben Zone.js zur Implementierung der Änderungserkennung
  • (2) Nach dem Verständnis von Angular Auf der Grundlage der Änderungserkennung werden die Prinzipien der Angular-Leistungsoptimierung und die Kriterien zur Beurteilung, ob eine Seite eine gute Leistung aufweist, weiter geklärt
  • (3) Einige gezielte Laufzeiten Es werden Lösungen zur Leistungsoptimierung bereitgestellt
  • Weitere programmierbezogene Kenntnisse finden Sie unter:
  • Einführung in die Programmierung
  • ! !

Das obige ist der detaillierte Inhalt vonWie optimiert Angular? Eine kurze Analyse von Lösungen zur Leistungsoptimierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen