Vueの使用に関する深い理解

Vueの使用に関する深い理解

Vueのコアコンセプトを理解する

Vue を使用すると、人々は心身ともに幸せになります。Vue には Angular と React の両方の利点があります。軽量で、シンプルな API、完全なドキュメント、シンプルで強力、すべてが揃っています。

Vue を一文で要約するとしたら、まず頭に浮かぶのは公式ドキュメントの一文です。

Vue.js (発音は /vjuː/、view に似ています) は、ユーザー インターフェイスを構築するためのプログレッシブ フレームワークです。

この文章は誰もが知っているかもしれませんが、実際に理解している人は多くありません。実際、この文章を理解すれば、Vue のコアコンセプトを理解できます。

では、プログレッシブ フレームワークとは何なのか、どのように理解すればよいのでしょうか。その前に、まずフレームワークとは何かを理解する必要があります。初期のフロントエンド開発では、特定の機能を完成させるために、HTML ページ内の DOM ノードを JS 経由で取得し、DOM ノード内のテキスト コンテンツを取得したり、DOM ノードにイベントを追加したりして、一連のプログラム操作を実行する必要があります。ただし、タスク量が多いと、業務が増えるにつれてコードが肥大化し、混乱が生じます。実際の開発では、複雑なロジックと膨大な開発量をネイティブ JS で完成させることはできません。

このとき、開発者は js コードをデータ (Model)、ロジック コントロール (*)、ビュー (View) の 3 つのセクションに分割します。データ セクションはデータ部分のみを担当し、ビュー セクションはスタイルの変更を担当し、ロジック コントロールはビュー セクションとデータ セクションの接続を担当します。これには大きな利点があります。要件が変更された場合は、対応するセクションのみを変更する必要があります。

この開発モデルは、いわゆる MV* 構造です。現在知られている MVC、MVP、MVVM はすべて MV* から派生したものです。これらのフレームワーク モデルを比較すると、これらの開発モデルではビューとデータが直接接触しないようにするという重要な特徴をまとめることができます。ネイティブ JS で DOM を取得する操作と比較すると、ネイティブ DOM ストリームは実際には DOM をデータとして使用し、DOM からモデルを取得し、DOM を変更してビューを更新していることがわかります。ビューとモデルは実際には混在しているため、コードは当然乱雑になり、メンテナンスが困難になります。

レスポンシブなシステムを持つ Vue インスタンスでは、DOM 状態はデータ状態のマッピングに過ぎず、つまり UI=VM(State) となります。式の右側の State が変化すると、ページ表示部分の UI もそれに応じて変化します。そのため、多くの人が初めて Vue を使い始めたときに、非常に使いやすいと感じるのです。しかし、Vue のコアの位置付けはフレームワークではなく、その設計は MVVM モデルに完全に従っていません。図を見ると、State と View の 2 つの部分しかないことがわかります。Vue のコア機能は、状態とインターフェースのマッピングを重視しており、コードの構造構成には注意を払っていません。したがって、コア機能だけを使用する場合、それはフレームワークではなく、ビュー テンプレート エンジンのようなものです。これが、Vue 開発者がビューに似た発音の「view」と名付けた理由です。

前述のように、Vue のコア機能はビューテンプレートエンジンですが、これは Vue がフレームワークになれないことを意味するものではありません。下図の通り、Vue のコンポーネントがすべてここに含まれています。宣言型レンダリング (ビュー テンプレート エンジン) をベースに、コンポーネント システム、クライアント ルーティング、大規模な状態管理を追加することで、完全なフレームワークを構築できます。さらに重要なのは、これらの機能は互いに独立していることです。すべてを統合する必要はなく、コア機能に基づいて他のコンポーネントを選択できます。ご覧のとおり、いわゆる「プログレッシブ」は実際に Vue を使用する方法であり、Vue の設計コンセプトも反映しています。

Vueの双方向バインディングの原理と実装を探る

結果

以下の2つのコンテンツを紹介します

1. Vue双方向バインディングの原理

2.宣言的なデータレンダリングといくつかの簡単な指示を含む、Vueの簡易版を実装するプロセス

Vue 双方向バインディング原則

