目次
Start
#次のコードに示すように、これで が完了します。 Vue
实现数据代理 Observer 类
实现管理器 Dep 类
实现订阅者 Watcher 类
埋坑
总结
ホームページ ウェブフロントエンド Vue.js vue の双方向バインディングを実装する方法を段階的に説明します。

vue の双方向バインディングを実装する方法を段階的に説明します。

Mar 03, 2022 pm 07:41 PM
vue 双方向バインディング

vue はどのように双方向バインディングを実装しますか?この記事では、誰もが双方向バインディングの論理的方向をよりよく理解できるように、vue 双方向バインディングの作成方法を説明します。

vue の双方向バインディングを実装する方法を段階的に説明します。

この記事は、読者が双方向バインディングの実装方法と双方向バインディングの論理的方向性をよりよく理解できるように、主に落とし穴を作成して埋めるプロセスです。 . ここまでは、段階的に双方向バインディングを最初から実装してみましょう。これはクラスのチュートリアルの記事です。記事に従って各クラスを注意深く観察する限り、実装は難しくありません。

Start

魔術師を始めませんか? 犬を連れてください! !

正しくないようです

画像からやり直します

vue の双方向バインディングを実装する方法を段階的に説明します。

画像から確認できますnew Vue ()これは 2 つのステップに分かれています

  • エージェントはすべてのデータを監視し、それを Dep に関連付け、## を通じてビューを更新するようにサブスクライバーに通知します。 #Dep 。 [関連する推奨事項: vuejs ビデオ チュートリアル ]

  • すべてのテンプレートを解析し、テンプレートで使用されるデータをサブスクライブし、更新関数をバインドすると、データが発生するとき

    Dep が変更された場合は、更新機能を実行するようにサブスクライバに通知します。

次のステップは、実装方法と何を記述する必要があるかを分析することです。まず、vue の基本コードの一部を見てみましょう。最初から分析しましょう

<div id="app">
  <input v-model="message" />
  <p>{{message}}</p>
</div>
ログイン後にコピー
let app = new Vue({
    el:"#app",
    data:{
      message:"测试这是一个内容"
    }
})
ログイン後にコピー

上記のコードから、

el 属性と data 属性を持つ new Vue の動作がわかります。これは最も基本的な属性です。 HTML コードでは、

が vue によってレンダリングされるテンプレート ルート ノードであることがわかっているため、ページをレンダリングするには、vue がテンプレート解析メソッド Compile# を実装する必要があります。 ## クラス、解析メソッドも必要です。 2 つの命令 {{ }}v-model を処理するには、テンプレートの解析に加えて、データを実装する必要もありますObserver クラス

Vue クラスの実装

#次のコードに示すように、これで が完了します。 Vue

クラスです。非常にシンプルです。

class に興味がある場合は、キーワードに慣れていない場合は、最初にそれらを学習することをお勧めします。以下から、2 つのクラスが次のとおりであることがわかります。ここでインスタンス化されるもののうち、1 つはプロキシ データ クラス、もう 1 つは解析テンプレート クラスです。

class Vue {
  constructor(options) {
    // 代理数据
    new Observer(options.data)
    // 绑定数据
    this.data = options.data
    // 解析模板
    new Compile(options.el, this)
  }
}
ログイン後にコピー
次に、テンプレートを解析するための Compile

クラスを作成しましょう。もう一度分析しましょう。テンプレートを解析するにはどうすればよいですか?

#テンプレートを解析したい場合、DOM上で直接操作を続けることは不可能なので、ドキュメントフラグメント(仮想DOM)を作成し、テンプレートDOMノードを仮想DOMノードにコピーする必要があります。 DOM ノードは、元の DOM ノードを置き換えます
  • #仮想ノードがコピーされた後、解析のためにノード ツリー全体を走査する必要があります。解析プロセス中、atrr DOM の属性は、

    textContent
  • ノードのコンテンツを解析して二重中括弧があるかどうかを判断することに加えて、Vue 関連のコマンドを見つけるために走査されます。解析された属性
  • テンプレート解析の実装コンパイル クラス

  • 以下では、段階的に実装していきます。
  • ##コンパイル#を構築します。 ## クラスでは、最初に静的ノードと Vue インスタンスを取得し、次に仮想 dom を格納するための仮想 dom 属性を定義します。

