反応性モデルの説明
序文
私がアプリケーションと Web サイトの開発を始めてから (すでに) 10 年になりますが、JavaScript エコシステムが今日ほどエキサイティングなものはありません!
2022 年、コミュニティは「Signal」のコンセプトに魅了され、ほとんどの JavaScript フレームワークがそれを独自のエンジンに統合しました。私は Preact について考えています。Preact は 2022 年 9 月以来、コンポーネントのライフサイクルから切り離されたリアクティブ変数を提供しています。最近では、2023 年 5 月に実験的に Signals を実装した Angular が、バージョン 18 から正式に開始されました。他の JavaScript ライブラリもアプローチを再考することを選択しました...
2023 年から現在まで、私はさまざまなプロジェクトで一貫して Signals を使用してきました。その実装と使用のシンプルさは私を完全に納得させ、技術ワークショップ、トレーニング セッション、カンファレンス中にそのメリットを専門家ネットワークと共有しました。
しかし最近、このコンセプトは本当に「革命的」なのか、Signals に代わるものはあるのか、と自問し始めました。そこで、私はこの考察をさらに深く掘り下げ、リアクティブ システムへのさまざまなアプローチを発見しました。
この投稿は、さまざまな反応性モデルの概要と、それらがどのように機能するかについての私の理解を示しています。
注意: この時点では、おそらくお察しのとおり、Java の「Reactive Streams」については説明しません。そうでなければ、この投稿のタイトルを「WTF Is Backpressure!?」 ?
にしていたでしょう。理論
反応性モデルについて話すときは、(何よりもまず) "反応性プログラミング" について話しますが、特に "反応性" について話します。
リアクティブ プログラミングは、データ ソースの変更を消費者に自動的に伝達できる開発パラダイムです。
したがって、反応性を、データの変更に応じてリアルタイムで依存関係を更新する機能として定義できます。
NB: つまり、ユーザーがフォームに入力したり送信したりすると、これらの変更に反応し、読み込みコンポーネント、または何かが起こっていることを示すその他の表示を行う必要があります。 .. 別の例として、データを非同期で受信する場合、このデータのすべてまたは一部を表示したり、新しいアクションを実行したりするなどの対応が必要です。
これに関連して、リアクティブ ライブラリは自動的に更新され、効率的に伝播する変数を提供するため、シンプルで最適化されたコードを簡単に作成できます。
効率を高めるために、これらのシステムは、値が変更された場合に限り、これらの変数を再計算/再評価する必要があります。同様に、ブロードキャストされたデータの一貫性と最新性を確保するために、システムは中間状態 (特に状態変化の計算中) を表示しないようにする必要があります。
NB: 状態とは、プログラム/アプリケーションの存続期間全体を通じて使用されるデータ/値を指します。
わかりました、しかしそれでは…これらの "反応性モデル" とは一体何なのでしょうか?
PUSH、別名「熱心な」反応性
最初の反応性モデルは、「PUSH」 (または「熱心な」反応性) と呼ばれます。このシステムは次の原則に基づいています:
- データ ソースの初期化 (「Observables」として知られています)
- コンポーネント/関数はこれらのデータ ソースをサブスクライブします (これらはコンシューマーです)
- 値が変更されると、データはすぐにコンシューマー (「オブザーバー」として知られています) に伝播されます
ご想像のとおり、「PUSH」モデルは「Observable/Observer」設計パターンに依存しています。
1 番目の使用例: 初期状態と状態変化
次の初期状態を考えてみましょう。
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
リアクティブ ライブラリ (RxJS など) を使用すると、この初期状態は次のようになります。
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
注意: この投稿では、すべてのコード スニペットを「疑似コード」と見なす必要があります。
ここで、コンシューマ (コンポーネントなど) が、このデータ ソースが更新されるたびに状態 D の値をログに記録したいと仮定します。
d.subscribe((value) => console.log(value));
私たちのコンポーネントはデータ ストリームをサブスクライブします。まだ変化を引き起こす必要があります。
a.next({ firstName: "Jane", lastName: "Doe" });
そこから、「PUSH」システムが変更を検出し、自動的に消費者にブロードキャストします。上記の初期状態に基づいて、発生する可能性のある操作を以下に説明します。
- データ ソース A で状態変化が発生しました!
- A の値は B に伝播されます (データ ソース B の計算);
- 次に、B の値が D に伝播されます (データ ソース D の計算);
- A の値は C に伝播されます (データ ソース C の計算);
- 最後に、C の値が D に伝播されます (データ ソース D の再計算);
このシステムの課題の 1 つは計算の順序にあります。実際、私たちのユースケースに基づくと、D は 2 回評価される可能性があることがわかります。1 回目は前の状態の C の値で、2 回目は前の状態の C の値で評価されます。 2 回目は C の値が最新です。この種の反応性モデルでは、この課題は 「ダイヤモンド問題」 ♦️.
と呼ばれます。2 番目の使用例: 次の反復
次に、状態が 2 つの主要なデータ ソースに依存していると仮定します。
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
E を更新するとき、システムは状態全体を再計算します。これにより、前の状態を上書きすることで、信頼できる単一の情報源を保持できます。
- データ ソース E で状態変化が発生しました!
- A の値は B に伝播されます (データ ソース B の計算);
- 次に、B の値が D に伝播されます (データ ソース D の計算);
- A の値は C に伝播されます (データ ソース C の計算);
- E の値は C に伝播されます (データ ソース C の再計算)。
- 最後に、C の値が D に伝播されます (データ ソース D の再計算);
再び「ダイヤモンド問題」が発生します...今回はデータ ソース C 上で 2 回評価される可能性があり、常に D 上で評価されます。
ダイヤモンドの問題
「ダイヤモンド問題」は、「熱心な」反応性モデルにおける新しい課題ではありません。一部の計算アルゴリズム (特に MobX で使用されるアルゴリズム) は、状態計算を平準化するために「リアクティブな依存関係ツリーのノード」にタグを付けることができます。このアプローチでは、システムは最初に「ルート」データ ソース (この例では A と E)、次に B と C、最後に D を評価します。状態計算の順序を変更すると、この種の問題の解決に役立ちます。
PULL、別名「怠惰な」反応性
2 番目の反応性モデルは 「PULL」 と呼ばれます。 "PUSH" モデルとは異なり、次の原則に基づいています:
- リアクティブ変数の宣言
- システムは状態の計算を延期します
- 派生状態は依存関係に基づいて計算されます
- システムは過剰なアップデートを回避します
覚えておくべき最も重要なのはこの最後のルールです。前のシステムとは異なり、この最後のルールは、同じデータ ソースの複数の評価を避けるために状態の計算を延期します。
1 番目の使用例: 初期状態と状態変化
前の初期状態を保持しましょう...
この種のシステムでは、初期状態の構文は次の形式になります。
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
注意: React 愛好家はおそらくこの構文を認識しているでしょう ?
リアクティブ変数を宣言すると、タプルが「誕生」します。一方は不変変数です。もう一方のこの変数の更新関数。残りのステートメント (この場合は B、C、D) は、それぞれの依存関係を「リッスン」するため、派生状態とみなされます。
d.subscribe((value) => console.log(value));
「遅延」システムの決定的な特徴は、変更がすぐには反映されず、明示的に要求された場合にのみ反映されることです。
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
「PULL」モデルでは、(コンポーネントから) エフェクト() を使用してリアクティブ変数 (依存関係として指定) の値を記録すると、状態変化の計算がトリガーされます。
- D は、その依存関係 (B および C) が更新されたかどうかを確認します。
- B は、その依存関係 (A) が更新されたかどうかを確認します。
- A はその値を B に伝播します (B の値を計算します);
- C は、その依存関係 (A) が更新されたかどうかを確認します。
- A はその値を C に伝播します (C の値を計算します)
- B と C はそれぞれの値を D に伝播します (D の値を計算します);
依存関係をクエリするときに、このシステムの最適化が可能です。実際、上記のシナリオでは、A が更新されたかどうかを判断するために 2 回クエリが実行されます。ただし、状態が変化したかどうかを定義するには、最初のクエリで十分な場合があります。 C はこのアクションを実行する必要はありません...代わりに、A はその値をブロードキャストすることのみが可能です。
2 番目の使用例: 次の反復
2 番目のリアクティブ変数「root」を追加して、状態をいくらか複雑にしてみましょう。
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
もう一度言いますが、システムは明示的に要求されるまで状態の計算を延期します。以前と同じ効果を使用して、新しいリアクティブ変数を更新すると、次の手順がトリガーされます:
- D は、その依存関係 (B および C) が更新されたかどうかを確認します ;
- B は、その依存関係 (A) が更新されたかどうかを確認します ;
- C は、その依存関係 (A および E) が更新されたかどうかを確認します ;
- E はその値を C に伝播し、C はメモ化 (C の値の計算) を介して A の値をフェッチします。
- C はその値を D に伝播し、D はメモ化 (D の値の計算) を介して B の値をフェッチします。
A の値は変更されていないため、この変数を再計算する必要はありません (同じことが B の値にも当てはまります)。このような場合、メモ化アルゴリズムを使用すると、状態計算中のパフォーマンスが向上します。
プッシュプル、別名「きめの細かい」反応性
最後の反応性モデルは、「PUSH-PULL」システムです。 「PUSH」という用語は変更通知の即時伝達を反映し、「PULL」はオンデマンドで状態値をフェッチすることを指します。このアプローチは、以下の原則に従う、いわゆる「きめの細かい」反応性と密接に関連しています。
- リアクティブ変数の宣言 (リアクティブ プリミティブについて話しています)
- 依存関係は原子レベルで追跡されます
- 変更の伝播は非常に的を絞ったものです
この種の反応性は「PUSH-PULL」モデルに限定されたものではないことに注意してください。きめ細かい反応性とは、システムの依存関係を正確に追跡することを指します。したがって、この方法でも機能する PUSH と PULL 反応性モデルがあります (私は Jotai または Recoil について考えています。
1 番目の使用例: 初期状態と状態変化
まだ前の初期状態に基づいています...「きめの細かい」反応性システムにおける初期状態の宣言は次のようになります:
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
注意: シグナル キーワードの使用は、ここでの単なる逸話ではありません ?
構文の点では、「PUSH」モデルに非常に似ていますが、注目すべき重要な違いが 1 つあります: 依存関係! 「きめ細かい」反応性システム、派生状態は使用する変数を暗黙的に追跡するため、派生状態の計算に必要な依存関係を明示的に宣言する必要はありません。この場合、B と C は A の値への変更を自動的に追跡し、D は B と C の両方への変更を追跡します。
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
このようなシステムでは、リアクティブ変数の更新は、基本的な "PUSH" モデルよりも効率的です。これは、変更がそれに依存する派生変数に自動的に伝播されるためです (通知としてのみ、伝播されません)。値そのもの)。
d.subscribe((value) => console.log(value));
次に、オンデマンド (ロガー の例を見てみましょう) に応じて、システム内で D を使用すると、関連するルート状態 (この場合は A) の値がフェッチされ、値が計算されます。派生状態 (B と C) を計算し、最後に D を評価します。これは直感的な操作モードではないでしょうか?
2 番目の使用例: 次の反復
次の状態を考えてみましょう。
a.next({ firstName: "Jane", lastName: "Doe" });
もう一度言いますが、PUSH-PULL システムの「きめ細かい」側面により、各状態の自動追跡が可能になります。したがって、派生状態 C はルート状態 A と E を追跡するようになりました。変数 E を更新すると、次のアクションがトリガーされます:
- リアクティブプリミティブ E の状態変化!
- 対象を絞った変更通知 (C を介して E から D);
- E はその値を C に伝播し、C はメモ化 (C の値の計算) を介して A の値を取得します。
- C はその値を D に伝播し、D はメモ化 (D の値の計算) を介して B の値を取得します。
これは、このモデルを非常に効率的にする、リアクティブな依存関係の相互の事前の関連付けです!
実際、古典的な "PULL" システム (React の Virtual DOM など) では、コンポーネントからリアクティブ状態を更新すると、フレームワークに変更が通知されます (" 違います」フェーズ)。次に、オンデマンド (および遅延) で、フレームワークはリアクティブな依存関係ツリーを走査して変更を計算します。変数が更新されるたびに!この依存関係の状態の「発見」には多大なコストがかかります...
「きめ細かい」リアクティブ システム (シグナルなど) を使用すると、リアクティブ変数/プリミティブの更新により、それらにリンクされている派生状態に変更が自動的に通知されます。したがって、関連する依存関係を (再) 検出する必要はありません。状態の伝播がターゲットです!
結論(.value)
2024 年、ほとんどの Web フレームワークは、特に反応性モデルの観点から、その動作方法を再考することを選択しました。この変化により、企業の効率性と競争力は全般的に向上しました。他の人は (まだ) ハイブリッド (ここでは Vue について考えています) を選択しており、これにより多くの状況でより柔軟になります。
最後に、どのモデルを選択しても、私の意見では、(良い) リアクティブ システムはいくつかの主要なルールに基づいて構築されます。
- システムは、一貫性のない派生 状態を防止します。
- システム内で状態を使用すると、リアクティブに派生した状態が生成されます。
- システムは過剰な作業を最小限に抑えます ;
- そして、「与えられた初期状態について、状態がたどる経路に関係なく、システムの最終結果は常に同じになります! 「
この最後の点は、宣言型プログラミングの基本原則として解釈できますが、(良い) リアクティブ システムは決定論的である必要があると私がどのように考えているかを示しています。これは、アルゴリズムの複雑さに関係なく、リアクティブ モデルを信頼性が高く、予測可能で、大規模な技術プロジェクトで使いやすくする「決定論」です。
以上が一体、反応性とは!?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

それで、あなたはここで、Ajaxと呼ばれるこのことについてすべてを学ぶ準備ができています。しかし、それは正確には何ですか? Ajaxという用語は、動的でインタラクティブなWebコンテンツを作成するために使用されるテクノロジーのゆるいグループ化を指します。 Ajaxという用語は、もともとJesse Jによって造られました

記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

この記事では、ブラウザ開発者ツールを使用した効果的なJavaScriptデバッグについて説明し、ブレークポイントの設定、コンソールの使用、パフォーマンスの分析に焦点を当てています。

マトリックスの映画効果をあなたのページにもたらしましょう!これは、有名な映画「The Matrix」に基づいたクールなJQueryプラグインです。プラグインは、映画の古典的な緑色のキャラクター効果をシミュレートし、画像を選択するだけで、プラグインはそれを数値文字で満たされたマトリックススタイルの画像に変換します。来て、それを試してみてください、それはとても面白いです! それがどのように機能するか プラグインは画像をキャンバスにロードし、ピクセルと色の値を読み取ります。 data = ctx.getimagedata(x、y、settings.greasize、settings.greasize).data プラグインは、写真の長方形の領域を巧みに読み取り、jQueryを使用して各領域の平均色を計算します。次に、使用します

この記事では、jQueryライブラリを使用してシンプルな画像カルーセルを作成するように導きます。 jQuery上に構築されたBXSLiderライブラリを使用し、カルーセルをセットアップするために多くの構成オプションを提供します。 今日、絵のカルーセルはウェブサイトで必須の機能になっています - 1つの写真は千の言葉よりも優れています! 画像カルーセルを使用することを決定した後、次の質問はそれを作成する方法です。まず、高品質の高解像度の写真を収集する必要があります。 次に、HTMLとJavaScriptコードを使用して画像カルーセルを作成する必要があります。ウェブ上には、さまざまな方法でカルーセルを作成するのに役立つ多くのライブラリがあります。オープンソースBXSLiderライブラリを使用します。 BXSLiderライブラリはレスポンシブデザインをサポートしているため、このライブラリで構築されたカルーセルは任意のものに適合させることができます

データセットは、APIモデルとさまざまなビジネスプロセスの構築に非常に不可欠です。これが、CSVのインポートとエクスポートが頻繁に必要な機能である理由です。このチュートリアルでは、Angular内でCSVファイルをダウンロードおよびインポートする方法を学びます


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

WebStorm Mac版
便利なJavaScript開発ツール

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

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

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

ホットトピック