Vue の双方向バインディングは、データ ハイジャックとパブリッシャー サブスクライバー モデルを組み合わせることで実現されますが、データ ハイジャックとは何でしょうか? Vue はどのようにしてデータをハイジャックするのでしょうか?簡単に言えば、Object.defineProperty() を通じてオブジェクト プロパティの setter および getter 操作を乗っ取り、データが変更されたときに実行したい操作を実行することです。コンソール コームを通じて、vue 初期化データで定義されたオブジェクトが何であるかを確認できます。

var vm = 新しい Vue({
    データ: {
        テスト : {
            1: 1
        }
    },
    作成: 関数 () {
        コンソールにログ出力します。
    }
});

結果を印刷:

印刷結果を見ると、属性 a に get と set の 2 つのメソッドがあることがわかります。なぜこの 2 つのメソッドがあるのでしょうか。これはまさに、Vue が Object.defineProperty() を通じてデータをハイジャックする方法です。

Object.defineProperty() メソッドは何を行いますか?文書にはこう書いてある

簡単に言えば、読み取りや書き込みの権限、列挙可能かどうかなど、オブジェクトのプロパティのいくつかの固有の操作を制御できます。ここでは主に get メソッドと set メソッドについて学習します。使用方法について詳しく知りたい場合は、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty を参照してください。

オブジェクトの属性データを簡単に出力できます。

var ブック = {
  名前: 「人間の弱さ」
};
console.log(Book.name); // 人間の弱点

しかし、console.log(Book.name) の実行中に書籍のタイトルに引用符を追加したい場合はどうすればよいでしょうか? Object.defineProperty() を使用する必要があります。

//console.log(book.name) と同時に、book に書籍番号を直接追加します。var Book = {};
var name = '';
Object.defineProperty(Book,'name',{
    設定:関数(値) {
        名前 = 値;
        console.log('書籍名を選択しました:' + 値);
    },
    取得:関数() {
        console.log('getメソッドが監視されています');
        '<'+name+'>' を返します。
    }
});
Book.name = 'Weakness of Human Nature'; //本に名前を付けました: Weakness of Human Natureconsole.log(Book.name); //<Weakness of Human Nature>

Book オブジェクトの name プロパティは Object.defineProperty() メソッドを通じて設定され、その get メソッドと set メソッドが書き換えられます。name プロパティを取得するときに get メソッドが呼び出され、name プロパティを設定するときに set メソッドがトリガーされます。したがって、ステートメント Book.name = 'Weakness of Human Nature' を実行すると、set メソッドが呼び出され、選択した書籍のタイトル「Weakness of Human Nature」が出力されます。 console.log(Book.name) が呼び出されると、get メソッドがトリガーされ、出力は「Weakness of Human Nature」になります。この文をコードに追加すると、何が印刷されるでしょうか?

console.log(本)

結果は次のとおりです。

これは、上記の Vue 印刷データとの比較と非常によく似ており、Vue がこのようにデータをハイジャックしていることがわかります。では、パブリッシャー-サブスクライバー パターンとは何でしょうか? ?

サブスクライバー パターンとパブリッシャー パターンは通常、メッセージ キューで使用されます。メッセージ キューを実装する方法は、一般的に 2 つあります。1 つはプロデューサーとコンシューマーを使用する方法で、もう 1 つはサブスクライバー/パブリッシャー モデルを使用する方法です。サブスクライバーとパブリッシャーがメッセージ キューを実装する方法は、サブスクライバー モデルを使用します。

たとえば、いわゆる購読者は、私たちが日常生活で新聞を購読するときと同じです。新聞を購読する場合、通常は新聞社または仲介機関に登録する必要があります。新聞の新しい版が発行されると、郵便配達員は新聞を購読している人に順番に新聞を配布する必要があります。

いわゆる購読者は、私たちが日常生活で新聞を購読するのと同じです。新聞を購読する場合、通常は新聞社または何らかの仲介機関に登録する必要があります。新聞の新版が発行されると、郵便配達員は新聞を購読している人にその新聞を配布する必要があります。

このパターンをコードに実装する場合は、次の 2 つの手順を実行する必要があります。