class Compile {
  constructor(el, vm) {
    // 获取静态节点
    this.el = document.querySelector(el);
    // vue实例
    this.vm = vm 
    // 虚拟dom
    this.fragment = null 
    // 初始化方法
    this.init()
  }
}
ログイン後にコピー

    初期化メソッドを実装します
  • init()

    , このメソッドは主に仮想 dom を作成し、テンプレートを解析するメソッドを呼び出すために使用されます。解析が完了すると、ページ内の DOM ノードが置き換えられます。

    class Compile { 
      //...省略其他代码
    
      init() {
        // 创建一个新的空白的文档片段(虚拟dom)
        this.fragment = document.createDocumentFragment()
      	// 遍历所有子节点加入到虚拟dom中
        Array.from(this.el.children).forEach(child => {
          this.fragment.appendChild(child)
        })
        // 解析模板
        this.parseTemplate(this.fragment)
        // 解析完成添加到页面
        this.el.appendChild(this.fragment);
      }
    }
    ログイン後にコピー

    実装 解析テンプレート メソッド
  • parseTemplate
は、主に仮想 DOM 内のすべての子ノードを走査して解析し、子ノードのタイプに応じて異なる処理を実行します。
  • class Compile { 
      //...省略其他代码
    
      // 解析模板 
      parseTemplate(fragment) {
        // 获取虚拟DOM的子节点
        let childNodes = fragment.childNodes || []
        // 遍历节点
        childNodes.forEach((node) => {
          // 匹配大括号正则表达式 
          var reg = /\{\{(.*)\}\}/;
          // 获取节点文本
          var text = node.textContent;
          if (this.isElementNode(node)) { // 判断是否是html元素
            // 解析html元素
            this.parseHtml(node)
          } else if (this.isTextNode(node) && reg.test(text)) { //判断是否文本节点并带有双花括号
            // 解析文本
            this.parseText(node, reg.exec(text)[1])
          }
    
          // 递归解析,如果还有子元素则继续解析
          if (node.childNodes && node.childNodes.length != 0) {
            this.parseTemplate(node)
          }
        });
      }
    }
    ログイン後にコピー

    上記のコードによれば、HTML 要素であるかテキスト要素であるかという 2 つの単純な判断を実装する必要があると結論付けられます。ここでは ## を取得します #nodeType
  • の値は区別するために使用されます。理解できない場合は、
ポータルを直接参照してください: Node.nodeType
    の拡張子もあります次のコードの
  • isVueTag

    メソッド。

    class Compile { 
      //...省略其他代码
    
    	// 判断是否携带 v-
      isVueTag(attrName) {
        return attrName.indexOf("v-") == 0
      }
      // 判断是否是html元素
      isElementNode(node) {
        return node.nodeType == 1;
      }
      // 判断是否是文字元素
      isTextNode(node) {
        return node.nodeType == 3;
      }
    }
    ログイン後にコピー
    を使用して、
  • parseHtml

    メソッドを実装します。HTML コードの解析主に、HTML 要素の attr 属性のトラバースが含まれます。

class Compile {
  //...省略其他代码

  // 解析html
  parseHtml(node) {
    // 获取元素属性集合
    let nodeAttrs = node.attributes || []
    // 元素属性集合不是数组,所以这里要转成数组之后再遍历
    Array.from(nodeAttrs).forEach((attr) => {
      // 获取属性名称
      let arrtName = attr.name;
      // 判断名称是否带有 v- 
      if (this.isVueTag(arrtName)) {
        // 获取属性值
        let exp = attr.value;
        //切割 v- 之后的字符串
        let tag = arrtName.substring(2);
        if (tag == "model") {
          // v-model 指令处理方法
          this.modelCommand(node, exp, tag)
        }
      }
    });
  }
}
ログイン後にコピー
  • 实现modelCommand方法,在模板解析阶段来说,我们只要把 vue实例中data的值绑定到元素上,并实现监听input方法更新数据即可。

class Compile {
	//...省略其他代码
  
   // 处理model指令
  modelCommand(node, exp) {
    // 获取数据
    let val = this.vm.data[exp]
    // 解析时绑定数据
    node.value = val || ""

    // 监听input事件
    node.addEventListener("input", (event) => {
      let newVlaue = event.target.value;
      if (val != newVlaue) {
        // 更新data数据
        this.vm.data[exp] = newVlaue
        // 更新闭包数据,避免双向绑定失效
        val = newVlaue
      }
    })
  }
}
ログイン後にコピー
  • 处理Text元素就相对简单了,主要是将元素中的textContent内容替换成数据即可

class Compile {
	//...省略其他代码
  
  //解析文本
  parseText(node, exp) {
    let val = this.vm.data[exp]
    // 解析更新文本
    node.textContent = val || ""
  }
}
ログイン後にコピー

至此已经完成了Compile类的初步编写,测试结果如下,已经能够正常解析模板

vue の双方向バインディングを実装する方法を段階的に説明します。

下面就是我们目前所实现的流程图部分

