ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript フレームワークにおける変更と変更検出の詳細な図による説明

JavaScript フレームワークにおける変更と変更検出の詳細な図による説明

黄舟
黄舟オリジナル
2017-03-09 14:50:561209ブラウズ

JavaScript フレームワークの変更と変更検出の詳細な図による説明

2015 年に入ると、開発者には JS フレームワークに関する選択肢が増えました。 Angular、Ember、React、Backbone に加えて、多数の競合他社が出現しています。現在ではフレームワークが多すぎて選択できません。

誰もがさまざまな観点からこれらのフレームワークを比較できますが、最も興味深い違いの 1 つは状態の管理方法だと思います。特に、これらのフレームワークでは、状態が頻繁に変化する場合にどのような動作をするのか、ユーザー インターフェイスの変化にどのような方法で対応するのかを考えることは非常に意味があります。

アプリケーションの状態とユーザー インターフェイスの一貫性の管理は、長い間 UI 開発における複雑さの原因でした。この記事では、これまでのところ、それに対処するためのいくつかの方法を取り上げます: Ember のデータ バインディング、Angular のダーティ チェック、React の仮想 DOM と不変データ構造との関係。

データの表示

これから説明する基本的なタスクは、プログラムの内部状態と、それを画面に表示される可視要素に組み込む方法についてです。オブジェクト、配列、文​​字列、数値のセットを取得し、それらをテキスト、フォーム、リンク、ボタン、画像のツリー構造に変換します。 Web 開発では、前者は JavaScript データ構造として表現され、後者は DOM として表現されることがよくあります。

私たちはこのプロセスを「レンダリング」と呼びますが、これはデータ モデルを目に見えるユーザー インターフェイス コンテンツに「マッピング」するものと考えることができます。テンプレートを使用してデータをレンダリングすると、データを表す DOM (または HTML) が得られます。

プロセス自体は非常に単純に思えます。フォーム データ モデルを UI にマッピングするのは簡単ではないかもしれませんが、入力から出力への非常に単純な変換プロセスです。

データが頻繁に変更されると言うと、事態は難しくなります。ユーザーが UI を操作するとき、またはデータを更新する世界で何かが変化するとき、UI はいずれにしてもそれらの変更を反映する必要があります。さらに、DOM ツリーを再構築する操作は高価な操作であり、多額の費用を消費するため、画面にデータを更新する作業はできるだけ少なくしたいと考えています。

UIを一度レンダリングするだけと比べて、ステータスの更新を伴うため、より難しい問題があります。ここでも、いくつかの異なるシナリオが考えられます。

サーバーサイドレンダリング: 最初からやり直します

「変化はありません、宇宙は不変です」

JavaScript の偉大な時代以前は、Web ページとのすべての対話がサーバーサイドのバックアンド-をトリガーしていました。 4 回目のインタラクション、およびクリックごとに、フォームの送信は Web ページのリロードを意味し、リクエストがサーバーに送信され、サーバーが新しいページを処理して応答し、ブラウザーがそのページを再レンダリングします。

この場合、何かが起こるたびにフロントエンドは状態を管理する必要がなく、ブラウザは何も気にしません。どのようなステータスであっても、サーバーによって管理されます。フロントエンドは、サーバーによって生成された HTML と CSS、そしておそらく少しの JavaScript です。

フロントエンドの観点から見ると、これは非常に単純な方法であり、処理も非常に遅くなります。各対話は UI の再レンダリングを意味するだけでなく、データがリモート データ センターに返され、その後リモートからフロントエンド インターフェイスに返されるリモート対話プロセスでもあります。

今では、私たちのほとんどはもうそんなことはしません。サーバー側でアプリケーションの状態を初期化し、この状態をフロントエンドで管理できます (この同型 JavaScript 記事の大部分でこれについて説明しています)。ただし、このより複雑な方法をうまく使用している人もまだいます。 。

第一世代の JS: インターフェイスの手動再描画