1. パブリッシャーとサブスクライバーを初期化します。

2. サブスクライバーはパブリッシャーに登録する必要があります。パブリッシャーがメッセージをパブリッシュすると、そのメッセージがサブスクライバーに順番にパブリッシュされます。

加入者登録

発行者がメッセージを公開する

次に、Vue原則を通じて簡単なMVVM双方向バインディングデモを実装します。

思考分析

mvvm を実装するには、ビューが変更されたときにデータを更新することと、データが変更されたときにビューを更新することという 2 つの主要な側面があります。

実際、ビューが変更されたときにデータを更新することは、入力イベントをリッスンする入力タグなどのイベント監視を通じて実現できるため、ビューを更新するためのデータの変更を分析することに重点を置いています。

データが変更されたときにビューを更新するための鍵は、ビューがいつ変更されたかを知ることです。ビューがいつ変更されたかがわかっていれば、残りは簡単に処理できます。このとき、前述の Object.defineProperty() が役立ちます。 Object.defineProperty() を通じて、プロパティに set 関数が設定されます。この関数はプロパティが変更されたときにトリガーされるため、データが変更されたときにビューを更新するために、set 関数にいくつかの更新メソッドを配置するだけで済みます。

実装プロセス

データの双方向バインディングを実装する方法はすでにわかっているので、まずデータをハイジャックして監視する必要があります。そのためには、まずすべてのプロパティを監視するリスナー Observer を設定する必要があります。プロパティが変更された場合は、サブスクライバー Watcher に通知して、更新が必要かどうかを確認する必要があります。属性が複数存在する場合、サブスクライバーも複数存在するため、これらのサブスクライバーを具体的に収集し、リスナー Observer とサブスクライバー Watcher の間で統一された管理を実行するには、メッセージ サブスクライバー Dep が必要です。ノード要素にはいくつかの命令がある可能性があるため、各ノード要素をスキャンして解析し、関連する命令をサブスクライバーウォッチャーに初期化し、テンプレートデータを置き換えて対応する関数をバインドする命令パーサーコンパイルも必要です。このとき、サブスクライバーウォッチャーは対応する属性の変更を受信すると、対応する更新関数を実行してビューを更新します。

上記のアイデアを整理するには、双方向バインディングを完了するための 3 つの手順を実装する必要があります。

1. すべてのプロパティをハイジャックして監視し、変更があった場合にサブスクライバーに通知するオブザーバーを実装します。

2. プロパティの変更の通知を受信し、対応する関数を実行してビューを更新できるサブスクライバー ウォッチャーを実装します。

3. 各ノードの関連命令をスキャンして解析し、初期化テンプレート データに基づいて対応するサブスクライバーを初期化できるパーサー Compile を実装します。

フローチャートは次のとおりです。

1. リスナーオブザーバーを実装する

データ リスナーのコア メソッドは Object.defineProperty() です。これは、ループをトラバースして Object.defineProperty() を実行することで、すべてのプロパティ値をリッスンします。コードは次のように記述できます。

//すべての属性について、すべての属性を再帰的に走査します。function defineReactive(data,key,val) {
    observe(val); //すべてのプロパティを再帰的に走査する Object.defineProperty(data,key,{
        enumerable:true, // プロパティ記述子は、プロパティの構成可能値が true の場合にのみ変更でき、対応するオブジェクトからプロパティを削除することもできます。
        configurable:true, //このプロパティは、このプロパティの列挙型が true の場合にのみ、オブジェクトの列挙プロパティに表示できます。get:function() {
            戻り値:
        },
        設定:関数(newVal) {
            val = 新しいVal;
            console.log('プロパティ '+key+' が監視されており、現在の値は "'+newVal.toString()+'" です');
        }
    })
}

関数 observe(データ) {
    if(!data || typeof data !== 'object') {
        戻る;
    }
    Object.keys(データ).forEach(関数(キー){
        Reactive を定義します (データ、キー、データ[キー])。
    });
}

