Vue は https://jsrun.net/RMIKp/embedded/all/light MVVM フレームワークには、主に、データが変更されたときにビューを更新することと、ビューが変更されたときにデータを更新することという 2 つの側面が含まれます。 ビューはデータを変更して更新します。input のようなタグの場合は、 データが変更されたときにビューを更新するには、 1. 実施プロセス双方向バインディングを実装する方法がわかったので、まずデータをハイジャックして監視する必要があるため、すべての属性の変更を監視するための プロパティが変更された場合、サブスクライバー 監視する必要があるノードと属性をスキャンして解析するには、ディレクティブ パーサーの したがって、プロセスはおそらく次のようになります。
2. オブザーバーを表示する
var ライブラリ = { 本1: { 名前: ""、 }, 本2: "", }; 観察(ライブラリ); library.book1.name = "vue authoritative guide"; // プロパティ名は監視されており、現在の値は "vue authoritative guide" です library.book2 = "そのような本はありません"; // 属性 book2 は監視されており、現在の値は "そのような本はありません" です // データの検出機能を追加 defineReactive(data, key, val) { observe(val); // すべてのサブ属性を再帰的に走査する let dep = new Dep(); // 新しい dep を作成する Object.defineProperty(データ、キー、{ 列挙可能: true、 設定可能: true、 取得: 関数() { if (依存ターゲット) { // サブスクライバーを追加するかどうかを決定します。これは最初の 1 回のみ必要で、次回は必要ありません。詳細については、Watcher 関数を参照してください。 dep.addSub(Dep.target); // サブスクライバーを追加します } 戻り値: }, 設定: 関数(newVal) { if (val == newVal) return; // 値が変更されていない場合は戻ります val = 新しいVal; コンソール.log( 「プロパティ」+ キー + 「」が監視されており、現在の値は次のとおりです: 「」+ newVal.toString() + 「」」 ); dep.notify(); // データが変更された場合は、すべてのサブスクライバーに通知します。 }, }); } // 監視オブジェクトのすべてのプロパティ function observe(data) { if (!data || typeof data !== "object") { return; // オブジェクトでない場合は戻ります } Object.keys(データ).forEach(関数(キー) { defineReactive(データ、キー、データ[キー]); }); } // Dep は、サブスクライバーを収集し、プロパティが変更されたときに更新関数をトリガーする役割を担います。 関数Dep() { this.subs = {}; } 派生プロトタイプ = { addSub: 関数(sub) { this.subs.push(sub); }, 通知: 関数() { this.subs.forEach((sub) => sub.update()); }, }; アイデア分析では、サブスクライバー メッセージを収容できるサブスクライバー Dep が必要です。これは、サブスクライバーを収集し、属性が変更されたときに対応する更新機能を実行するために使用されます。 コードから、 これまでのところ、比較的完全な 3. ウォッチャーを実装するサブスクライバー では、get をトリガーするにはどうすればよいでしょうか? サブスクライバー Watcher が初期化されたときにのみ Dep.target にサブスクライバーをキャッシュし、正常に追加された後に削除する必要があります。 関数ウォッチャー(vm, exp, cb) { this.cb = cb; this.vm = vm; this.exp = exp; 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; }, }; ここまでで、シンプルな パーサー コードを ES6 コンストラクターに変換してプレビューします。 https://jsrun.net/8SIKp/embed... このコードはコンパイラを実装せず、バインドされた変数を直接渡すため、バインド用のノードにデータ ( 2 秒後に変更を加えます。ページも変更されたことがわかります。 // マイビュー プロキシキー(キー) { var self = this; Object.defineProperty(this, キー, { 列挙可能: false、 設定可能: true、 取得: 関数 proxyGetter() { self.data[キー]を返します。 }, 設定: 関数 proxySetter(newVal) { self.data[キー] = newVal; } }); } 上記のコードの目的は、 4. コンパイルを実装する上記では双方向データバインディングを実装していますが、DOM ノードはプロセス全体で解析されるのではなく、固定的に置き換えられます。そのため、データを解析してバインドするにはパーサーが必要です。 パーサー
テンプレートを解析するには、まず DOM データを解析し、次に DOM 要素の対応する指示を処理する必要があります。したがって、DOM 操作全体は比較的頻繁に行われます。新しいフラグメントを作成し、必要な解析済み 関数 nodeToFragment(el) { var フラグメント = document.createDocumentFragment(); var child = el.firstChild; (子)の間{ // Dom 要素をフラグメントに移動します。fragment.appendChild(child); 子 = el.firstChild; } フラグメントを返します。 } 次に、各ノードをトラバースし、関連する命令とテンプレート構文を含むノードに対して特別な処理を実行する必要があります。まず、最も単純なテンプレート構文処理を実行し、正規表現を使用して「{{variable}}」の形式で構文を解析します。 関数compileElement(el){ var childNodes = el.childNodes; var self = this; [].slice.call(childNodes).forEach(function(node) { var reg = /\{\{(.*)\}\}/; // {{xx}} に一致 var テキスト = node.textContent; if (self.isTextNode(node) && reg.test(text)) { // この形式の命令であるかどうかを判断します {{}} self.compileText(node, reg.exec(text)[1]); } (node.childNodes && node.childNodes.length)の場合{ self.compileElement(node); // 子ノードを再帰的に走査し続けます} }); }, 関数compileText(ノード, exp) { var self = this; var initText = this.vm[exp]; updateText(node, initText); // 初期化されたデータをビューに初期化します。 new Watcher(this.vm, exp, function (value) { // サブスクライバーを生成し、更新関数をバインドします。 self.updateText(node, value); }); }, 関数 updateText (ノード, 値) { node.textContent = typeof value == 'undefined' ? '' : value; } 最も外側のノードを取得した後、 次に、データが変更されたときに対応する DOM を更新するために、現在のパラメータに対応する更新関数サブスクライバーを生成する必要があります。 これで、解析、初期化、コンパイルの 3 つのプロセスが完了します。 次に、双方向データ バインディングにテンプレート変数を使用するように myVue を変更します。 https://jsrun.net/K4IKp/embed... 5. 解析イベントを追加する
v-model と v-on 解析を追加します。 関数コンパイル(ノード) { var nodeAttrs = node.attributes; var self = this; Array.prototype.forEach.call(nodeAttrs, function(attr) { var attrName = attr.name; if (isDirective(attrName)) { var exp = attr.value; var dir = attrName.substring(2); if (isEventDirective(dir)) { // イベント命令 self.compileEvent(node, self.vm, exp, dir); } それ以外 { // v-model ディレクティブ self.compileModel(node, self.vm, exp, dir); } node.removeAttribute(attrName); // 解析が完了したので、属性を削除します} }); } // v-ディレクティブ解析関数 isDirective(attr) { attr.indexOf("v-") == 0 を返します。 } // on: ディレクティブ解析関数 isEventDirective(dir) { dir.indexOf("on:") === 0 を返します。 } 上記の 6. myVueのフルバージョン
クラスMyVue { コンストラクタ(オプション) { var self = this; オプションデータ this.methods = オプション.methods; Object.keys(this.data).forEach(function(key) { self.proxyKeys(キー); }); これを観察します。 新しいコンパイル(options.el、this); options.mounted.call(this); //すべてが完了したらマウントされた関数を実行します} プロキシキー(キー) { // this.data プロパティを this にプロキシします。var self = this; Object.defineProperty(this, キー, { 列挙可能: false、 設定可能: true、 取得: 関数 getter() { self.data[キー]を返します。 }, 設定: 関数 setter(newVal) { self.data[キー] = newVal; }, }); } } その後、テストすることができます。 https://jsrun.net/Y4IKp/embed... プロセスをまとめてみましょう。この図をもう一度見てみると、さらに明確になります。 コードアドレスを表示できます: Vue2.x 双方向バインディングの原則と実装 これで、 以下もご興味があるかもしれません:
|
>>: MySQLの大規模テーブル最適化ソリューションについての簡単な説明
よく使われる4つのMySQLエンジンの紹介(1):MyISAMストレージエンジン:トランザクションや...
Msyqlデータベースのインストール、参考までに具体的な内容は次のとおりです。 ①ブラウザでhttp...
MySQL では、データベースの文字化けは一般的に文字セットを設定することで修正できますが、文字化け...
MongoDB のインストール プロセスと問題記録1. MongoDBのインストールMongoDBを...
DockerでRedisをデプロイするまずLinuxにDockerをインストールし、次にDocker...
Vue でprovide+inject組み合わせを使用するまず、App.vue を変更する必要があ...
Mixin は、再利用可能な機能を Vue コンポーネント間で分散する非常に柔軟な方法を提供します。...
目的: 1. Alibaba Cloud Serverを介してサーバーの外部ネットワークをマッピング...
Springboot プロジェクトを開始するには、次の 3 つの方法があります。 1. メインメソッ...
編集者注: この記事は、Teambition チームの @娄昊川 が寄稿したものです。Teambit...
UDP の理論については詳しく説明しません。UDP に関する HelloWorld プログラムを紹介...
1 分で最初の Web ページを作成します。簡単な Web ページを作ってみましょう。ぜひフォローし...
目次序文iframeはサンドボックスを実装しますdiffメソッドを使用したサンドボックスの実装プロキ...
前回の記事では、Oracle でピボット テーブルを実装するいくつかの方法を紹介しました。今日は、同...
最近確認された5件のデータを照会するビジネスがあります。 `id`、`title` を選択 `th_...