検索
ホームページウェブフロントエンドVue.jsこの記事では、Vue の双方向バインディングの原理を詳しく分析します (徹底的に理解してください)。

この記事では、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 をこのメソッドにバインドします: vm.$methods[value].call(vm, e);

計算の原理

vue インスタンスを作成するときに、計算されたパラメータを受け取ります。

vue インスタンスを初期化するときに、次のことを実行します。計算されたキーのオブジェクト .defineProperty の処理と取得プロパティの追加。

(学習ビデオ共有: Web フロントエンド )

以上がこの記事では、Vue の双方向バインディングの原理を詳しく分析します (徹底的に理解してください)。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事は掘金社区で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
vue.jsとフロントエンドスタック:接続の理解vue.jsとフロントエンドスタック:接続の理解Apr 24, 2025 am 12:19 AM

VUE.JSは、開発効率とユーザーエクスペリエンスを向上させるために、フロントエンドテクノロジースタックと密接に統合されています。 1)建設ツール:Webpackおよびロールアップと統合して、モジュール開発を実現します。 2)国家管理:VUEXと統合して、複雑なアプリケーションステータスを管理します。 3)ルーティング:Vuerouterと統合して、単一ページのアプリケーションルーティングを実現します。 4)CSSプリプロセッサ:SASSをサポートし、スタイル開発効率を改善するために少なくなります。

Netflix:React(またはその他のフレームワーク)の使用の調査Netflix:React(またはその他のフレームワーク)の使用の調査Apr 23, 2025 am 12:02 AM

Netflixは、Reactのコンポーネント設計と仮想DOMメカニズムが複雑なインターフェイスと頻繁な更新を効率的に処理できるため、ユーザーインターフェイスを構築するためにReactを選択しました。 1)コンポーネントベースの設計により、Netflixはインターフェイスを管理可能なウィジェットに分解し、開発効率とコード保守性を向上させることができます。 2)仮想DOMメカニズムは、DOM操作を最小化することにより、Netflixユーザーインターフェイスの滑らかさと高性能を保証します。

vue.jsとフロントエンド:フレームワークに深く飛び込むvue.jsとフロントエンド:フレームワークに深く飛び込むApr 22, 2025 am 12:04 AM

Vue.jsは、使いやすく強力なため、開発者に愛されています。 1)そのレスポンシブデータバインディングシステムは、ビューを自動的に更新します。 2)コンポーネントシステムは、コードの再利用性と保守性を向上させます。 3)コンピューティングプロパティとリスナーは、コードの読みやすさとパフォーマンスを向上させます。 4)Vuedevtoolsの使用とコンソールエラーのチェックは、一般的なデバッグ手法です。 5)パフォーマンスの最適化には、主要な属性、計算された属性、およびキープアライブコンポーネントの使用が含まれます。 6)ベストプラクティスには、クリアコンポーネントの命名、単一ファイルコンポーネントの使用、ライフサイクルフックの合理的な使用が含まれます。

フロントエンドのvue.jsの力:主要な機能と利点フロントエンドのvue.jsの力:主要な機能と利点Apr 21, 2025 am 12:07 AM

Vue.jsは、効率的で保守可能なフロントエンドアプリケーションを構築するのに適した進歩的なJavaScriptフレームワークです。その主な機能には、1。レスポンシブデータバインディング、2。コンポーネント開発、3。仮想DOM。これらの機能を通じて、VUE.JSは開発プロセスを簡素化し、アプリケーションのパフォーマンスと保守性を向上させ、最新のWeb開発で非常に人気を博しています。

Vue.jsはReactよりも優れていますか?Vue.jsはReactよりも優れていますか?Apr 20, 2025 am 12:05 AM

Vue.jsとReactにはそれぞれ独自の利点と欠点があり、選択はプロジェクトの要件とチームの条件に依存します。 1)Vue.jsは、シンプルで使いやすいため、小さなプロジェクトや初心者に適しています。 2)Reactは、その豊富な生態系とコンポーネント設計のため、大規模なプロジェクトと複雑なUIに適しています。

Vue.jsの関数:フロントエンドでのユーザーエクスペリエンスの強化Vue.jsの関数:フロントエンドでのユーザーエクスペリエンスの強化Apr 19, 2025 am 12:13 AM

Vue.jsは複数の機能を介してユーザーエクスペリエンスを改善します。1。レスポンシブシステムは、リアルタイムデータフィードバックを実現します。 2。コンポーネント開発により、コードの再利用性が向上します。 3. Vuerouterはスムーズなナビゲーションを提供します。 4.動的データの結合および遷移アニメーションは、相互作用効果を強化します。 5.エラー処理メカニズムにより、ユーザーのフィードバックが保証されます。 6.パフォーマンスの最適化とベストプラクティスは、アプリケーションのパフォーマンスを改善します。

Vue.js:Web開発におけるその役割を定義しますVue.js:Web開発におけるその役割を定義しますApr 18, 2025 am 12:07 AM

Web開発におけるVue.jsの役割は、開発プロセスを簡素化し、効率を向上させるプログレッシブJavaScriptフレームワークとして機能することです。 1)開発者は、レスポンシブデータのバインディングとコンポーネント開発を通じてビジネスロジックに集中できるようになります。 2)VUE.JSの作業原則は、パフォーマンスを最適化するためにレスポンシブシステムと仮想DOMに依存しています。 3)実際のプロジェクトでは、VUEXを使用してグローバルな状態を管理し、データの応答性を最適化することが一般的な慣行です。

Vue.jsの理解:主にフロントエンドフレームワークVue.jsの理解:主にフロントエンドフレームワークApr 17, 2025 am 12:20 AM

Vue.jsは、2014年にYou YuxiがリリースしたプログレッシブJavaScriptフレームワークで、ユーザーインターフェイスを構築します。その中心的な利点には、次のものが含まれます。1。レスポンシブデータバインディング、データ変更の自動更新ビュー。 2。コンポーネントの開発では、UIは独立した再利用可能なコンポーネントに分割できます。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、