var ライブラリ = {
    本1: {
        名前: ''
    },
    本2: ''
};
観察(ライブラリ);
library.book1.name = 'vue authoritative guide'; // プロパティ名は監視されており、現在の値は「vue authoritative guide」です
library.book2 = 'そのような本はありません'; // 属性 book2 は監視されており、現在の値は「そのような本はありません」です

observe() メソッドを使用して下方向にトラバースし、すべての属性を見つけ、defineReactive() メソッドを使用してデータハイジャック監視を実行します。

上記のアイデアでは、メッセージ サブスクライバーを収容できるメッセージ サブスクライバー Dep が必要です。サブスクライバーは主にメッセージ サブスクライバーを収集し、属性が変更されたときに対応するサブスクライバーの更新機能を実行します。したがって、メッセージ サブスクライバー Dep には、メッセージ サブスクライバーを格納するコンテナーが必要です。上記のリスナー Observer を少し変更します。

関数defineReactive(データ,キー,値) {
    観察(val);
    var dep = 新しい Dep();
    Object.defineProperty(データ、キー、{
        列挙可能: true、
        設定可能: true、
        取得: 関数() {
            if (サブスクライバーを追加する必要がありますか) { //ウォッチャー初期化トリガー dep.addSub(watcher); // ここでサブスクライバーを追加します}
            戻り値:
        },
        設定: 関数(newVal) {
            場合 (val === newVal) {
                戻る;
            }
            val = 新しいVal;
            console.log('property' + key + ' が監視されており、現在の値は次のとおりです: "' + newVal.toString() + '"');
            dep.notify(); // データが変更された場合は、すべてのサブスクライバーに通知します}
    });
}

関数 observe(データ) {
    if(!data || typeof data !== 'object') {
        戻る;
    }
    Object.keys(データ).forEach(関数(キー){
        Reactive を定義します (データ、キー、データ[キー])。
    });
}

関数Dep() {
    this.subs = [];
}

//プロトタイプ プロパティを使用すると、オブジェクトにプロパティとメソッドを追加できます。 //プロトタイプ プロパティは関数オブジェクト、特にコンストラクターでのみ使用できます。関数オブジェクトを宣言している限り、プロトタイプは存在します。 //オブジェクト インスタンスにはこのプロパティはありません。 Dep.prototype = {                        
    サブ関数を追加します。
        this.subs.push(sub);
    },
    通知:関数() {
        this.subs.forEach(関数(sub) {
            sub.update(); //各サブスクライバーに更新を確認するよう通知する})
    }
}
依存関係ターゲット = null;

コードでは、サブスクライバー Dep in get にサブスクライバー設計を追加します。これは、初期化時に Watcher をトリガーし、サブスクライバーを追加する必要があるかどうかを判断するためです。具体的な実装方法については、以下で詳しく説明します。 set メソッドでは、関数が変更されると、すべてのサブスクライバーに通知され、サブスクライバーは対応する更新関数を実行します。ここまでで、比較的完全な Observer が形成されました。次に、サブスクライバー Watcher を記述する必要があります。

2.サブスクライバーウォッチャーの実装

私たちのアイデアによれば、サブスクライバー Wahcher は初期化中にサブスクライバー Dep に自身を追加する必要があるのですが、どのように追加するのでしょうか?

リスナー Observer が get 関数でサブスクライバーを追加する操作を実行することは既にわかっているので、サブスクライバー Watcher が初期化されてサブスクライバーを追加する操作を実行するときに、対応する get 関数をトリガーするだけで済みます。では、対応する get 関数をどのようにトリガーするのでしょうか?対応するプロパティ値を取得するだけでよく、Object.defineProperty() を通じて対応する取得をトリガーできます。

ここで注意すべき点が 1 つあります。サブスクライバーを追加するのは、サブスクライバーが初期化されたときだけなので、サブスクライバーを Dep.target にキャッシュし、正常に追加された後に削除する判断が必要です。コードは次のとおりです。

関数ウォッチャー(vm,exp,cb) {
    this.vm = vm; //SelfVue のスコープを指す this.exp = exp; //バインディング プロパティのキー値 this.cb = cb; //クロージャ this.value = this.get();
}

ウォッチャー.プロトタイプ = {
    更新:関数() {
        これを実行してください。
    },
    実行:関数() {
        var 値 = this.vm.data[this.exp];
        var oldVal = this.value;
        if(値 !== oldVal) {
            this.value = 値;
            this.cb.call(this.vm,値,oldVal);
        }
    },
    取得:関数() {
        Dep.target = this; // 自分自身をキャッシュします var value = this.vm.data[this.exp]; // リスナーで get 関数の実行を強制します Dep.target = null; // 自分自身を解放します return value;
    }
}

この時点で、リスナー Observer の defineReactive() に若干の調整を加える必要があります。

関数defineReactive(データ,キー,値) {
    観察(val);
    var dep = 新しい Dep();
    Object.defineProperty(データ、キー、{
        列挙可能: true、
        設定可能: true、
        取得: 関数() {
            if(Dep.target) { // サブスクライバーを追加するかどうかを判断します dep.addSub(Dep.target);
            }
            戻り値:
        },
        設定: 関数(newVal) {
            場合 (val === newVal) {
                戻る;
            }
            val = 新しいVal;
            console.log('property' + key + ' が監視されており、現在の値は次のとおりです: "' + newVal.toString() + '"');
            dep.notify(); // データが変更された場合は、すべてのサブスクライバーに通知します}
    });
}

これまでのところ、Watcher の簡略化されたバージョンが形成されました。単純な双方向バインディングを実現するには、サブスクライバー Watcher をリスナー Observer に関連付けるだけです。ここでは命令パーサーが設計されていないため、テンプレートデータをハードコードします。テンプレートにノード要素があり、id が「name」で、双方向バインディングのバインディング変数も「name」であり、2 つの大きな二重括弧で囲まれているとします (これは今のところ役に立ちません)。テンプレートコードは次のとおりです。

<本文>
    <h1 id="名前">{{名前}}</h1>
</本文>

オブザーバーとウォッチャーの関連付けを実現するには、SelfVue クラスを定義する必要があります。コードは次のとおりです。

//オブザーバーとウォッチャーを関連付ける function SelfVue(data,el,exp) {
    this.data = データ;
    観察(データ);
    el.innerHTML = this.data[exp];
    新しいウォッチャー(this,exp,function(value) {
        el.innerHTML = 値;
    });
    これを返します。
}

次に、双方向バインディングを実現するために、ページ上に新しい SelfVue を作成します。

<本文>
    <h1 id="名前"{{名前}}></h1>
</本文>

<script src="../js/observer.js"></script>
<script src="../js/Watcher.js"></script>
<script src="../js/SelfVue.js"></script>

<スクリプト>
     var ele = document.querySelector('#name');
     var selfVue = 新しいSelfVue({
         名前: 'hello world'
     },ele,'名前');

     ウィンドウ.setTimeout(関数() {
         console.log('名前の値が変更されました');
         selfVue.name = 'さようなら世界';
     },2000);
</スクリプト>

ページを開くと、「hello world」と表示され、2 秒後に「byebye world」に変わります。シンプルな双方向バインディングが実現されます。

vue と比較すると、問題が見つかりました。属性に値を割り当てるときの形式は、「selfVue.data.name = 'byebye world''」ですが、理想的な形式は、「selfVue.name = 'byebye world''」です。では、この形式を実現するにはどうすればよいでしょうか。SelfVue への属性アクセスが selfVue.data への属性アクセスに委任されるように、新しい SelfVue を作成するときにプロキシ処理を実行するだけで済みます。原則としては、Object.defineProperty() を使用して属性を 1 つのレイヤーにラップします。コードは次のとおりです。

関数SelfVue(データ,el,exp) {
    var self = this;
    this.data = データ;
    //Object.keys() メソッドは、指定されたオブジェクトの列挙可能なプロパティの配列を返します。Object.keys(data).forEach(function(key) {
        self.proxyKeys(key); // プロキシ属性をバインドする });
    観察(データ);
    el.innerHTML = this.data[exp]; // テンプレートデータの値を初期化する new Watcher(this,exp,function(value) {
        el.innerHTML = 値;
    });
    これを返します。
}

