Vueデータ双方向バインディング実装方法

Vueデータ双方向バインディング実装方法

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 つのステップで実行します。

  • ステップ 1: データ内のデータをページに同期して、M ==> V の初期化を実現します。
  • ステップ 2: 入力ボックスに値が入力されると、新しい値がデータと同期され、V ==> M のバインディングが実現されます。
  • ステップ 3: データが更新されると、ページの変更がトリガーされ、M ==> V のバインディングが実現されます。

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 要素をどのように解析するのでしょうか?考え方は次のとおりです。

  • まず、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 を使用して、データが更新されたときにアクションをトリガーします。このアクションの主な目的は、データを監視したすべてのオブザーバーが更新メソッドを実行できるようにすることです。

要約すると、このセクションで必要なことは次のとおりです。

  1. Watcher クラスを実装します。
  2. 命令を解析するときにオブザーバーを追加します(つまり、コンパイル メソッド内)。
  3. データハイジャックを実装します(観察メソッドを実装します)。

完全なコードは次のとおりです。

  クラス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の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Vue2.0/3.0双方向データバインディングの実装原理の詳細説明
  • vuexの強制リフレッシュによるデータ損失問題の分析
  • Vue はデータの変更をどのように追跡しますか?
  • Vue+canvas は、ウォーターフォール チャートを上から下までリアルタイムに更新する効果を実現します (QT と同様)
  • Vueデータ割り当て問題の解決
  • Vueはデータを初期状態にリセットします
  • Vue コンポーネント値転送中のデータ損失の分析と解決
  • SpringBoot+Vueでデータ追加機能を実現
  • 手書きの Vue2.0 データハイジャックの例
  • Vueでデータを読み取るためにこれを悪用しないでください
  • Vue でデータコレクターを設計する

<<:  Linuxにおけるselinuxの基本設定チュートリアルの詳細な説明

>>:  MySQL でローカル ユーザーを作成し、データベース権限を付与する方法の例

推薦する

CSS 命名: BEM、スコープ付き CSS、CSS モジュール、CSS-in-JS の説明

CSS の適用範囲はグローバルです。プロジェクトがどんどん大きくなり、参加する人が増えるにつれて、命...

MySQL の on と where における左結合設定条件の使用法の違いの分析

この記事では、MySQL の左結合における on 条件と where 条件の使用法の違いを例を使って...

JavaScriptのアロー関数の特徴と通常の関数との違い

目次1. 矢印関数の使用1. 通常関数から矢印関数へ2. 中括弧を省略してリターンする3. 括弧を省...

MySQL ストアド プロシージャの作成と呼び出しの詳細な説明

目次序文ストアドプロシージャ: 1. ストアドプロシージャの作成と呼び出し1. ストアドプロシージャ...

JavaScript の 7 つのデータ型の詳細な説明

目次序文:詳しい紹介:練習する:要約する序文: Python、Java、Cシリーズなど、すべての主要...

Nginx リバース プロキシを使用してクロスドメイン問題を解決する方法の詳細な説明

質問前回のクロスドメイン リソース共有に関する記事では、ドメイン間で Cookie を送信する場合、...

Vue プロジェクトでの支払い機能の実装 (WeChat 支払いと Alipay 支払い)

目次プロジェクトにおける一般的な支払い方法Alipay決済微信ペイプロジェクトにおける一般的な支払い...

Vue Element-ui フォーム検証ルールの実装

目次1. はじめに2. ルール検証の入力モード2.1 サンプルコード2.2、フォーム項目2.3. 小...

Vuex のコアコンセプトと基本的な使用法の詳細な説明

目次導入始めるインストール①直接ダウンロードする方法②CND法③NPM方式④糸法NPMインストールの...

ウェブサイトのビジュアルデザインの重要なポイント

手工芸デザインからグラフィックデザイン、そしてウェブデザインまで、デザインの原則は同じままですが、私...

WeChatアプレットタブの左右スライドスイッチ機能実装コード

効果画像: 1. はじめに独自のアプレットでこのような機能を実装する必要がある1. 核となる考え方ス...

Vueはシンプルなショッピングカートの例を実装します

この記事では、参考までに、Vue の具体的なコードを共有して、簡単なショッピングカートを実装します。...

フォアマン Ubuntu16 クイックインストール

クイックスタートガイドForeman インストーラーは、完全に機能する Foreman セットアップ...

Vue+swiperでタイムライン効果を実現

この記事では、タイムライン効果を実現するためのvue+swiperの具体的なコードを参考までに共有し...