最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue 數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方法

 更新時間:2021年03月04日 10:24:13   作者:小飛2020  
這篇文章主要介紹了vue 數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方法,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下

1. 前言

本文適合于學(xué)習(xí)Vue源碼的初級學(xué)者,閱讀后,你將對Vue的數(shù)據(jù)雙向綁定原理有一個大致的了解,認(rèn)識Observer、Compile、Wathcer三大角色(如下圖所示)以及它們所發(fā)揮的功能。

本文將一步步帶你實(shí)現(xiàn)簡易版的數(shù)據(jù)雙向綁定,每一步都會詳細(xì)分析這一步要解決的問題以及代碼為何如此寫,因此,在閱讀完本文后,希望你能自己動手實(shí)現(xiàn)一個簡易版數(shù)據(jù)雙向綁定。

2. 代碼實(shí)現(xiàn)

2.1 目的分析

本文要實(shí)現(xiàn)的效果如下圖所示:

本文用到的HTML和JS主體代碼如下:

<div id="app">
  <h1 v-text="msg"></h1>
  <input type="text" v-model="msg">
  <div>
    <h1 v-text="msg2"></h1>
    <input type="text" v-model="msg2">
  </div>
</div>
let vm = new Vue({
    el: "#app",
    data: {
      msg: "hello world",
      msg2: "hello xiaofei"
    }
  })

我們將按照下面三個步驟來實(shí)現(xiàn):

  • 第一步:將data中的數(shù)據(jù)同步到頁面上,實(shí)現(xiàn) M ==> V 的初始化;
  • 第二步:當(dāng)input框中輸入值時,將新值同步到data中,實(shí)現(xiàn) V ==> M 的綁定;
  • 第三步:當(dāng)data數(shù)據(jù)發(fā)生更新的時候,觸發(fā)頁面發(fā)生變化,實(shí)現(xiàn) M ==> V 的綁定。

2.2 實(shí)現(xiàn)過程

2.2.1 入口代碼

首先,我們要創(chuàng)造一個Vue類,這個類接收一個 options 對象,同時,我們要對 options 對象中的有效信息進(jìn)行保存;

然后,我們有三個主要模塊:Observer、Compile、Wathcer,其中,Observer用來數(shù)據(jù)劫持的,Compile用來解析元素,Wathcer是觀察者??梢詫懗鋈缦麓a:(Observer、Compile、Wathcer這三個概念,不用細(xì)究,后面會詳解講解)。

class Vue {
    // 接收傳進(jìn)來的對象
    constructor(options) {
      // 保存有效信息
      this.$el = document.querySelector(options.el);
      this.$data = options.data;

      // 容器: {屬性1: [wathcer1, wathcer2...], 屬性2: [...]},用來存放每個屬性觀察者
      this.$watcher = {};

      // 解析元素: 實(shí)現(xiàn)Compile
      this.compile(this.$el); // 要解析元素, 就得把元素傳進(jìn)去

      // 劫持?jǐn)?shù)據(jù): 實(shí)現(xiàn) Observer
      this.observe(this.$data); // 要劫持?jǐn)?shù)據(jù), 就得把數(shù)據(jù)傳入
    }
    compile() {}
    observe() {}
  }

2.2.2 頁面初始化

在這一步,我們要實(shí)現(xiàn)頁面的初始化,即解析出v-text和v-model指令,并將data中的數(shù)據(jù)渲染到頁面中。

這一步的關(guān)鍵在于實(shí)現(xiàn)compile方法,那么該如何解析el元素呢?思路如下:

  • 首先要獲取到el下面的所有子節(jié)點(diǎn),然后遍歷這些子節(jié)點(diǎn),如果子節(jié)點(diǎn)還有子節(jié)點(diǎn),那我們就需要用到遞歸的思想;
  • 遍歷子節(jié)點(diǎn)找到所有有指令的元素,并將對應(yīng)的數(shù)據(jù)渲染到頁面中。

代碼如下:(主要看compile那部分)