SelfVue.プロトタイプ = {
    proxyKeys:function(キー) {
        var self = this;
        Object.defineProperty(this,key,{
            列挙可能:false、
            設定可能:true、
            取得:関数proxyGetter() {
                self.data[キー]を返します。
            },
            設定:関数 proxySetter(newVal) {
                self.data[キー] = newVal;
            } 
        });
    }
}

このようにして、テンプレート データを希望の形式で変更できます。

3.命令パーサーの実装 コンパイル

上記の双方向バインディングのデモでは、DOM ノードはプロセス全体で解析されず、特定のノードが固定されてデータが置き換えられていることがわかりました。そのため、次に、解析とバインディングを行うパーサー コンパイルを実装し、パーサーの役割を分析する必要があります。実装手順は次のとおりです。

1. テンプレート命令を解析し、テンプレートデータを置き換え、ビューを初期化する

2. テンプレート命令に対応するノードに対応する更新関数をバインドし、対応するサブスクライバーを初期化する

テンプレートを解析するには、まず DOM 要素を取得し、次に DOM 要素の指示を含むノードを処理する必要があります。このプロセスは DOM 要素に対して操作するのが面倒なので、まずフラグメント フラグメントを作成し、解析する DOM 要素をフラグメント フラグメントに格納して処理します。

