この記事では、Vue の双方向バインディングの原理を詳しく分析します (徹底的に理解してください)。
この記事では、vue をカスタマイズし、データの双方向バインディングを段階的に実装します。例を通じて、vue の双方向バインディングの原理を段階的に理解できます。皆さんのお役に立てれば幸いです。
#カスタム vue クラス
- Vue には、テンプレートとデータという少なくとも 2 つのパラメーターが必要です。 [関連する推奨事項:
vue.js ビデオ チュートリアル ]
- Compiler オブジェクトを作成し、データをテンプレートにレンダリングして、指定したノードにマウントします。 ##
class MyVue { // 1,接收两个参数:模板(根节点),和数据对象 constructor(options) { // 保存模板,和数据对象 if (this.isElement(options.el)) { this.$el = options.el; } else { this.$el = document.querySelector(options.el); } this.$data = options.data; // 2.根据模板和数据对象,渲染到根节点 if (this.$el) { // 监听data所有属性的get/set new Observer(this.$data); new Compiler(this) } } // 判断是否是一个dom元素 isElement(node) { return node.nodeType === 1; } }
ログイン後にコピー#ページへのデータの最初のレンダリングを実現
##コンパイラ
1. node2fragment 関数はテンプレート要素をメモリに抽出するため、データをテンプレートにレンダリングしてすぐにページにマウントできます。2. テンプレートがメモリに抽出された後、buildTemplate 関数を使用してテンプレート要素を走査します
要素ノード
- buildElement 関数を使用して要素の v- で始まる属性を確認します
- テキスト ノード
- buildText 関数を使用して、テキストに {{}} コンテンツがあるかどうかを確認します
- 3、vue 命令を処理する CompilerUtil クラスを作成し、{{}}、データのレンダリングを完了します
4。これで、最初のデータのレンダリングが完了します。次に、自動的に更新する必要があります。データが変更されたときのビュー。
class Compiler { constructor(vm) { this.vm = vm; // 1.将网页上的元素放到内存中 let fragment = this.node2fragment(this.vm.$el); // 2.利用指定的数据编译内存中的元素 this.buildTemplate(fragment); // 3.将编译好的内容重新渲染会网页上 this.vm.$el.appendChild(fragment); } node2fragment(app) { // 1.创建一个空的文档碎片对象 let fragment = document.createDocumentFragment(); // 2.编译循环取到每一个元素 let node = app.firstChild; while (node) { // 注意点: 只要将元素添加到了文档碎片对象中, 那么这个元素就会自动从网页上消失 fragment.appendChild(node); node = app.firstChild; } // 3.返回存储了所有元素的文档碎片对象 return fragment; } buildTemplate(fragment) { let nodeList = [...fragment.childNodes]; nodeList.forEach(node => { // 需要判断当前遍历到的节点是一个元素还是一个文本 if (this.vm.isElement(node)) { // 元素节点 this.buildElement(node); // 处理子元素 this.buildTemplate(node); } else { // 文本节点 this.buildText(node); } }) } buildElement(node) { let attrs = [...node.attributes]; attrs.forEach(attr => { // v-model="name" => {name:v-model value:name} let { name, value } = attr; // v-model / v-html / v-text / v-xxx if (name.startsWith('v-')) { // v-model -> [v, model] let [_, directive] = name.split('-'); CompilerUtil[directive](node, value, this.vm); } }) } buildText(node) { let content = node.textContent; let reg = /\{\{.+?\}\}/gi; if (reg.test(content)) { CompilerUtil['content'](node, content, this.vm); } } }
let CompilerUtil = { getValue(vm, value) { // 解析this.data.aaa.bbb.ccc这种属性 return value.split('.').reduce((data, currentKey) => { return data[currentKey.trim()]; }, vm.$data); }, getContent(vm, value) { // 解析{{}}中的变量 let reg = /\{\{(.+?)\}\}/gi; let val = value.replace(reg, (...args) => { return this.getValue(vm, args[1]); }); return val; }, // 解析v-model指令 model: function (node, value, vm) { // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值 new Watcher(vm, value, (newValue, oldValue) => { node.value = newValue; }); let val = this.getValue(vm, value); node.value = val; }, // 解析v-html指令 html: function (node, value, vm) { // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值 new Watcher(vm, value, (newValue, oldValue) => { node.innerHTML = newValue; }); let val = this.getValue(vm, value); node.innerHTML = val; }, // 解析v-text指令 text: function (node, value, vm) { // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值 new Watcher(vm, value, (newValue, oldValue) => { node.innerText = newValue; }); let val = this.getValue(vm, value); node.innerText = val; }, // 解析{{}}中的变量 content: function (node, value, vm) { let reg = /\{\{(.+?)\}\}/gi; let val = value.replace(reg, (...args) => { // 在触发getter之前,为dom创建Wather,并为Watcher.target赋值 new Watcher(vm, args[1], (newValue, oldValue) => { node.textContent = this.getContent(vm, value); }); return this.getValue(vm, args[1]); }); node.textContent = val; } }
データ駆動型ビューの実装
オブザーバー
1、defineRecative 関数を使用して、Object.defineProperty 処理を実行します。 get/set2 でデータ内の各データを監視できるように、データを取得します。 次に、データ値の変化を監視した後、ビューの内容を更新する方法を考えます。 Observer デザイン パターンを使用して、Dep クラスと Water クラスを作成します。
class Observer { constructor(data) { this.observer(data); } observer(obj) { if (obj && typeof obj === 'object') { // 遍历取出传入对象的所有属性, 给遍历到的属性都增加get/set方法 for (let key in obj) { this.defineRecative(obj, key, obj[key]) } } } // obj: 需要操作的对象 // attr: 需要新增get/set方法的属性 // value: 需要新增get/set方法属性的取值 defineRecative(obj, attr, value) { // 如果属性的取值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法 this.observer(value); // 第三步: 将当前属性的所有观察者对象都放到当前属性的发布订阅对象中管理起来 let dep = new Dep(); // 创建了属于当前属性的发布订阅对象 Object.defineProperty(obj, attr, { get() { // 在这里收集依赖 Dep.target && dep.addSub(Dep.target); return value; }, set: (newValue) => { if (value !== newValue) { // 如果给属性赋值的新值又是一个对象, 那么也需要给这个对象的所有属性添加get/set方法 this.observer(newValue); value = newValue; dep.notify(); console.log('监听到数据的变化'); } } }) } }
オブザーバー デザイン パターンを使用して Dep クラスと Wather クラスを作成する
1. オブザーバー デザイン パターンを使用する目的は次のとおりです。
テンプレートを解析し、データ内の特定のデータがテンプレートで使用されている DOM ノード セットを収集し、データが変更された場合、DOM ノード セットを更新することでデータの更新を実現します。
Dep: 特定のデータ属性が依存する dom ノードのセットを収集し、更新メソッドを提供するために使用されます。
Watcher: 各 domノードのパッケージ オブジェクト
- attr: dom によって使用されるデータ属性 cb: dom 値を変更するコールバック関数は、次の場合に
- を受け取ります。 2 を作成しました。この時点で、このアイデアは問題ないと感じており、すでに勝利を確信しています。では、Dep と Watcher をどのように使用するのでしょうか?
各属性の dep を追加して、依存する dom を収集します
データ データは、ページが最初にレンダリングされるときに読み取られるためです。このとき、データのゲッターがトリガーされるので、dom
を具体的に収集します。CompilerUtil クラスが v-model、{{}} およびその他のコマンドを解析するとき、 getter がトリガーされます。トリガーする前に Water を作成し、dom を指す static 属性を Watcher に追加してから、getter 関数で static 変数を取得し、依存関係に追加して、コレクションを完成させます。 getter がトリガーされるたびに静的変数に値が割り当てられるため、間違った依存関係が収集されることはありません。
class Dep { constructor() { // 这个数组就是专门用于管理某个属性所有的观察者对象的 this.subs = []; } // 订阅观察的方法 addSub(watcher) { this.subs.push(watcher); } // 发布订阅的方法 notify() { this.subs.forEach(watcher => watcher.update()); } }
ログイン後にコピーclass Watcher { constructor(vm, attr, cb) { this.vm = vm; this.attr = attr; this.cb = cb; // 在创建观察者对象的时候就去获取当前的旧值 this.oldValue = this.getOldValue(); } getOldValue() { Dep.target = this; let oldValue = CompilerUtil.getValue(this.vm, this.attr); Dep.target = null; return oldValue; } // 定义一个更新的方法, 用于判断新值和旧值是否相同 update() { let newValue = CompilerUtil.getValue(this.vm, this.attr); if (this.oldValue !== newValue) { this.cb(newValue, this.oldValue); } } }
ログイン後にコピー3. この時点では、データがバインドされるとビューが自動的に更新されます。当初はコードを段階的に実装したかったのですが、扱いが難しいと感じたので、投稿しました完全なクラス。
ビュー駆動型データの実装
実際には、入力ボックスの入力と変更イベントを監視するためのものです。 CompilerUtilのモデルメソッドを変更します。具体的なコードは次のとおりです。
model: function (node, value, vm) { new Watcher(vm, value, (newValue, oldValue)=>{ node.value = newValue; }); let val = this.getValue(vm, value); node.value = val; // 看这里 node.addEventListener('input', (e)=>{ let newValue = e.target.value; this.setValue(vm, value, newValue); }) },
要約
vue 双方向バインディングの原則
vue はテンプレートとデータを受け取りますパラメーター。 1. まず、data 内のデータを再帰的に走査し、各プロパティに対して Object.defineProperty を実行し、get 関数と set 関数を定義します。そして、プロパティごとに dep 配列を追加します。 get が実行されると、呼び出された DOM ノードに対してウォッチャーが作成され、配列に格納されます。 set が実行されると、値が再割り当てされ、dep 配列の notify メソッドが呼び出されて、この属性を使用するすべてのウォッチャーに通知され、対応する dom コンテンツが更新されます。 2. テンプレートをメモリにロードし、テンプレート内の要素を再帰し、要素に v- で始まるコマンドまたは二重中括弧命令があることを検出すると、対応する値がデータから取得されてテンプレートの内容が変更されます。今回はテンプレートの内容が変更され、属性の dep 配列に dom 要素が追加されます。これにより、データ駆動型ビューが実装されます。 v-model命令を処理する際に、domに入力イベント(または変更)を追加し、入力時に対応する属性の値を変更することでページ駆動型データを実現します。 3. テンプレートをデータにバインドした後、テンプレートを実際の DOM ツリーに追加します。
ウォッチャーをdep配列に配置するにはどうすればよいですか?テンプレートを解析すると、v 命令に従って対応するデータ属性値が取得されます。このとき、属性の get メソッドが呼び出されます。最初に Watcher インスタンスを作成し、内部の属性値を取得します。古い値としてウォッチャー内に保存されています。値を取得する前に、Watcher プロトタイプ オブジェクトに属性 Watcher.target = this を追加してから、Watcher.target = となる値を取得します。 null; get が呼び出されたときに Watcher.target に従ってウォッチャー インスタンス オブジェクトを取得するようにします。 メソッドの原理 vue インスタンスを作成するとき、メソッド パラメータを受け取ります テンプレート命令を解析するときに v-on に遭遇します。対応するイベントのリスナーが DOM 要素に追加され、call メソッドを使用して vue をこのメソッドにバインドします: 計算の原理 vue インスタンスを作成するときに、計算されたパラメータを受け取ります。 vue インスタンスを初期化するときに、次のことを実行します。計算されたキーのオブジェクト .defineProperty の処理と取得プロパティの追加。 (学習ビデオ共有: Web フロントエンド )vm.$methods[value].call(vm, e);
以上がこの記事では、Vue の双方向バインディングの原理を詳しく分析します (徹底的に理解してください)。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









vue.jsでJSファイルを参照するには3つの方法があります。タグ;; mounted()ライフサイクルフックを使用した動的インポート。 Vuex State Management Libraryを介してインポートします。

Vue.jsの監視オプションにより、開発者は特定のデータの変更をリッスンできます。データが変更されたら、Watchはコールバック関数をトリガーして更新ビューまたはその他のタスクを実行します。その構成オプションには、すぐにコールバックを実行するかどうかを指定する即時と、オブジェクトまたは配列の変更を再帰的に聴くかどうかを指定するDEEPが含まれます。

vue.jsでBootstrapを使用すると、5つのステップに分かれています。ブートストラップをインストールします。 main.jsにブートストラップをインポートしますブートストラップコンポーネントをテンプレートで直接使用します。オプション:カスタムスタイル。オプション:プラグインを使用します。

Vue.jsでは、Lazy Loadingを使用すると、コンポーネントまたはリソースを必要に応じて動的にロードすることができ、初期ページの読み込み時間を短縮し、パフォーマンスを改善できます。特定の実装方法には、< Keep-Alive>および<コンポーネントは>コンポーネント。怠zyなロードは、FOUC(スプラッシュ画面)の問題を引き起こす可能性があり、不必要なパフォーマンスのオーバーヘッドを避けるために怠zyなロードが必要なコンポーネントにのみ使用する必要があることに注意してください。

HTMLテンプレートのボタンをメソッドにバインドすることにより、VUEボタンに関数を追加できます。 VUEインスタンスでメソッドを定義し、関数ロジックを書き込みます。

CSSアニメーションまたはサードパーティライブラリを使用して、VUEでマーキー/テキストスクロール効果を実装します。この記事では、CSSアニメーションの使用方法を紹介します。スクロールテキストを作成し、テキストを< div>をラップします。 CSSアニメーションを定義し、オーバーフローを設定します:非表示、幅、アニメーション。キーフレームを定義し、アニメーションの開始と終了時にtranslatex()を設定します。期間、スクロール速度、方向などのアニメーションプロパティを調整します。

Vue Devtoolsを使用してブラウザのコンソールでVueタブを表示することにより、Vueバージョンを照会できます。 NPMを使用して、「NPM List -G Vue」コマンドを実行します。 package.jsonファイルの「依存関係」オブジェクトでVueアイテムを見つけます。 Vue CLIプロジェクトの場合、「Vue -Version」コマンドを実行します。 < script>でバージョン情報を確認してくださいVueファイルを参照するHTMLファイルにタグを付けます。

vue.jsには、前のページに戻る4つの方法があります。$ router.go(-1)$ router.back()outes< router-link to ="/" Component Window.history.back()、およびメソッド選択はシーンに依存します。