class Vue {
    // 接收傳進(jìn)來的對象
    constructor(options) {
      // 獲取有用信息
      this.$el = document.querySelector(options.el);
      this.$data = options.data;

      // 容器: {屬性1: [wathcer1, wathcer2...], 屬性2: [...]}
      this.$watcher = {};

      // 2. 解析元素: 實(shí)現(xiàn)Compile
      this.compile(this.$el); // 要解析元素, 就得把元素傳進(jìn)去

      // 3. 劫持?jǐn)?shù)據(jù): 實(shí)現(xiàn) Observer
      this.observe(this.$data); // 要劫持?jǐn)?shù)據(jù), 就得把數(shù)據(jù)傳入
    }
    compile(el) {
      // 解析元素下的每一個子節(jié)點(diǎn), 所以要獲取el.children
      // 備注: children 返回元素集合, childNodes返回節(jié)點(diǎn)集合
      let nodes = el.children;

      // 解析每個子節(jié)點(diǎn)的指令
      for (var i = 0, length = nodes.length; i < length; i++) {
        let node = nodes[i];
        // 如果當(dāng)前節(jié)點(diǎn)還有子元素, 遞歸解析該節(jié)點(diǎn)
        if(node.children){
          this.compile(node);
        }
        // 解析帶有v-text指令的元素
        if (node.hasAttribute("v-text")) {
          let attrVal = node.getAttribute("v-text");
          node.textContent = this.$data[attrVal]; // 渲染頁面
        }
        // 解析帶有v-model指令的元素
        if (node.hasAttribute("v-model")) {
          let attrVal = node.getAttribute("v-model");
          node.value = this.$data[attrVal];
        }
      }
    }
    observe(data) {}
  }

這樣,我們就實(shí)現(xiàn)頁面的初始化了。

2.2.3 視圖影響數(shù)據(jù)

因?yàn)閕nput帶有v-model指令,因此我們要實(shí)現(xiàn)這樣一個功能:在input框中輸入字符,data中綁定的數(shù)據(jù)發(fā)生相應(yīng)的改變。

我們可以在input這個元素上綁定一個input事件,事件的效果就是:將data中的相應(yīng)數(shù)據(jù)修改為input中的值。

這一部分的實(shí)現(xiàn)代碼比較簡單,只要看標(biāo)注那個地方就明白了,代碼如下:

class Vue {
    constructor(options) {
      this.$el = document.querySelector(options.el);
      this.$data = options.data;
      
      this.$watcher = {};  

      this.compile(this.$el);

      this.observe(this.$data);
    }
    compile(el) {
      let nodes = el.children;

      for (var i = 0, length = nodes.length; i < length; i++) {
        let node = nodes[i];
        if(node.children){
          this.compile(node);
        }
        if (node.hasAttribute("v-text")) {
          let attrVal = node.getAttribute("v-text");
          node.textContent = this.$data[attrVal];
        }
        if (node.hasAttribute("v-model")) {
          let attrVal = node.getAttribute("v-model");
          node.value = this.$data[attrVal];
          // 看這里??!只多了三行代碼!!
          node.addEventListener("input", (ev)=>{
            this.$data[attrVal] = ev.target.value;
            // 可以試著在這里執(zhí)行:console.log(this.$data),
            // 就可以看到每次在輸入框輸入文字的時候,data中的msg值也發(fā)生了變化
          })
        }
      }
    }
    observe(data) {}
  }

2.2.4 數(shù)據(jù)影響視圖

至此,我們已經(jīng)實(shí)現(xiàn)了:當(dāng)我們在input框中輸入字符的時候,data中的數(shù)據(jù)會自動發(fā)生更新;

本小節(jié)的主要任務(wù)是:當(dāng)data中的數(shù)據(jù)發(fā)生更新的時候,綁定了該數(shù)據(jù)的元素會在頁面上自動更新視圖。具體思路如下:

1) 我們將要實(shí)現(xiàn)一個 Wathcer 類,它有一個update方法,用來更新頁面。觀察者的代碼如下:

class Watcher{
    constructor(node, updatedAttr, vm, expression){
      // 將傳進(jìn)來的值保存起來,這些數(shù)據(jù)都是渲染頁面時要用到的數(shù)據(jù)
      this.node = node;
      this.updatedAttr = updatedAttr;
      this.vm = vm;
      this.expression = expression;
      this.update();
    }
    update(){
      this.node[this.updatedAttr] = this.vm.$data[this.expression];
    }
  }

2) 試想,我們該給哪些數(shù)據(jù)添加觀察者?何時給數(shù)據(jù)添加觀察者?

在解析元素的時候,當(dāng)解析到v-text和v-model指令的時候,說明這個元素是需要和數(shù)據(jù)雙向綁定的,因此我們在這時往容器中添加觀察者。我們需用到這樣一個數(shù)據(jù)結(jié)構(gòu):{屬性1: [wathcer1, wathcer2...], 屬性2: [...]},如果不是很清晰,可以看下圖:

