Angular を最適化するにはどうすればよいですか?次の記事では、Angular でのパフォーマンスの最適化について説明します。お役に立てば幸いです。
この記事では Angular のパフォーマンスの最適化について説明し、主にランタイムに関連する最適化について紹介します。最適化の方法について話す前に、まずどのようなページにパフォーマンスの問題があるのかを明確にする必要があります。良いパフォーマンスの尺度は何ですか?パフォーマンス最適化の背後にある原理は何ですか?これらの質問に興味がある場合は、読み続けてください。 [関連チュートリアルの推奨事項: "angular チュートリアル"]
変更検出メカニズム# #ネットワーク送信の最適化とは異なり、ランタイムの最適化では、Angular の動作メカニズムと、パフォーマンスの問題を効果的に回避するコードの作成方法 (ベスト プラクティス) に重点が置かれます。 Angular の動作メカニズムを理解するには、まずその変更検出メカニズム (ダーティ チェックとも呼ばれる)、つまり状態の変化をビューに再レンダリングする方法を理解する必要があります。コンポーネントのステータスの変化をビューにどのように反映するかは、3 つのフロントエンド フレームワークすべてが解決する必要がある問題でもあります。さまざまなフレームワークのソリューションは同様のアイデアを持っていますが、それぞれ独自の特徴もあります。
まず第一に、Vue と React は両方とも仮想 DOM を使用してビューの更新を実装しますが、特定の実装にはまだ違いがあります: React の場合:
forceUpdate を使用して
render メソッドをトリガーし、ビューを更新します。
Object.defineProperty を使用して、これらすべてのプロパティをラップされた
getter および
setter# に変換します。
##各コンポーネント インスタンスには、対応する
が呼び出されると、関連するコンポーネントを更新できるように、watcher
に再計算するよう通知します。
そして、Angular はこれを次のように実行します。 Zone .js を導入すると、非同期操作用に API にパッチが適用され、変更検出用のトリガーがリッスンされます。 Zone.js の原理は、前の 記事
で詳しく紹介されています。簡単に言うと、Zone.js は、モンキー パッチを適用することで、ブラウザまたはノード内のすべての非同期 API を強力にカプセル化し、置き換えます。 たとえば、ブラウザの
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 では、ログ記録、パフォーマンスの監視、非同期コールバック実行のタイミング制御などの機能を簡単に実装できます。 これらのフック関数 (フック) は、
メソッドを通じて設定できます。詳細については、次の設定を参照してください: <div class="code" style="position:relative; padding:0px; margin:0px;"><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(&#39;key&#39;) 可以获取
onFork: Function; // 当 zone 被 forked,触发该函数
onIntercept?: Function; // 对所有回调进行拦截
onInvoke?: Function; // 当回调被调用时,触发该函数
onHandleError?: Function; // 对异常进行统一处理
onScheduleTask?: Function; // 当任务进行调度时,触发该函数
onInvokeTask?: Function; // 当触发任务执行时,触发该函数
onCancelTask?: Function; // 当任务被取消时,触发该函数
onHasTask?: Function; // 通知任务队列的状态改变
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
Give one
:
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) }); });
最終的な実行結果:
Zone.js の原理を理解した後、ソースを読んでください。 Angular のコードを見ると、非同期メソッドまたはイベントが呼び出されるたびに変更検出を実装するために Angular で Zone.js が使用されていることがわかります。それはおおよそ次のとおりです。
applicatoin_ref.ts
ファイル内で、ApplicationRef がビルドされるときに、マイクロタスク キューが空であるというコールバック イベントをサブスクライブします。
tickメソッド (つまり、変更検出) を呼び出します:
2 番目に、checkStable メソッドでは、マイクロタスク キューが空になったときに onMicrotaskEmpty
イベントがトリガーされると判断されます (組み合わせて、変更検出をトリガーするのと同等です) ):
最後に、checkStable メソッドの呼び出しをトリガーできる場所は、Zone.js の 3 つのフック関数内です。 onInvoke
、onInvokeTask
、onHasTask
:
#例: onHasTask
—— ZoneTask
の有無が検出されたときにトリガーされるフック:
さらに、Zone.js の非同期タスクは次のとおりです。 3 つのカテゴリに分かれています:
マイクロ タスク: Promise
などによって作成されました。native
の Promise
は前に必要です現在のイベント ループの最後が実行され、パッチが適用された Promise
もイベント ループの終了前に実行されます。
マクロ タスク: setTimeout
などで作成され、native
の setTimeout
が処理時間になります。
イベント タスク: addEventListener
などによって作成されます。これらの タスク
は複数回トリガーされる場合もあれば、まったくトリガーされない場合もあります。
実際、ブラウザの観点からは、イベント タスクは実際にはマクロ タスクとみなすことができます。つまり、すべてのイベントまたは非同期 API はマクロ タスクまたはマイクロ タスクとして理解できます。それらのタスクの 1 つとその実行順序は、前の 記事 で詳しく分析されています。簡単に言うと:
(1) メインスレッドが実行された後、最初にマイクロプロセッサがチェックされます。 . タスクキューにまだ実行すべきタスクがあるかどうか
(2) 初回ポーリング終了後、マクロタスクキューにまだ実行すべきタスクがあるかどうかを確認します。マイクロ タスク リストに実行するタスクがまだあるかどうかを確認し、このプロセスが繰り返されます。
#パフォーマンス最適化の原則
#最も直感的なものページのパフォーマンスを判断する方法は、ページの応答がスムーズで応答性が高いかどうかを確認することです。ページ応答は本質的に、ページ状態の変更をページに再レンダリングするプロセスです。比較的マクロな観点から見ると、Angular の変更検出は実際にはイベント応答サイクル全体の一部にすぎません。ユーザーとページ間のすべてのインタラクションはイベントによってトリガーされ、応答プロセス全体は大まかに次のとおりです: ページの応答速度の最適化を検討する場合は、次のことから始めることができます。各ステージ:(1) トリガー イベント ステージの場合、イベントのトリガーを減らして、変更検出と再レンダリングの全体的な数を減らすことができます(2) イベント ハンドラーの場合実行ロジックステージでは、複雑なコードを最適化できる 実行時間を短縮するためのロジック (3) 変更検出検出データバインディングおよび DOM 更新ステージでは、変更検出とテンプレートデータの計算数を削減して、レンダリング時間 (4) ブラウザのレンダリング段階では、別のブラウザの使用またはハードウェア構成の改善を検討する必要がある場合があります。第 2 段階と第 4 段階はここにあります非同期用に上記の Angular と組み合わせると、第 1 段階と第 3 段階の最適化方法でタスクの分類をさらに明確にすることができます: (1) マクロ タスクのマージ リクエストの場合は、次のようにしてください。ティック数を減らす (2) ティックをマージするマイクロタスクの場合 (3) イベントタスクのイベントトリガーと登録イベントを減らす (4) ティックは次のように分割されます2 つのフェーズ: チェックとレンダリング。チェック フェーズでの計算と不要なレンダリングを削減します前面有提到,大多数情况通过观察页面是否流畅可以判断页面的是否存在性能问题。虽然这种方式简单、直观,但也相对主观,并非是通过精确的数字反映页面的性能到底如何。换言之,我们需要用一个更加有效、精确的指标来衡量什么样的页面才是具备良好性能的。而 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)针对模板 ——减少不必要的计算和渲染
プロセス制御に try/catch を使用しないでください。これにより、多くの時間が消費されます (大量のスタックが記録されます)。
概要#(1) Angular が Zone .js を使用して変更検出を実装する方法を簡単に説明します##(2) Angular の変更検出を理解した上で、原理をさらに明確にしますAngular のパフォーマンス最適化の概要と、ページのパフォーマンスが良好かどうかを判断する基準
(3) ターゲットを絞ったランタイム パフォーマンス最適化ソリューションを提供します
##プログラミング関連の知識の詳細については、次のサイトをご覧ください:プログラミング入門
! !以上がAngular はどのように最適化しますか?パフォーマンス最適化ソリューションの簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。