vue の双方向バインディングを実装する方法を段階的に説明します。

坑点一:

  • 在第6点modelCommand方法中并没有实现双向绑定,只是单向绑定,后续要双向绑定时还需要继续处理

坑点二:

  • 第7点parseText方法上面的代码中并没有去订阅数据的改变,所以这里只会在模板解析时绑定一次数据

实现数据代理 Observer 类

这里主要是用于代理data中的所有数据,这里会用到一个Object.defineProperty方法,如果不了解这个方法的先去看一下文档传送门:

文档:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Observer类主要是一个递归遍历所有data中的属性然后进行数据代理的的一个方法

defineReactive中传入三个参数data, key, val

datakey都是Object.defineProperty的参数,而val将其作为一个闭包变量供Object.defineProperty使用

// 监听者
class Observer {
  constructor(data) {
    this.observe(data)
  }
  // 递归方法
  observe(data) {
    //判断数据如果为空并且不是object类型则返回空字符串
    if (!data || typeof data != "object") {
      return ""
    } else {
      //遍历data进行数据代理
      Object.keys(data).forEach(key => {
        this.defineReactive(data, key, data[key])
      })
    }
  }

  // 代理方法
  defineReactive(data, key, val) {
    // 递归子属性
    this.observe(data[key])
    Object.defineProperty(data, key, {
      configurable: true,  //可配置的属性
      enumerable: true, //可遍历的属性
      get() {
        return val
      },
      set(newValue) {
        val = newValue
      }
    })
  }
}
ログイン後にコピー

下面我们来测试一下是否成功实现了数据代理,在Vue的构造函数输出一下数据

class Vue {
  constructor(options) {
    // 代理数据
    new Observer(options.data)
    console.log(options.data)
    // 绑定数据
    this.data = options.data
    // 解析模板
    new Compile(options.el, this)
  }
}
ログイン後にコピー

结果如下,我们可以看出已经实现了数据代理。

vue の双方向バインディングを実装する方法を段階的に説明します。

对应的流程图如下所示

vue の双方向バインディングを実装する方法を段階的に説明します。

坑点三:

  • 这里虽然实现了数据代理,但是按照图上来说,还需要引入管理器,在数据发生变化时通知管理器数据发生了变化,然后管理器再通知订阅者更新视图,这个会在后续的填坑过程过讲到。

实现管理器 Dep 类

上面我们已经实现了模板解析到初始化视图,还有数据代理。而下面要实现的Dep类主要是用于管理订阅者和通知订阅者,这里会用一个数组来记录每个订阅者,而类中也会给出一个notify方法去调用订阅者的update方法,实现通知订阅者更新功能。这里还定义了一个target属性用来存储临时的订阅者,用于加入管理器时使用。

class Dep {
  constructor() {
    // 记录订阅者
    this.subList = []
  }
  // 添加订阅者
  addSub(sub) {
    // 先判断是否存在,防止重复添加订阅者
    if (this.subList.indexOf(sub) == -1) {
      this.subList.push(sub)
    }
  }
  // 通知订阅者
  notify() {
    this.subList.forEach(item => {
      item.update() //订阅者执行更新,这里的item就是一个订阅者,update就是订阅者提供的方法
    })
  }
}
// Dep全局属性,用来临时存储订阅者
Dep.target = null
ログイン後にコピー

管理器实现完成之后我们也就实现了流程图中的以下部分。要注意下面几点

  • Observer通知Dep主要是通过调用notify方法
  • Dep通知Watcher主要是是调用了Watcher类中的update方法

vue の双方向バインディングを実装する方法を段階的に説明します。


实现订阅者 Watcher 类

订阅者代码相对少,但是理解起来还是有点难度的,在Watcher类中实现了两个方法,一个是update更新视图方法,一个putIn方法(我看了好几篇文章都是定义成 get 方法,可能是因为我理解的不够好吧)。

  • update:主要是调用传入的cb方法体,用于更新页面数据
  • putIn:主要是用来手动加入到Dep管理器中。
// 订阅者
class Watcher {
  // vm:vue实例本身
  // exp:代理数据的属性名称
  // cb:更新时需要做的事情
  constructor(vm, exp, cb) {
    this.vm = vm
    this.exp = exp
    this.cb = cb
    this.putIn()
  }
  update() {
    // 调用cb方法体,改变this指向并传入最新的数据作为参数
    this.cb.call(this.vm, this.vm.data[this.exp])
  }
  putIn() {
    // 把订阅者本身绑定到Dep的target全局属性上
    Dep.target = this
    // 调用获取数据的方法将订阅者加入到管理器中
    let val = this.vm.data[this.exp]
    // 清空全局属性
    Dep.target = null
  }
}
ログイン後にコピー