ノードToFragment:function(el) {
        varfragment = document.createDocumentFragment(); //createdocumentfragment() メソッドは、すべてのプロパティとメソッドを含む仮想ノード オブジェクトを作成します。
        var child = el.firstChild;
        while(子) {
            // Dom 要素をフラグメントに移動します。fragment.appendChild(child);
            子 = el.firstChild;
        }
        フラグメントを返します。
    }

次に、すべてのノードをトラバースし、命令を含むノードに対して特別な処理を実行する必要があります。ここでは、まず最も単純なケース、つまり '{{variable}}' 形式の命令のみを処理します。コードは次のとおりです。

//各ノードを走査し、関連する仕様を含むノードに対して特別な処理を実行する compileElement:function(el) {
        var childNodes = el.childNodes; //childNodes プロパティは、ノードの子ノード コレクションを NodeList オブジェクトとして返します。
        var self = this;
        //slice() メソッドは、既存の配列から選択された要素を返します。
        [].slice.call(childNodes).forEach(function(node) {
            var reg = /\{\{(.*)\}\}/;
            var text = node.textContent; //textContent プロパティは、指定されたノードのテキスト コンテンツを設定または返します。if(self.isTextNode(node) && reg.test(text)) { //{{}} 命令を満たしているかどうかを判断します。//exec() メソッドは、文字列内の正規表現の一致を取得するために使用されます。
                // 一致する結果を含む配列を返します。一致するものが見つからない場合、戻り値は null になります。
                自己.compileText(ノード、reg.exec(テキスト)[1]);
            }
            if(node.childNodes && node.childNodes.length) {
                self.compileElement(node); // 子ノードを再帰的に走査し続けます}
        });
    },
    compileText:function(node,exp) {
        var self = this;
        var initText = this.vm[exp];
        this.updateText(node,initText); // 初期化されたデータをビューに初期化します new Watcher(this.vm,exp,function(value) {
            自己更新テキスト(ノード、値)。
        });

    },
    updateText:function(ノード,値) {
        node.textContent = typeof value == 'undefined' ? '': value;
    },

最も外側のノードを取得したら、compileElement 関数を呼び出してすべての子ノードを判断します。ノードがテキスト ノードであり、{{}} フォーム命令のノードと一致する場合は、コンパイルを開始します。コンパイル プロセスでは、まず、上記の手順 1 に対応するビュー データを初期化する必要があります。次に、上記の手順 2 に対応する更新関数をバインドするサブスクライバーを生成する必要があります。これにより、命令の解析、初期化、コンパイルの 3 つのプロセスが完了し、パーサー コンパイルが正常に動作できるようになります。

パーサー Compile をリスナー Observer およびサブスクライバー Watcher に関連付けるには、クラス SelfVue 関数を再度変更する必要があります。

