Angular の変更検出メカニズムの簡単な分析
変更検出とは何ですか?
アプリケーション開発プロセスでは、状態はアプリケーション上に表示する必要があるデータを表します。状態が変化すると、多くの場合、変化した状態を検出し、それに応じて対応するインターフェイスを更新するためのメカニズムが必要になります。このメカニズムは、変更検出メカニズムと呼ばれます。 [関連チュートリアルの推奨事項: "angular チュートリアル"]
WEB 開発では、アプリケーション インターフェイスを更新することは、実際には DOM ツリーを変更することになります。 DOM 操作はコストがかかるため、非効率的な変更検出はアプリケーションのパフォーマンスの低下を引き起こします。したがって、変更検出メカニズムを実装する際のフレームワークの効率がそのパフォーマンスを大きく左右します。
変更検出の実装方法
Angular は、コンポーネント データの変更を検出し、その変更を反映するためにビューを自動的に再レンダリングできます。しかし、ボタンのクリックなどの低レベルのイベントの後に、どうやってこれを実行できるのでしょうか?
ゾーンを通じて、Angular は変更検出メカニズム を自動的にトリガーできます。
ゾーンとは何ですか?つまり、Zone は実行コンテキスト (実行コンテキスト) であり、実行環境として理解できます。一般的なブラウザ実行環境とは異なり、このリンクで実行されるすべての非同期タスクはタスクと呼ばれます。ゾーンはこれらのタスク用のフックを多数提供し、開発者が環境を簡単に「監視」できるようにします。すべての非同期タスク。
余談: Angular はオブザーバブル オブジェクト (Observable) の使用を強く推奨しているため、完全に Observable に基づいてアプリケーションを開発する場合は、Zone を置き換えてコール スタックを追跡する機能を実装できます。ゾーンを使用するよりもわずかに優れており、パフォーマンスが向上します。
// Angular 在 v5.0.0-beta.8 起可以通过配置不使用 Zone import { platformBrowser } from '@angular/platform-browser'; platformBrowser().bootstrapModuleFactory(AppModuleNgFactory, { ngZone: 'noop' });
ブラウザのデフォルトのメカニズムをオーバーライドします
Angular は、起動時にブラウザの低レベル API (addEventListener
など) を書き換えます。は、クリック処理を含むすべてのブラウザ イベントを登録するために使用されるブラウザ関数です。 Angular は、addEventListener
をこれと同等の新しいものに置き換えます:
// this is the new version of addEventListener function addEventListener(eventName, callback) { // call the real addEventListener callRealAddEventListener(eventName, function() { //first call the original callback callback(...); // and then run Angular-specific functionality var changed = angular.runChangeDetection(); if (changed) { angular.reRenderUIPart(); } }); }
新しい addEventListener
は、あらゆるイベント ハンドラーにさらに多くの機能を追加します。コールバックと呼ばれるのは登録だけではありません。変更検出を実行して UI を更新する機会があります。
ブラウザ非同期 API のサポート
変更検出をサポートするために、次の一般的なブラウザ メカニズムにパッチが適用されました:
- すべてのブラウザ イベント (シングルクリック) 、マウスオーバー、キーの押下など)
setTimeout()
andsetInterval()
- Ajax HTTP リクエスト
実際、Zone.js は他の多くのブラウザ API にパッチを適用して、Websocket などの Angular 変更検出を透過的にトリガーします。
このメカニズムの制限の 1 つは、何らかの理由で Zone.js が非同期ブラウザ API をサポートしていない場合、変更検出がトリガーされないことです。これは、たとえば IndexedDB コールバックの場合に当てはまります。
デフォルトの変更検出メカニズムはどのように機能しますか?
すべての Angular コンポーネントには、アプリケーションの起動時に作成される変更検出機能が関連付けられています。例:
@Component({ selector: 'todo-item', template: `<span class="todo noselect" (click)="onToggle()">{{todo.owner.firstname}} - {{todo.description}} - completed: {{todo.completed}}</span>` }) export class TodoItem { @Input() todo:Todo; @Output() toggle = new EventEmitter<Object>(); onToggle() { this.toggle.emit(this.todo); } }
このコンポーネントは、Todo オブジェクトを入力として受け取り、todo 状態が切り替わったときにイベントを発行します。
export class Todo { constructor(public id: number, public description: string, public completed: boolean, public owner: Owner) { } }
Todo には owner
という属性があり、それ自体が 2 つのプロパティ (firstname
と lastname
) を持つオブジェクトであることがわかります。
変更検出器とはどのようなものですか?
変更検出器が実行時にどのように見えるかを実際に確認できます。これを確認するには、特定のプロパティにアクセスしたときにブレークポイントをトリガーするコードを Todo クラスに追加するだけです。
ブレークポイントにヒットすると、スタック トレースをたどって変更検出を表示できます。
このメソッドは、最初は奇妙に見えるかもしれません。すべての変数です。奇妙なネーミング。しかし、さらに詳しく調べてみると、テンプレートで使用されている式ごとに、その式で使用されているプロパティの現在の値とそのプロパティの以前の値を比較するという、非常に単純なことを行っていることがわかりました。
前後のプロパティ値が異なる場合、isChanged
が true に設定されます。それだけです。ほとんどの場合、looseNotIdentical()
というメソッドを使用して値を比較します。
ネストされたオブジェクト owner
はどうなるでしょうか?
変更検出コードでは、ネストされたオブジェクトの owner
プロパティも相違点についてチェックされていることがわかります。ただし、firstname
属性のみが比較され、lastname
属性は比較されません。これは、lastname
がコンポーネント template
で使用されていないためです。同様に、Todo のトップレベルの id 属性は同じ理由で比較されません。
これにより、自信を持って言えます:
默认情况下,Angular Change Detection 通过检查模板表达式的值是否已更改来工作。
我们还可以得出结论:
默认情况下,Angular 不做深度对象比较来检测变化,它只考虑模板使用的属性
为什么默认情况下更改检测会这样工作?
Angular 的主要目标之一是更加透明和易于使用,因此框架用户不必费尽心思调试框架并了解内部机制即可有效地使用它。
如果 Angular 默认更改检测机制基于组件输入的参考比较而不是默认机制,那会是什么情况?即使是像 TODO 应用程序这样简单的东西也很难构建:开发人员必须非常小心地创建一个新的 Todo,而不是简单地更新属性。
OnPush
变化检测策略
如果你觉得默认模式影响了性能,我们也可以自定义 Angular 更改检测。将组件更改检测策略更新为OnPush
:
@Component({ selector: 'todo-list', changeDetection: ChangeDetectionStrategy.OnPush, template: ... }) export class TodoList { ... }
现在让我们在应用程序中添加几个按钮:一个是通过直接改变列表的第一项来切换列表的第一项,另一个是向整个列表添加一个 Todo。代码如下所示:
@Component({ selector: 'app', template: `<div> <todo-list [todos]="todos"></todo-list> </div> <button (click)="toggleFirst()">Toggle First Item</button> <button (click)="addTodo()">Add Todo to List</button>` }) export class App { todos:Array = initialData; constructor() { } toggleFirst() { this.todos[0].completed = ! this.todos[0].completed; } addTodo() { let newTodos = this.todos.slice(0); newTodos.push( new Todo(1, "TODO 4", false, new Owner("John", "Doe"))); this.todos = newTodos; } }
现在让我们看看这两个新按钮的行为:
- 第一个按钮“切换第一项”不起作用!这是因为该
toggleFirst()
方法直接改变了列表中的一个元素。TodoList
无法检测到这一点,因为它的输入参考todos
没有改变 - 第二个按钮确实有效!请注意,该方法
addTodo()
创建了 todo 列表的副本,然后将项目添加到副本中,最后将 todos 成员变量替换为复制的列表。这会触发更改检测,因为组件检测到其输入中的参考更改:它收到了一个新列表! - 在第二个按钮中,直接改变 todos 列表是行不通的!我们真的需要一个新的清单。
OnPush
只是通过引用比较输入吗?
情况并非如此。当使用 OnPush 检测器时,框架将在 OnPush 组件的任何输入属性更改、触发事件或 Observable 触发事件时检查
尽管允许更好的性能,但OnPush
如果与可变对象一起使用,则使用会带来很高的复杂性成本。它可能会引入难以推理和重现的错误。但是有一种方法可以使使用OnPush
可行。
使用 Immutable.js 简化 Angular 应用程序的构建
如果我们只使用不可变对象和不可变列表来构建我们的应用程序,则可以OnPush
透明地在任何地方使用,而不会遇到更改检测错误的风险。这是因为对于不可变对象,修改数据的唯一方法是创建一个新的不可变对象并替换之前的对象。使用不可变对象,我们可以保证:
- 新的不可变对象将始终触发
OnPush
更改检测 - 我们不会因为忘记创建对象的新副本而意外创建错误,因为修改数据的唯一方法是创建新对象
实现不可变的一个不错的选择是使用Immutable.js库。该库为构建应用程序提供了不可变原语,例如不可变对象(映射)和不可变列表。
避免变更检测循环:生产与开发模式
Angular 更改检测的重要属性之一是,与 AngularJs 不同,它强制执行单向数据流:当我们的控制器类上的数据更新时,更改检测运行并更新视图。
如何在 Angular 中触发变更检测循环?
一种方法是如果我们使用生命周期回调。例如,在TodoList组件中,我们可以触发对另一个组件的回调来更改其中一个绑定:
ngAfterViewChecked() { if (this.callback && this.clicked) { console.log("changing status ..."); this.callback(Math.random()); } }
控制台中将显示一条错误消息:
EXCEPTION: Expression '{{message}} in App@3:20' has changed after it was checked
仅当我们在开发模式下运行 Angular 时才会抛出此错误消息。如果我们启用生产模式会发生什么? 在生产模式下,错误不会被抛出,问题也不会被发现。
在开发阶段始终使用开发模式会更好,因为这样可以避免问题。这种保证是以 Angular 总是运行两次变更检测为代价的,第二次检测这种情况。在生产模式下,变更检测只运行一次。
打开/关闭变化检测,并手动触发它
在某些特殊情况下,我们确实想要关闭更改检测。想象一下这样一种情况,大量数据通过 websocket 从后端到达。我们可能只想每 5 秒更新一次 UI 的某个部分。为此,我们首先将更改检测器注入到组件中:
constructor(private ref: ChangeDetectorRef) { ref.detach(); setInterval(() => { this.ref.detectChanges(); }, 5000); }
正如我们所看到的,我们只是分离了变化检测器,这有效地关闭了变化检测。然后我们只需每 5 秒通过调用手动触发它detectChanges()
。
现在让我们快速总结一下我们需要了解的关于 Angular 变更检测的所有内容:它是什么,它是如何工作的以及可用的主要变更检测类型是什么。
概括
Angular 更改检测是一个内置的框架功能,可确保组件数据与其 HTML 模板视图之间的自动同步。
更改检测的工作原理是检测常见的浏览器事件,如鼠标点击、HTTP 请求和其他类型的事件,并确定每个组件的视图是否需要更新。
变更检测有两种类型:
- 默认更改检测:Angular 通过比较事件发生前后的所有模板表达式值来决定是否需要更新视图,用于组件树的所有组件
- OnPush 更改检测:这通过检测是否已通过组件输入或使用异步管道订阅的 Observable 将某些新数据显式推送到组件中来工作
Angular默认更改检测机制实际上与 AngularJs 非常相似:它比较浏览器事件之前和之后模板表达式的值,以查看是否有更改。它对所有组件都这样做。但也有一些重要的区别:
一方面,没有变化检测循环,也没有 AngularJs 中命名的摘要循环。这允许仅通过查看其模板和控制器来推理每个组件。
另一个区别是,由于变化检测器的构建方式,检测组件变化的机制要快得多。
最后,与 AngularJs 不同的是,变化检测机制是可定制的。
更多编程相关知识,请访问:编程教学!!
以上がAngular の変更検出メカニズムの簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