坑点四:

  • Watcher类中的putIn方法再构造函数调用后并没有加入到管理器中,而是将订阅者本身绑定到target全局属性上而已

埋坑

通过上面的代码我们已经完成了每一个类的构建,如下图所示,但是还是有几个流程是有问题的,也就是上面的坑点。所以下面要填坑

vue の双方向バインディングを実装する方法を段階的に説明します。

埋坑 1 和 2

完成坑点一和坑点二,在modelCommandparseText方法中增加实例化订阅者代码,并自定义要更新时执行的方法,其实就是更新时去更新页面中的值即可

modelCommand(node, exp) {
  
  // ...省略其他代码
  
  // 实例化订阅者,更新时直接更新node的值
  new Watcher(this.vm, exp, (value) => {
    node.value = value
  })
}


parseText(node, exp) {
  
  //  ...省略其他代码
  
  // 实例化订阅者,更新时直接更新文本内容
  new Watcher(this.vm, exp, (value) => {
    node.textContent = value
  })
}
ログイン後にコピー

埋坑 3

完成坑点三,主要是为了引入管理器,通知管理器发生改变,主要是在Object.defineProperty set方法中调用dep.notify()方法

// 监听方法
defineReactive(data, key, val) {
  // 实例化管理器--------------增加这一行
  let dep = new Dep()
  
  // ...省略其他代码
  
    set(newValue) {
      val = newValue
      // 通知管理器改变--------------增加这一行
      dep.notify()
    }

}
ログイン後にコピー

埋坑 4

完成坑点四,主要四将订阅者加入到管理器中

defineReactive(data, key, val) {
  // ...省略其他代码
    get() {
      // 将订阅者加入到管理器中--------------增加这一段
      if (Dep.target) {
        dep.addSub(Dep.target)
      }
      return val
    },
  // ...省略其他代码
}
ログイン後にコピー

完成了坑点四可能就会有靓仔疑惑了,这里是怎么加入的呢Dep.target又是什么呢,我们不妨从头看看代码并结合下面这张图

vue の双方向バインディングを実装する方法を段階的に説明します。

至此我们已经实现了一个简单的双向绑定,下面测试一下

vue の双方向バインディングを実装する方法を段階的に説明します。

完结撒花

总结

本文解释的并不多,所以才是类教程文章,如果读者有不懂的地方可以在评论去留言讨论

vue の双方向バインディングを実装する方法を段階的に説明します。

(学习视频分享:vuejs教程web前端

以上がvue の双方向バインディングを実装する方法を段階的に説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホット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衣類リムーバー

AI Hentai Generator

AI Hentai Generator

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

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

VUEのボタンに関数を追加する方法 VUEのボタンに関数を追加する方法 Apr 08, 2025 am 08:51 AM

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

VueでBootstrapの使用方法 VueでBootstrapの使用方法 Apr 07, 2025 pm 11:33 PM

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

vue.jsでJSファイルを参照する方法 vue.jsでJSファイルを参照する方法 Apr 07, 2025 pm 11:27 PM

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

VueでWatchの使用方法 VueでWatchの使用方法 Apr 07, 2025 pm 11:36 PM

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

Vue Multi-Page開発とはどういう意味ですか? Vue Multi-Page開発とはどういう意味ですか? Apr 07, 2025 pm 11:57 PM

VUEマルチページ開発は、VUE.JSフレームワークを使用してアプリケーションを構築する方法です。アプリケーションは別々のページに分割されます。コードメンテナンス:アプリケーションを複数のページに分割すると、コードの管理とメンテナンスが容易になります。モジュール性:各ページは、簡単に再利用および交換するための別のモジュールとして使用できます。簡単なルーティング:ページ間のナビゲーションは、単純なルーティング構成を介して管理できます。 SEOの最適化:各ページには独自のURLがあり、SEOに役立ちます。

Vueによる前のページに戻る方法 Vueによる前のページに戻る方法 Apr 07, 2025 pm 11:30 PM

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

Vueのバージョンを照会する方法 Vueのバージョンを照会する方法 Apr 07, 2025 pm 11:24 PM

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

Function Intercept Vueの使用方法 Function Intercept Vueの使用方法 Apr 08, 2025 am 06:51 AM

VUEの関数傍受は、指定された期間内に関数が呼び出され、パフォーマンスの問題を防ぐ回数を制限するために使用される手法です。実装方法は次のとおりです。LodashLibrary:Import {Debounce}から「Lodash」からインポート。 debounce関数を使用して、インターセプト関数を作成します。インターセプト関数を呼び出すと、制御関数は500ミリ秒でせいぜい1回呼び出されます。

See all articles