ホームページ  >  記事  >  ウェブフロントエンド  >  Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

青灯夜游
青灯夜游転載
2021-10-11 09:55:472537ブラウズ

Angular を最適化するにはどうすればよいですか?次の記事では、Angular でのパフォーマンスの最適化について説明します。お役に立てば幸いです。

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

この記事では Angular のパフォーマンスの最適化について説明し、主にランタイムに関連する最適化について紹介します。最適化の方法について話す前に、まずどのようなページにパフォーマンスの問題があるのか​​を明確にする必要があります。良いパフォーマンスの尺度は何ですか?パフォーマンス最適化の背後にある原理は何ですか?これらの質問に興味がある場合は、読み続けてください。 [関連チュートリアルの推奨事項: "angular チュートリアル"]

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

変更検出メカニズム# #ネットワーク送信の最適化とは異なり、ランタイムの最適化では、Angular の動作メカニズムと、パフォーマンスの問題を効果的に回避するコードの作成方法 (ベスト プラクティス) に重点が置かれます。 Angular の動作メカニズムを理解するには、まずその変更検出メカニズム (ダーティ チェックとも呼ばれる)、つまり状態の変化をビューに再レンダリングする方法を理解する必要があります。コンポーネントのステータスの変化をビューにどのように反映するかは、3 つのフロントエンド フレームワークすべてが解決する必要がある問題でもあります。さまざまなフレームワークのソリューションは同様のアイデアを持っていますが、それぞれ独自の特徴もあります。

まず第一に、Vue と React は両方とも仮想 DOM を使用してビューの更新を実装しますが、特定の実装にはまだ違いがあります:

React の場合:

    setState
  • または

    forceUpdate を使用して render メソッドをトリガーし、ビューを更新します。

    親コンポーネントはビューを更新します。また、
  • re-render
  • サブコンポーネント

    Vue の場合:

Vue がトラバースするかどうかも決定します。
    data
  • オブジェクトのすべてのプロパティ。

    Object.defineProperty を使用して、これらすべてのプロパティをラップされた getter および setter# に変換します。 ##各コンポーネント インスタンスには、対応する

    watcher
  • インスタンス オブジェクトがあり、コンポーネントのレンダリング プロセス中にプロパティを依存関係として記録します。依存関係
  • setter

    が呼び出されると、関連するコンポーネントを更新できるように、watcher に再計算するよう通知します。

  • そして、Angular はこれを次のように実行します。 Zone .js を導入すると、非同期操作用に API にパッチが適用され、変更検出用のトリガーがリッスンされます。 Zone.js の原理は、前の 記事 で詳しく紹介されています。簡単に言うと、Zone.js は、モンキー パッチを適用することで、ブラウザまたはノード内のすべての非同期 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 はloading の場合、すべての非同期インターフェイスがカプセル化されます。したがって、Zone.js で実行されるすべての非同期メソッドはタスクとして扱われ、タスクによって均一に監視され、非同期タスクの実行の前後または特定の段階で追加の操作を実行するための対応するフック関数 (フック) が提供されます。 。したがって、Zone.js では、ログ記録、パフォーマンスの監視、非同期コールバック実行のタイミング制御などの機能を簡単に実装できます。 これらのフック関数 (フック) は、

Zone.fork()

メソッドを通じて設定できます。詳細については、次の設定を参照してください: <pre class="brush:js;toolbar:false;">Zone.current.fork(zoneSpec) // zoneSpec 的类型是 ZoneSpec // 只有 name 是必选项,其他可选 interface ZoneSpec { name: string; // zone 的名称,一般用于调试 Zones 时使用 properties?: { [key: string]: any; } ; // zone 可以附加的一些数据,通过 Zone.get(&amp;#39;key&amp;#39;) 可以获取 onFork: Function; // 当 zone 被 forked,触发该函数 onIntercept?: Function; // 对所有回调进行拦截 onInvoke?: Function; // 当回调被调用时,触发该函数 onHandleError?: Function; // 对异常进行统一处理 onScheduleTask?: Function; // 当任务进行调度时,触发该函数 onInvokeTask?: Function; // 当触发任务执行时,触发该函数 onCancelTask?: Function; // 当任务被取消时,触发该函数 onHasTask?: Function; // 通知任务队列的状态改变 }</pre>Give one

onInvoke の簡単な例

:

let logZone = Zone.current.fork({ 
  name: &#39;logZone&#39;,
  onInvoke: function(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) {
    console.log(targetZone.name, &#39;enter&#39;);
    parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)
    console.log(targetZone.name, &#39;leave&#39;); }
});

logZone.run(function myApp() {
    console.log(Zone.current.name, &#39;queue promise&#39;);
    Promise.resolve(&#39;OK&#39;).then((value) => {console.log(Zone.current.name, &#39;Promise&#39;, value)
  });
});

最終的な実行結果:

Zone.js の原理を理解した後、ソースを読んでください。 Angular のコードを見ると、非同期メソッドまたはイベントが呼び出されるたびに変更検出を実装するために Angular で Zone.js が使用されていることがわかります。それはおおよそ次のとおりです。

まず、

applicatoin_ref.ts

ファイル内で、

ApplicationRefAngular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析 がビルドされるときに、マイクロタスク キューが空であるというコールバック イベントをサブスクライブします。

tick

メソッド (つまり、変更検出) を呼び出します:

2 番目に、checkStable メソッドでは、マイクロタスク キューが空になったときに onMicrotaskEmpty イベントがトリガーされると判断されます (組み合わせて、変更検出をトリガーするのと同等です) ):

2-Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

最後に、checkStable メソッドの呼び出しをトリガーできる場所は、Zone.js の 3 つのフック関数内です。 onInvokeonInvokeTaskonHasTask:

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

#例: onHasTask—— ZoneTask の有無が検出されたときにトリガーされるフック:

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

さらに、Zone.js の非同期タスクは次のとおりです。 3 つのカテゴリに分かれています:

マイクロ タスク: Promise などによって作成されました。nativePromise は前に必要です現在のイベント ループの最後が実行され、パッチが適用された Promise もイベント ループの終了前に実行されます。

マクロ タスク: setTimeout などで作成され、nativesetTimeout が処理時間になります。

イベント タスク: addEventListener などによって作成されます。これらの タスクは複数回トリガーされる場合もあれば、まったくトリガーされない場合もあります。

実際、ブラウザの観点からは、イベント タスクは実際にはマクロ タスクとみなすことができます。つまり、すべてのイベントまたは非同期 API はマクロ タスクまたはマイクロ タスクとして理解できます。それらのタスクの 1 つとその実行順序は、前の 記事 で詳しく分析されています。簡単に言うと:

(1) メインスレッドが実行された後、最初にマイクロプロセッサがチェックされます。 . タスクキューにまだ実行すべきタスクがあるかどうか

(2) 初回ポーリング終了後、マクロタスクキューにまだ実行すべきタスクがあるかどうかを確認します。マイクロ タスク リストに実行するタスクがまだあるかどうかを確認し、このプロセスが繰り返されます。

#パフォーマンス最適化の原則

#最も直感的なものページのパフォーマンスを判断する方法は、ページの応答がスムーズで応答性が高いかどうかを確認することです。ページ応答は本質的に、ページ状態の変更をページに再レンダリングするプロセスです。比較的マクロな観点から見ると、Angular の変更検出は実際にはイベント応答サイクル全体の一部にすぎません。ユーザーとページ間のすべてのインタラクションはイベントによってトリガーされ、応答プロセス全体は大まかに次のとおりです:

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

ページの応答速度の最適化を検討する場合は、次のことから始めることができます。各ステージ:

(1) トリガー イベント ステージの場合、イベントのトリガーを減らして、変更検出と再レンダリングの全体的な数を減らすことができます

(2) イベント ハンドラーの場合実行ロジックステージでは、複雑なコードを最適化できる 実行時間を短縮するためのロジック

(3) 変更検出検出データバインディングおよび DOM 更新ステージでは、変更検出とテンプレートデータの計算数を削減して、レンダリング時間

(4) ブラウザのレンダリング段階では、別のブラウザの使用またはハードウェア構成の改善を検討する必要がある場合があります。

第 2 段階と第 4 段階はここにあります非同期用に上記の Angular と組み合わせると、第 1 段階と第 3 段階の最適化方法でタスクの分類をさらに明確にすることができます:

(1) マクロ タスクのマージ リクエストの場合は、次のようにしてください。ティック数を減らす

(2) ティックをマージするマイクロタスクの場合

(3) イベントタスクのイベントトリガーと登録イベントを減らす

(4) ティックは次のように分割されます2 つのフェーズ: チェックとレンダリング。チェック フェーズでの計算と不要なレンダリングを削減します

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

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

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

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

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

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

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

性能优化方案

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

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

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

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

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

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

如上图,防抖动处理只是保证了代码逻辑不会重复运行,但是 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 可以跳过某个组件或者某个父组件以及它下面所有子组件的变化检测,如下所示:

Angular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析

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

  • リストのループレンダリングには trackBy を使用します。
  • キャッシュされた値をできるだけ使用し、メソッド呼び出しや属性呼び出しの使用を避けます。
  • 本当に関数を呼び出す必要があるテンプレートと、複数の呼び出しの場合は、テンプレート キャッシュ
  • ngIf を使用してコンポーネントの表示を制御し、コンポーネントが呼び出される場所に配置できます。
(6) その他のコーディング最適化の提案

プロセス制御に try/catch を使用しないでください。これにより、多くの時間が消費されます (大量のスタックが記録されます)。
  • 過剰なアニメーションはページの読み込み遅延を引き起こします
  • 長いリストには仮想スクロールを使用できます
  • プリロード モジュールの読み込みをできるだけ遅らせます。ブラウザの同時 http リクエスト スレッド数には制限があるためです。制限を超えると、後続のリクエストはブロックされます。ブロックとハング
  • Wait

概要#(1) Angular が Zone .js を使用して変更検出を実装する方法を簡単に説明します##(2) Angular の変更検出を理解した上で、原理をさらに明確にしますAngular のパフォーマンス最適化の概要と、ページのパフォーマンスが良好かどうかを判断する基準

(3) ターゲットを絞ったランタイム パフォーマンス最適化ソリューションを提供します

##プログラミング関連の知識の詳細については、次のサイトをご覧ください:

プログラミング入門

! !

以上がAngular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。