1. はじめにこの記事は、Vue ソースコードを学習している初心者に適しています。これを読めば、Vue における双方向データバインディングの原理を大まかに理解でき、Observer、Compile、Wathcer の 3 つの主要な役割 (下図参照) とその機能を理解できます。 この記事では、双方向データ バインディングのシンプルなバージョンを実装する手順を段階的に説明します。各ステップでは、そのステップで解決する問題と、コードがこのように記述されている理由を詳細に分析します。したがって、この記事を読んだ後、双方向データ バインディングのシンプルなバージョンを自分で実装できるようになることを願っています。 2. コードの実装2.1 目的分析この記事で達成される効果は次の図に示されています。 この記事で使用されている HTML と JS の主なコードは次のとおりです。 <div id="アプリ"> <h1 v-text="メッセージ"></h1> <input type="text" v-model="msg"> <div> <h1 v-text="msg2"></h1> <input type="text" v-model="msg2"> </div> </div> vm = new Vue({ el: "#app", データ: { メッセージ: "こんにちは世界", メッセージ2: 「こんにちは、シャオフェイ」 } }) これを 3 つのステップで実行します。
2.2 実装プロセス2.2.1 エントリーコードまず、オプション オブジェクトを受け取る Vue クラスを作成する必要があります。同時に、オプション オブジェクトに有効な情報を保存する必要があります。 次に、Observer、Compile、Wathcer という 3 つの主要モジュールがあります。このうち、Observer はデータ ハイジャックに使用され、Compile は要素の解析に使用され、Wathcer はオブザーバーです。次のようなコードを記述できます。(Observer、Compile、Wathcer の 3 つの概念については後で詳しく説明するので、詳しく学習する必要はありません)。 クラスVue { // 渡されたオブジェクトを受け取るconstructor(options) { // 有効な情報を保存します this.$el = document.querySelector(options.el); this.$data = オプション.data; //コンテナー: {属性 1: [wathcer1、wathcer2...]、属性 2: [...]}、各属性オブザーバーを格納するために使用されます this.$watcher = {}; // 要素の解析: コンパイルの実装 this.compile(this.$el); // 要素を解析するには、要素を渡す必要があります // データのハイジャック: Observer の実装 this.observe(this.$data); // データをハイジャックするには、データを渡す必要があります} コンパイル() {} 観察する() {} } 2.2.2 ページの初期化このステップでは、ページを初期化する必要があります。つまり、v-text および v-model 命令を解析し、data 内のデータをページにレンダリングする必要があります。 このステップの鍵は、compile メソッドを実装することです。では、el 要素をどのように解析するのでしょうか?考え方は次のとおりです。
コードは次のとおりです: (主にコンパイル部分を見てください) クラスVue { // 渡されたオブジェクトを受け取るconstructor(options) { // 役に立つ情報を取得します this.$el = document.querySelector(options.el); this.$data = オプション.data; // コンテナ: {attribute1: [wathcer1, wathcer2...], attribute2: [...]} this.$watcher = {}; // 2. 要素の解析: コンパイルの実装 this.compile(this.$el); // 要素を解析するには、要素を渡す必要があります // 3. データのハイジャック: Observer の実装 this.observe(this.$data); // データをハイジャックするには、データを渡す必要があります} コンパイル(el) { // 要素の下の各子ノードを解析し、el.children を取得します。 // 注意: children は要素セットを返し、childNodes はノードセットを返します。let nodes = el.children; // 各子ノードの命令を解析します for (var i = 0, length = nodes.length; i < length; i++) { ノードをノード[i]とします。 // 現在のノードに子要素がある場合は、ノードを再帰的に解析します if(node.children){ これをコンパイルします(ノード); } // v-textディレクティブを持つ要素を解析します if (node.hasAttribute("v-text")) { attrVal = node.getAttribute("v-text"); とします。 node.textContent = this.$data[attrVal]; // ページをレンダリングする} // v-model ディレクティブを持つ要素を解析します if (node.hasAttribute("v-model")) { attrVal = node.getAttribute("v-model"); とします。 ノードの値 = this.$data[attrVal]; } } } 観察(データ) {} } このようにして、ページの初期化が実現しました。 2.2.3 ビューはデータに影響を与えるinput には v-model 命令があるため、入力ボックスに文字が入力されると、それに応じて data にバインドされたデータが変更されるような関数を実装する必要があります。 入力イベントを入力要素にバインドできます。イベントの効果は、データ内の対応するデータを入力の値に変更することです。 この部分の実装コードは比較的単純です。マークした箇所を見れば理解できます。コードは以下のとおりです。 クラスVue { コンストラクタ(オプション) { this.$el = document.querySelector(options.el); this.$data = オプション.data; this.$watcher = {}; this.compile(this.$el); this.observe(this.$data); } コンパイル(el) { ノードを el.children とします。 (var i = 0、長さ = nodes.length; i < length; i++) { ノードをノード[i]とします。 if(ノード.children){ これをコンパイルします(ノード); } ノードが属性を持っている場合("v-text") attrVal = node.getAttribute("v-text"); とします。 node.textContent = this.$data[attrVal]; } ノードが属性を持っている場合("v-model") attrVal = node.getAttribute("v-model"); とします。 ノードの値 = this.$data[attrVal]; // ここを見て! !あとコードは3行だけです! ! node.addEventListener("入力", (ev)=>{ this.$data[attrVal] = ev.target.value; // ここで実行してみてください: console.log(this.$data), // 入力ボックスにテキストを入力するたびに、データ内の msg 値も変化することがわかります}) } } } 観察(データ) {} } 2.2.4 データ影響ビューこれまでのところ、入力ボックスに文字を入力すると、データ内のデータが自動的に更新されることを実現しました。 このセクションの主なタスクは、データ内のデータが更新されると、データにバインドされた要素がページ上のビューを自動的に更新することです。具体的なアイデアは以下の通りです。 1) ページを更新するための更新メソッドを持つ Watcher クラスを実装します。オブザーバーコードは次のとおりです。 クラスウォッチャー{ コンストラクター(ノード、updatedAttr、vm、式){ //渡された値を保存します。これらの値はページをレンダリングするときに使用されます。this.node = node; this.updatedAttr = 更新されたAttr; this.vm = vm; this.expression = 式; これを更新します。 } アップデート(){ this.node[this.updatedAttr] = this.vm.$data[this.expression]; } } 2) 考えてみましょう。どのデータにオブザーバーを追加すればよいでしょうか?データにオブザーバーを追加するタイミングはいつですか? 要素を解析するときに、v-text および v-model 命令が解析されると、この要素は両方向でデータにバインドされる必要があることを意味するため、この時点でコンテナーにオブザーバーを追加します。次のようなデータ構造を使用する必要があります: {属性 1: [wathcer1, wathcer2...], 属性 2: [...]}。わかりにくい場合は、次の図を参照してください。 vue インスタンスに $watcher オブジェクトがあることがわかります。$watcher の各属性はバインドする必要がある各データに対応しており、値はデータを監視したオブザーバーを格納するために使用される配列です。 (注: Vue のソースコードでは、ここで言及した配列に対応する Dep というクラスが具体的に作成されます。この記事は簡易版なので、詳しくは紹介しません。) 3) データのハイジャック: オブジェクトのアクセサー プロパティの getter と setter を使用して、データが更新されたときにアクションをトリガーします。このアクションの主な目的は、データを監視したすべてのオブザーバーが更新メソッドを実行できるようにすることです。 要約すると、このセクションで必要なことは次のとおりです。
完全なコードは次のとおりです。 クラスVue { // 渡されたオブジェクトを受け取るconstructor(options) { // 役に立つ情報を取得します this.$el = document.querySelector(options.el); this.$data = オプション.data; // コンテナ: {attribute1: [wathcer1, wathcer2...], attribute2: [...]} this.$watcher = {}; // 要素の解析: コンパイルの実装 this.compile(this.$el); // 要素を解析するには、要素を渡す必要があります // データのハイジャック: Observer の実装 this.observe(this.$data); // データをハイジャックするには、データを渡す必要があります} コンパイル(el) { // 要素の下の各子ノードを解析し、el.children を取得します。 // 拡張: children は要素セットを返し、childNodes はノードセットを返します。let nodes = el.children; // 各子ノードの命令を解析します for (var i = 0, length = nodes.length; i < length; i++) { ノードをノード[i]とします。 // 現在のノードに子要素がある場合は、ノードを再帰的に解析します if (node.children) { これをコンパイルします(ノード); } ノードが属性を持っている場合("v-text") attrVal = node.getAttribute("v-text"); とします。 // node.textContent = this.$data[attrVal]; // ウォッチャーはインスタンス化時に update を呼び出し、このコード行を置き換えます/** * ノード データを更新するときに Watchcer が使用する必要があるデータを想像してみてください。 * egpinnerHTML = vm.$data[msg] * 渡されるパラメータは、現在のノード、更新されるノード属性、vue インスタンス、バインドされたデータ属性です。*/ // コンテナにオブザーバーを追加します: {msg1: [Watcher, Watcher...], msg2: [...]} if (!this.$watcher[attrVal]) { this.$watcher[attrVal] = []; } this.$watcher[attrVal].push(新しいウォッチャー(ノード、"innerHTML"、this、attrVal)) } ノードが属性を持っている場合("v-model") attrVal = node.getAttribute("v-model"); とします。 ノードの値 = this.$data[attrVal]; node.addEventListener("入力", (ev) => { this.$data[attrVal] = ev.target.value; }) if (!this.$watcher[attrVal]) { this.$watcher[attrVal] = []; } // 上で使用した innerHTML とは異なり、ここでの入力は値属性を使用します this.$watcher[attrVal].push(new Watcher(node, "value", this, attrVal)) } } } 観察(データ) { Object.keys(data).forEach((key) => { let val = data[key]; // このvalは常にメモリに保存されます。data[key]にアクセスするたびに、このvalにアクセスします Object.defineProperty(データ、キー、{ 得る() { return val; // ここで直接 data[key] を返すことはできません。そうしないと無限ループに陥ります}, set(newVal) { val !== newVal の場合 { val = newVal; // 同様に、data[key] をここで直接設定することはできないため、無限ループが発生します this.$watcher[key].forEach((w) => { w.update(); }) } } }) }) } } クラスウォッチャー{ コンストラクター(ノード、updatedAttr、vm、式) { //渡された値を保存します。this.node = node; this.updatedAttr = 更新されたAttr; this.vm = vm; this.expression = 式; これを更新します。 } アップデート() { this.node[this.updatedAttr] = this.vm.$data[this.expression]; } } vm = new Vue({ el: "#app", データ: { メッセージ: "こんにちは世界", メッセージ2: 「こんにちは、シャオフェイ」 } }) この時点でコードは完成です。 3. 今後の予定デザイン パターンの知識を使用して、上記のソース コードの問題を分析し、それを Vue ソース コードと比較します。これは、Vue ソース コードの分析と見なされます。 以上がVueデータ双方向バインディングの実装方法の詳細な内容です。Vueデータ双方向バインディングの詳細については、123WORDPRESS.COMの他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: Linuxにおけるselinuxの基本設定チュートリアルの詳細な説明
>>: MySQL でローカル ユーザーを作成し、データベース権限を付与する方法の例
CSS の適用範囲はグローバルです。プロジェクトがどんどん大きくなり、参加する人が増えるにつれて、命...
この記事では、MySQL の左結合における on 条件と where 条件の使用法の違いを例を使って...
目次1. 矢印関数の使用1. 通常関数から矢印関数へ2. 中括弧を省略してリターンする3. 括弧を省...
目次序文ストアドプロシージャ: 1. ストアドプロシージャの作成と呼び出し1. ストアドプロシージャ...
目次序文:詳しい紹介:練習する:要約する序文: Python、Java、Cシリーズなど、すべての主要...
質問前回のクロスドメイン リソース共有に関する記事では、ドメイン間で Cookie を送信する場合、...
type="radio" や type="checkbox"...
目次プロジェクトにおける一般的な支払い方法Alipay決済微信ペイプロジェクトにおける一般的な支払い...
目次1. はじめに2. ルール検証の入力モード2.1 サンプルコード2.2、フォーム項目2.3. 小...
目次導入始めるインストール①直接ダウンロードする方法②CND法③NPM方式④糸法NPMインストールの...
手工芸デザインからグラフィックデザイン、そしてウェブデザインまで、デザインの原則は同じままですが、私...
効果画像: 1. はじめに独自のアプレットでこのような機能を実装する必要がある1. 核となる考え方ス...
この記事では、参考までに、Vue の具体的なコードを共有して、簡単なショッピングカートを実装します。...
クイックスタートガイドForeman インストーラーは、完全に機能する Foreman セットアップ...
この記事では、タイムライン効果を実現するためのvue+swiperの具体的なコードを参考までに共有し...