ノンブロッキングおよびイベント駆動に基づいて構築されたノード サービスには、メモリ消費量が少ないという利点があり、大量のネットワーク リクエストの処理に非常に適しています。大量のリクエストを前提として、「メモリ制御」に関する問題を考慮する必要があります。 1. V8 のガベージ コレクション メカニズムとメモリ制限 Js はガベージ コレクション マシンによって制御されます

Vue.js は、今日のフロントエンド開発において非常に人気のあるフレームワークとなっています。 Vue.js が進化し続けるにつれて、単体テストの重要性がますます高まっています。今日は、Vue.js 3 で単体テストを作成する方法を検討し、いくつかのベスト プラクティスと一般的な問題と解決策を提供します。

ファイル モジュールは、ファイルの読み取り/書き込み/開く/閉じる/削除の追加など、基礎となるファイル操作をカプセル化したものです。ファイル モジュールの最大の特徴は、すべてのメソッドが **同期** と ** の 2 つのバージョンを提供することです。 asynchronous**、sync サフィックスが付いているメソッドはすべて同期メソッドであり、持たないメソッドはすべて異種メソッドです。

クロスドメインは開発においてよく遭遇するシナリオであり、インタビューでもよく議論される問題でもあります。一般的なクロスドメイン ソリューションとその背後にある原則を習得すると、開発効率が向上するだけでなく、面接でのパフォーマンスも向上します。