可以看到:vue實(shí)例中有一個$wathcer對象,$wathcer的每個屬性對應(yīng)每個需要綁定的數(shù)據(jù),值是一個數(shù)組,用來存放觀察了該數(shù)據(jù)的觀察者。(備注:Vue源碼中專門創(chuàng)造了Dep這么一個類,對應(yīng)這里所說的數(shù)組,本文屬于簡易版本,就不過多介紹了)

3) 劫持?jǐn)?shù)據(jù):利用對象的訪問器屬性getter和setter做到當(dāng)數(shù)據(jù)更新的時候,觸發(fā)一個動作,這個動作的主要目的就是讓所有觀察了該數(shù)據(jù)的觀察者執(zhí)行update方法。

總結(jié)一下,在本小節(jié)我們需要做的工作:

  1. 實(shí)現(xiàn)一個Wathcer類;
  2. 在解析指令的時候(即在compile方法中)添加觀察者;
  3. 實(shí)現(xiàn)數(shù)據(jù)劫持(實(shí)現(xiàn)observe方法)。

完整代碼如下:

  class Vue {
    // 接收傳進(jìn)來的對象
    constructor(options) {
      // 獲取有用信息
      this.$el = document.querySelector(options.el);
      this.$data = options.data;

      // 容器: {屬性1: [wathcer1, wathcer2...], 屬性2: [...]}
      this.$watcher = {};

      // 解析元素: 實(shí)現(xiàn)Compile
      this.compile(this.$el); // 要解析元素, 就得把元素傳進(jìn)去

      // 劫持?jǐn)?shù)據(jù): 實(shí)現(xiàn) Observer
      this.observe(this.$data); // 要劫持?jǐn)?shù)據(jù), 就得把數(shù)據(jù)傳入
    }
    compile(el) {
      // 解析元素下的每一個子節(jié)點(diǎn), 所以要獲取el.children
      // 拓展: children 返回元素集合, childNodes返回節(jié)點(diǎn)集合
      let nodes = el.children;

      // 解析每個子節(jié)點(diǎn)的指令
      for (var i = 0, length = nodes.length; i < length; i++) {
        let node = nodes[i];
        // 如果當(dāng)前節(jié)點(diǎn)還有子元素, 遞歸解析該節(jié)點(diǎn)
        if (node.children) {
          this.compile(node);
        }
        if (node.hasAttribute("v-text")) {
          let attrVal = node.getAttribute("v-text");
          // node.textContent = this.$data[attrVal]; 
          // Watcher在實(shí)例化時調(diào)用update, 替代了這行代碼

          /**
           * 試想Wathcer要更新節(jié)點(diǎn)數(shù)據(jù)的時候要用到哪些數(shù)據(jù)? 
           * e.g.   p.innerHTML = vm.$data[msg]
           * 所以要傳入的參數(shù)依次是: 當(dāng)前節(jié)點(diǎn)node, 需要更新的節(jié)點(diǎn)屬性, vue實(shí)例, 綁定的數(shù)據(jù)屬性
          */
          // 往容器中添加觀察者: {msg1: [Watcher, Watcher...], msg2: [...]}
          if (!this.$watcher[attrVal]) {
            this.$watcher[attrVal] = [];
          }
          this.$watcher[attrVal].push(new Watcher(node, "innerHTML", this, attrVal))
        }
        if (node.hasAttribute("v-model")) {
          let attrVal = node.getAttribute("v-model");
          node.value = this.$data[attrVal];

          node.addEventListener("input", (ev) => {
            this.$data[attrVal] = ev.target.value;
          })

          if (!this.$watcher[attrVal]) {
            this.$watcher[attrVal] = [];
          }
          // 不同于上處用的innerHTML, 這里input用的是vaule屬性
          this.$watcher[attrVal].push(new Watcher(node, "value", this, attrVal))
        }
      }
    }
    observe(data) {
      Object.keys(data).forEach((key) => {
        let val = data[key];  // 這個val將一直保存在內(nèi)存中,每次訪問data[key],都是在訪問這個val
        Object.defineProperty(data, key, {
          get() {
            return val;  // 這里不能直接返回data[key],不然會陷入無限死循環(huán)
          },
          set(newVal) {
            if (val !== newVal) {
              val = newVal;// 同理,這里不能直接對data[key]進(jìn)行設(shè)置,會陷入死循環(huán)
              this.$watcher[key].forEach((w) => {
                w.update();
              })
            }
          }
        })
      })
    }
  }

  class Watcher {
    constructor(node, updatedAttr, vm, expression) {
      // 將傳進(jìn)來的值保存起來
      this.node = node;
      this.updatedAttr = updatedAttr;
      this.vm = vm;
      this.expression = expression;
      this.update();
    }
    update() {
      this.node[this.updatedAttr] = this.vm.$data[this.expression];
    }
  }

  let vm = new Vue({
    el: "#app",
    data: {
      msg: "hello world",
      msg2: "hello xiaofei"
    }
  })