関数SelfVue(オプション) {
    var self = this;
    this.vm = これ;
    オプションデータ
    Object.keys(this.data).forEach(function(key) {
        self.proxyKeys(key); // プロキシ属性をバインドする });
    オプションのデータを観察します。
    新しいコンパイル(options.el、this.vm);
    これを返します。
}

変更後は、以前のように双方向バインディングに固定要素値を渡す必要がなくなりました。双方向バインディングには任意の変数に名前を付けることができます。

<本文>
    <div id="アプリ">
        <h1>{{タイトル}}</h1>
        <h2>{{名前}}</h2>
        <h3>{{コンテンツ}}</h3>
    </div>
</本文>
<script src="../js/observer2.js"></script>
<script src="../js/Watcher1.js"></script>
<script src="../js/compile1.js"></script>
<script src="../js/index3.js"></script>


<スクリプト>
    var selfVue = 新しいSelfVue({
        el:'#app',
        データ:{
            タイトル:'aaa',
            名前:'bbb',
            コンテンツ:'ccc'
        }
    });
    ウィンドウ.setTimeout(関数() {
        selfVue.title = 'ddd';
        selfVue.name = 'eee';
        selfVue.content = 'fff'
    },2000);
</スクリプト>

この時点で、双方向データバインディング機能は基本的に完成しました。次のステップは、より多くの命令の解析とコンパイルを改善することです。より多くの命令をどこで処理すればよいでしょうか? 答えは明らかです。上記の compileElement 関数を追加して他の命令ノードを判断し、そのすべての属性を走査して一致する命令属性があるかどうかを確認します。一致する命令属性がある場合は、解析してコンパイルします。 ここで、v-model ディレクティブとイベント ディレクティブの解析とコンパイルを追加します。これらのノードについては、compile 関数を使用して解析および処理します。

コンパイル:関数(ノード) {
        var nodeAttrs = node.attributes; //attributes 属性は、指定されたノード、つまり NamedNodeMap の属性セットを返します。
        var self = this;
        //Array.prototype プロパティは、Array コンストラクターのプロトタイプを表し、すべての Array オブジェクトに新しいプロパティとメソッドを追加できるようにします。
        //Array.prototype自体は配列です
        Array.prototype.forEach.call(nodeAttrs,function(attr) {
            var attrName = attr.name; //イベントメソッド名とプレフィックスを追加します: v-on:click="onClick"、次に attrName = 'v-on:click' id="app" attrname= 'id'
            (自己.isDirective(属性名))の場合{     
                var exp = attr.value; //イベントメソッド名とプレフィックスを追加します: v-on:click="onClick", exp = 'onClick'

                //substring() メソッドは、文字列内の指定された 2 つの添え字間の文字を抽出するために使用されます。戻り値は新しい文字列です //dir = 'on:click'
                var dir = attrName.substring(2);  
                if(self.isEventDirective(dir)) { //イベントディレクティブ self.compileEvent(node,self.vm,exp,dir);
                }else { //v-model ディレクティブ self.compileModel(node,self.vm,exp,dir);
                }

                ノードの属性を削除します。
            }
        });
    }

上記のコンパイル関数は、Compile プロトタイプに実装されています。まず、すべてのノード属性を走査し、その属性がコマンド属性かどうかを判定します。コマンド属性の場合は、どのコマンドであるかを判別し、対応する処理を実行します。

最後に、SelfVue を再度変更して、その形式が vue に似たものになるようにします。

関数SelfVue(オプション) {
    var self = this;
    オプションデータ
    this.methods = オプション.methods;
    Object.keys(this.data).forEach(function(key) {
        self.proxyKeys(キー);    
    });
    オプションのデータを観察します。
    新しいコンパイル(options.el、this);
    オプションをマウントして呼び出します(これを);
}

テストしてみましょう:

<本文>
    <div id="アプリ">
            <h2>{{タイトル}}</h2>
            <input v-model="名前">
            <h1>{{名前}}</h1>
            <button v-on:click="clickMe">クリックしてください!</button>
    </div>
</本文>

<script src="../js/observer3.js"></script>
<script src="../js/Watcher1.js"></script>
<script src="../js/compile2.js"></script>
<script src="../js/index4.js"></script>
<スクリプト>
    新しいSelfVue({
        el: '#app',
        データ: {
            タイトル: 'hello world'、
            名前: 'canfoo'
        },
        メソッド: {
            クリックミー: 関数 () {
                this.title = 'こんにちは世界';
            }
        },
        マウント: 関数 () {
            ウィンドウ.setTimeout(() => {
                this.title = 'こんにちは';
            }, 1000);
        }
    });
</スクリプト>

効果は以下のとおりです。

これまでのところ、簡略化されたデモは成功しています。上記の例を通じて、双方向バインディング、宣言的レンダリングなど、Vue のいくつかのメカニズムをより深く理解することができます。

上記は、vue の使い方を深く理解するための詳細な内容です。vue の使い方を深く理解するための詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Vue.js フロントエンドフレームワークにおけるイベント処理の概要
  • Vue.js ベースの iView UI フレームワークの非エンジニアリング実践記録 (推奨)
  • Vue.js ユニバーサル アプリケーション フレームワーク - Nuxt.js 入門チュートリアル
  • Vue.js を学ぶ際に遭遇する落とし穴
  • Vue の詳細な入門ノート
  • Vueのフロントエンドとバックエンドのデータのやり取りと表示を理解する方法
  • Emberjs による axios 経由でのファイルダウンロード方法
  • Emberjs を使ってシンプルな Todo アプリケーションを作成する
  • Ember.js と Vue.js の詳細な比較

<<:  プロセスごとにネットワーク帯域幅を監視する Linux ツール Nethogs のインストールと展開

>>:  外部ネットワークアクセスを許可するためのMysql5.6の設定手順の詳細を共有する

推薦する

HTML ウェブページの基本コンポーネントの概要

<br />Web ページ上の情報は主にテキストベースです。 Web ページでは、フォン...

初心者向けのHTMLタグネストルールの詳細なまとめ

最近、HTML を再度学習しており、これは HTML に対する新たな理解と言えます。これを過小評価し...

JavaScript データ型の詳細な説明

目次1. リテラル1.1 数値リテラル1.2 浮動小数点リテラル1.3 特別な値1.4 文字列リテラ...

jsで照明スイッチを制御する

参考までに、jsを使用して照明スイッチを制御します。具体的な内容は次のとおりです。トピック: js ...

vue3 学習ノートにおける axios の使用の変更の概要

目次1. axioの基本的な使い方2. クロスドメインの問題を解決するには? 3. パッケージ4. ...

Expressはログイン認証を実装

この記事では、ログイン認証を実装するためのExpressの具体的なコードを例として紹介します。具体的...

JavaScript の Set データ構造の詳細な説明

目次1. セットとは何か2. セットコンストラクタ2.1) 配列2.2) 文字列2.3) 議論2.4...