PHP と Vue: フロントエンド開発ツールの完璧な組み合わせ 今日のインターネットの急速な発展の時代において、フロントエンド開発はますます重要になっています。 Web サイトやアプリケーションのエクスペリエンスに対するユーザーの要求がますます高まっているため、フロントエンド開発者は、より効率的で柔軟なツールを使用して、応答性の高いインタラクティブなインターフェイスを作成する必要があります。フロントエンド開発の分野における 2 つの重要なテクノロジーである PHP と Vue.js は、組み合わせることで完璧なツールと見なされます。この記事では、PHP と Vue の組み合わせと、読者がこれら 2 つをよりよく理解し、適用できるようにするための詳細なコード例について説明します。

当初、JS はブラウザ側でのみ動作していたため、Unicode でエンコードされた文字列の処理は簡単でしたが、バイナリ文字列や非 Unicode エンコード文字列の処理は困難でした。バイナリは、コンピュータのビデオ/オーディオ/プログラム/ネットワーク パッケージの最低レベルのデータ形式です。

インターネット技術の発展に伴い、フロントエンド開発の重要性がますます高まっています。特にモバイル デバイスの人気により、効率的で安定しており、安全で保守が容易なフロントエンド開発テクノロジーが必要です。 Go 言語は、急速に発展しているプログラミング言語として、ますます多くの開発者によって使用されています。では、フロントエンド開発に Go 言語を使用することは可能でしょうか?次に、この記事ではフロントエンド開発にGo言語を使用する方法を詳しく説明します。まずはフロントエンド開発にGo言語が使われる理由を見てみましょう。多くの人は Go 言語は

C# 開発者としての私たちの開発作業には、通常、フロントエンドとバックエンドの開発が含まれますが、テクノロジーが発展し、プロジェクトが複雑になるにつれて、フロントエンドとバックエンドの共同開発はますます重要かつ複雑になってきています。この記事では、C# 開発者が開発作業をより効率的に完了できるようにする、フロントエンドとバックエンドの共同開発テクニックをいくつか紹介します。インターフェイスの仕様を決定した後、フロントエンドとバックエンドの共同開発は API インターフェイスの相互作用から切り離せません。フロントエンドとバックエンドの共同開発をスムーズに進めるためには、適切なインターフェース仕様を定義することが最も重要です。インターフェイスの仕様にはインターフェイスの名前が含まれます
