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の大規模テーブル最適化ソリューションについての簡単な説明
1. Dockerをインストールするyum -y install docker-ioインストールが完...
Centos7 バージョンをインストールするときに、外部ネットワークへの接続を選択すると、外部ネット...
MySQL は現在、ほとんどの企業や事業体で使用されているデータベースです。MySQL が使用される...
この記事では、パスワードボックスの検証情報を実装するためのJavaScriptの具体的なコードを例と...
よく食べて十分に休息を取るというのは簡単なことのように思えますが、実際に実行するのはそれほど簡単では...
一般的に ELK スタックとして知られる Elastic スタックは、Elasticsearch、L...
コンピュータを使用すると、システム内に大量のゴミが生成されます。最も一般的なケースは、同じファイルが...
序文この記事は主に Linux C でのログ出力コード テンプレートに関する関連コンテンツを紹介し、...
序文日常のコード開発では、配列のソートに関連する操作が多数あります。JavaScript では、so...
目次tomcatをデプロイする1.ダウンロードして解凍する2. 設定ファイルを変更する移植プロジェク...
JavascriptとDOMの関係は非常に曖昧で、CSSやHTMLのフロントエンド技術層も理解してい...
ファイルとは何ですか?すべてのファイルは実際には文字列のストリームですが、適切な解析方法を使用すると...
これは、ネイティブJSを使用してページングクリックコントロールを実装する必要がある面接の質問です。参...
目次1. コンポーネント通信1. Props 親コンポーネント ---> 子コンポーネント通信...
失敗の原因今日、カルーセルを書いていたときに、overflow;hidden; が失敗する可能性があ...