「どこを再描画すればよいか分からないので、指摘してください。」

第一世代の JavaScript フレームワーク (Backbone.js、Ext JS、Dojo など)。初めて実際のデータ モデルがブラウザに導入され、DOM を変更するだけの軽量スクリプトが置き換えられます。これは、初めてブラウザ上で状態を変更できることも意味します。データ モデルの内容が変更され、その変更をユーザー インターフェイスに反映します。

これらのフレームワークはアーキテクチャ的に UI コードをモデルから分離していますが、2 つの同期は依然として自分で行う必要があります。変更が発生すると、一連のイベントを取得できますが、どの部分をどのように再レンダリングする必要があるかを指定するのはユーザーの責任です。

このモデルのパフォーマンスには、アプリケーション開発者に開発の余地が多く残されています。どのコンテンツをいつ更新するかを制御できるため、必要に応じて微調整できます。多くの場合、ページの広い領域を単純に再レンダリングすることと、更新が必要なページのごく一部のみを更新することの間にはトレードオフが存在します。

Ember.js データ バインディング

「モデルとビューを制御するため、何を再描画する必要があるかを正確に知ることができます。」

どの状態変更を再描画する必要があるかを手動で指摘できます。これは最初の A です。 JavaScript アプリケーションのコード生成における複雑さの主な原因。多くのフレームワークは、問題のこの部分を解消するために設計されています。 Embe.js もその 1 つです。

Ember は Backbone に似ており、変更が発生するとモデルからイベントが発行されます。違いは、Ember はイベントの受信側にいくつかの機能も提供していることです。 UI をデータ モデルにバインドできます。これは、リスナーが UI にアタッチされ、変更イベントをリッスンすることを意味します。このリスナーは、イベントの受信後に何を更新する必要があるかを知っています。

これにより、非常に効果的な変更メカニズムが作成されます。最初にすべてのバインディングを設定することで、将来の同期コストが低くなります。何かが変更されると、アプリケーションの本当に変更する必要がある部分のみが変更されます。

このアプローチの最大のトレードオフは、データ モデルが変更されたときに、その変更を Ember に通知する必要があることです。これは、データが Ember 固有の API を継承する必要があり、データを変更して特別な set メソッドを追加する必要があることを意味します。 foo.x=42 は使用できません。foo.set('x',42) などを使用する必要があります。

将来的には、このメソッドは ECMAScript6 の登場によって恩恵を受ける可能性があります。バインディング メソッドを使用して Ember に一般オブジェクトを装飾させることができるため、このオブジェクトと対話するすべてのコードでこのセット変換を使用する必要がなくなります。

AngularJS: ダーティチェック

「何が変わったのか分かりません。更新する必要があるものをすべてチェックするだけです。」

Ember と同様に、Angular の目標は、手動で再実行しなければならないという問題を解決することです。変更後にレンダリングします。ただし、別の方法を使用します。

この式 {{foo.x}} などの Angular テンプレート コードを参照すると、Angular はこのデータをリッスンするだけでなく、この値のオブザーバーも作成します。その後、アプリケーションで何かが変更されるたびに、Angular はオブザーバーの値が前回から変更されているかどうかを確認します。変更された場合は、その値を UI に再レンダリングします。検査オブザーバーのこのような処理方法はダーティ チェックと呼ばれます。

この検出方法の最大の利点は、モデル内で必要なものを何でも使用できることです。angular にはこれに関する制約がありません。それは気にしません。基本オブジェクトを継承したり、特定の API を実装したりする必要はありません。

欠点は、データ モデルには何が変更されたかをフレームワークに伝える組み込みの検出機能がないため、フレームワークは変更があったかどうか、または正確にどこが変更されたかを知る方法がないことです。これは、モデルの変更を外部でチェックする必要があることを意味します。これが Angular の機能です。どのような変更が行われても、すべてのオブザーバーが実行されます。クリック イベント処理、HTTP 応答処理、タイムアウトなどにより、概要が生成されます。これが責任のあるプロセスです。オブザーバーを実行するため。