至此,代碼就完成了。

3. 未來的計劃

用設(shè)計模式的知識,分析上面這份源碼存在的問題,并和Vue源碼進(jìn)行比對,算是對Vue源碼的解析

以上就是vue 數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于vue 數(shù)據(jù)雙向綁定的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue+Element實(shí)現(xiàn)分頁效果

    vue+Element實(shí)現(xiàn)分頁效果

    這篇文章主要為大家詳細(xì)介紹了vue+Element實(shí)現(xiàn)分頁效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Vue向后端傳數(shù)據(jù)后端接收為null問題及解決

    Vue向后端傳數(shù)據(jù)后端接收為null問題及解決

    這篇文章主要介紹了Vue向后端傳數(shù)據(jù)后端接收為null問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Vue 中使用 CSS Modules優(yōu)雅方法

    Vue 中使用 CSS Modules優(yōu)雅方法

    這篇文章主要介紹了Vue 中使用 CSS Modules優(yōu)雅方法,本文文字結(jié)合實(shí)例代碼的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2018-04-04
  • Vue中的組件注冊方法及注意事項(xiàng)

    Vue中的組件注冊方法及注意事項(xiàng)

    在Vue中,組件是構(gòu)建頁面的基本單位。通過組件化開發(fā),可以提高代碼的復(fù)用性和可維護(hù)性。組件的注冊方法包括全局注冊和局部注冊兩種方式。同時,需要注意組件名的命名規(guī)范、組件選項(xiàng)的定義方式、組件之間的通信等問題,以實(shí)現(xiàn)更好的組件復(fù)用和開發(fā)效率
    2023-05-05
  • Vue3屬性值傳遞defineProps詳解

    Vue3屬性值傳遞defineProps詳解

    在Vue3中,defineProps()函數(shù)是定義和接收組件屬性的主要方式,通過簡單定義或?qū)ο蠖x,開發(fā)者可以靈活地接收并處理組件上的屬性值,簡單定義方式通過數(shù)組傳遞屬性名,而對象定義則可以約束屬性的數(shù)據(jù)類型、默認(rèn)值及是否必須傳遞等
    2024-09-09
  • 解讀vant的Uploader上傳問題

    解讀vant的Uploader上傳問題

    這篇文章主要介紹了解讀vant的Uploader上傳問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue實(shí)現(xiàn)圖書管理系統(tǒng)

    vue實(shí)現(xiàn)圖書管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • vue打包上傳到服務(wù)器加載慢的優(yōu)化方式

    vue打包上傳到服務(wù)器加載慢的優(yōu)化方式

    這篇文章主要介紹了vue打包上傳到服務(wù)器加載慢的優(yōu)化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 從零到一詳聊創(chuàng)建Vue工程及遇到的常見問題

    從零到一詳聊創(chuàng)建Vue工程及遇到的常見問題

    這篇文章主要介紹了從零到一詳聊如何創(chuàng)建Vue工程及遇到的常見問題 ,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-04-04
  • vant中的Cascader級聯(lián)選擇異步加載地區(qū)數(shù)據(jù)方式

    vant中的Cascader級聯(lián)選擇異步加載地區(qū)數(shù)據(jù)方式

    這篇文章主要介紹了vant中的Cascader級聯(lián)選擇異步加載地區(qū)數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07

最新評論

霍邱县| 南乐县| 丁青县| 景洪市| 平利县| 磴口县| 洛扎县| 汉沽区| 郸城县| 平武县| 北流市| 禹城市| 渭源县| 米易县| 青河县| 鹤壁市| 合川市| 吉安县| 长沙市| 南宫市| 卓资县| 芮城县| 天津市| 龙泉市| 临清市| 余江县| 汝阳县| 东光县| 满洲里市| 巴里| 巍山| 莱州市| 九龙坡区| 甘洛县| 长沙县| 湘乡市| 垣曲县| 丽江市| 咸宁市| 乌什县| 府谷县|