MySQLはインデックスプッシュダウンを数秒で理解するのに役立ちます

目次1. インデックスプッシュダウン最適化の原理2. インデックスプッシュダウンの具体的な実践1. ...

ウェブページのカスタム選択ボックス選択

選択ドロップダウン リスト フォームは誰もがよく知っているかもしれませんが、デフォルトのドロップダウ...

MySQL の文字セットの不一致によって発生する異常な接続テーブルの解決方法

目次1. 解決策2. MySQLの文字セット文字セット検証ルール次のように簡単なテーブルクエリを実行...

JS 非同期スタック トレース: await が Promise よりも優れている理由

概要async/await と Promise の基本的な違いは、await fn() は現在の関数...

Vueは動的クエリルール生成コンポーネントを実装します

1. 動的クエリルール動的クエリルールは、おおよそ次の図のようになります。ユーザのカスタマイズに応じ...

jQueryはアコーディオンの小さなケースを実装します

この記事では、アコーディオンを実装するためのjQueryの具体的なコードを参考までに紹介します。具体...

localStorageの有効期限を設定するいくつかの方法

目次問題の説明1. 基本的な解決策2. 中間的な解決策3. 高度なソリューション4. ハードコアソリ...

JavaScript オブジェクトからプリミティブ値への変換の詳細な説明

目次オブジェクトプロトタイプの値()オブジェクトプロトタイプtoString()シンボル.toPri...