毎回すべてのオブザーバーを実行するのはパフォーマンス上の悪夢のように聞こえますが、実際には超高速です。これは通常、変更が実際に検出されるまで DOM アクセスが発生せず、純粋な JavaScript で参照をチェックするコストが依然として非常に低いためです。ただし、大規模な UI が発生したり、頻繁に再レンダリングが必要な場合は、追加の最適化対策が不可欠です。

Ember と同様に、Angular も今後の標準の恩恵を受けます。EMACScript7 には、Angular に最適な Object.observe メソッドがあり、オブジェクトのプロパティの変更を監視するためのネイティブ API を提供します。ただし、オブザーバーは単純なオブジェクトのプロパティを観察するだけではないため、これは Angular のニーズをすべて満たすものではありません。

今後の Angular2 では、フロントエンドの更新チェックに関するいくつかの興味深い更新も行われます。Victor Savkin によって公開されたこの記事に関する最近の記事もあります。Victor が ng-conf で述べたこともご覧ください。

React: 仮想 DOM

「何が変わったのかわからないので、すべてをレンダリングして何が違うのか見てみることにします。」

React には興味深い機能がたくさんありますが、その中で最も興味深いのは仮想 DOM です。

React は、モデル API の使用を強制しないという点で Angular に似ており、適切と思われる任意のオブジェクトやデータ構造を使用できます。では、変更後に UI を最新の状態に保つにはどうすればよいでしょうか?

React が行うことは、古いサーバーサイド レンダリングの時代に戻ることです。そこでは、状態の変更を単純に無視できます。どこかで何かが変更されるたびに、UI 全体が再描画されます。これにより、UI コードが大幅に簡素化されます。 React コンポーネントの状態を維持することは気にしません。サーバー側のレンダリングと同様に、レンダリングは 1 回だけです。コンポーネントを変更する必要がある場合、コンポーネントは再度再レンダリングされます。最初のレンダリングと、更新されたデータによる後続のレンダリングの間に違いはありません。

これは非常に非効率的です。 React がそれだけを行うのであれば、それだけです。ただし、React は特別な方法を使用して再レンダリングします。

React UI がレンダリングするとき、最初に仮想 DOM にレンダリングされます。これは実際の DOM オブジェクトではなく、軽量の純粋な JavaScript オブジェクト構造であり、実際の DOM オブジェクトを表現するための単純なオブジェクトと配列が含まれています。この仮想 DOM を取得して、画面に表示できる実際の DOM 要素を作成するには、特別なプロセスが必要になります。

そして、変更があれば、その変更から新しい仮想DOMが生成されます。この新しい仮想 DOM は、データ モデルの新しい状態を反映します。 React には現在、新旧の 2 つの仮想 DOM があります。 2 つの仮想 DOM の差分比較アルゴリズムを使用して、一連の変更を取得します。これらの変更 (新しい要素の追加、要素の属性値の変更など) は実際の DOM に適用されます。

React を使用する非常に大きな利点、または少なくとも 1 つの利点は、変更を追跡する必要がないことです。新しい結果の変更がいつどこで行われたとしても、UI 全体を再レンダリングするだけで済みます。 Virtual DOM の差分チェック方法により、これが自動的に行われるため、コストのかかる DOM 操作が大幅に削減されます。

Om: 不変のデータ構造

「これらのものは変わっていないことがはっきりとわかります」

React の仮想 DOM テクノロジーはすでに非常に高速ですが、レンダリングしたいページが大きい場合や非常に頻繁に実行される場合 (詳細1 秒あたり 60 回を超える場合)、依然としてパフォーマンスのボトルネックが存在します。

Ember のように、データ モデルを変更するときに特別なコントロールを作成しない限り、(仮想および現実の) DOM 全体の再レンダリングを避ける方法は実際にはありません。

変更を制御する 1 つの方法は、不変の永続データ構造を使用することです。

React と ClojureScript に基づいた Om ライブラリを使用した David Nolen の研究で実証されているように、これらは React の仮想 DOM メソッドとうまく動作するようです。

不変のデータ構造については、その名前が示すように、オブジェクトを変更することはできず、その新しいバージョンを作成することしかできません。オブジェクトのプロパティを変更したい場合、それが変更できない場合は、次のようになります。新しいオブジェクトのみを作成し、この新しいプロパティに設定できます。永続データの仕組みにより、これは実際には思ったよりうまく機能します。

React コンポーネントの状態が不変データで構成されている場合、変更チェックは次のようになります: コンポーネントを再レンダリングするときに、コンポーネントの状態が前回レンダリングしたのと同じデータ構造をまだ指している場合は、これをスキップできます。このコンポーネントの最後の仮想 DOM と、この変更されていないコンポーネントをブランチ ノードとして含む内部コンポーネント全体を引き続き使用できます。ステータスに変化がないため、現時点ではさらにテストを続ける必要はありません。

Ember と同様に、Om のようなライブラリでは、データ内で古い JavaScript オブジェクト グラフを使用できません。モデルを最初から構築できるのは、不変のデータ構造を使用する場合のみです。違いは、今回はフレームワークの要件を満たすためにそれを行う必要がないことだと思います。これを行うのは、単にアプリケーションの状態を管理するためのより良い方法であるためです。不変のデータ構造を使用する利点は、レンダリングのパフォーマンスを向上させることではなく、アプリケーションのアーキテクチャを簡素化することです。

Om と ClojureScript も React と不変データ構造を組み合わせるのに役立ちますが、必須ではありません。純粋な React と Facebook の Immutable-js のようなライブラリを使用するだけで十分かもしれません。このライブラリの作者である Lee Byron は、React.js Conf でこのトピックについて素晴らしい紹介をしました。

Rich Hickey の永続データ構造と参照管理を参照することをお勧めします。これは状態管理手法の入門でもあります。

私はしばらくの間、詩の中で不変のデータ構造を使用してきました。フロントエンド UI アーキテクチャで使用されることは当然のこととは言えませんが。しかし、これは実際に起こっているようで、Angular チームはこれらのコンテンツのサポートの追加にも取り組んでいます。

概要

変更検出は UI 開発における中心的な問題であり、さまざまな JavaScript フレームワークが独自のメソッドを使用してさまざまなソリューションを提供しています。

EmberJS はデータ モデル API を制御するため、変更が発生したときにそれを検出できます。データを変更するために API を呼び出すと、対応するイベントがトリガーされます。

Angular.js は、変更が発生した後にそれを検出し、UI に登録したデータ バインディングを再実行し、値が変更されたかどうかを確認します。

Pure React は、UI 全体を新しい仮想 DOM に再レンダリングし、古い仮想 DOM と比較することでデータの変更を検出します。何が変更されたかを見つけて、それをリビジョンとして実際の DOM に送信します。

React および不変のデータ構造は、純粋な React ソリューションの拡張バージョンとして使用でき、React コンポーネント内では状態の変更が許可されていないため、コンポーネント ツリーを未変更の状態としてすぐにマークできます。内部状態への変更を禁止するのは、パフォーマンス上の理由からではなく、アプリケーションのアーキテクチャにプラスの影響を与えるためです。

翻訳者情報

翻訳者: Li Bingchen、HP ソフトウェア開発部門に勤務し、JAVA 製品開発で 10 年以上の経験があり、C#、Python、Nodejs に精通しています。彼は、インターネット電子商取引プラットフォームおよびエンタープライズ ソフトウェアの開発と管理において豊富な経験を持っています。私の現在の関心は、フロントエンド開発と金融サービスにおけるデータ統計分析の応用です。


以上がJavaScript フレームワークにおける変更と変更検出の詳細な図による説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。