亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue2?與?Vue3?的數(shù)據(jù)綁定原理及實現(xiàn)

 更新時間:2022年09月29日 11:54:53   作者:zkj???????  
這篇文章主要介紹了Vue2與Vue3的數(shù)據(jù)綁定原理及實現(xiàn),數(shù)據(jù)綁定是一種把用戶界面元素的屬性綁定到特定對象上面并使其同步的機制,使開發(fā)人員免于編寫同步視圖模型和視圖的邏輯

介紹

數(shù)據(jù)綁定是一種把用戶界面元素(控件)的屬性綁定到特定對象上面并使其同步的機制,使開發(fā)人員免于編寫同步視圖模型和視圖的邏輯。

觀察者模式.webp

觀察者模式又稱為發(fā)布-訂閱模式,定義對象間的一種一對多的依賴關(guān)系,當(dāng)它本身的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。比如用戶界面可以作為一個觀察者,業(yè)務(wù)數(shù)據(jù)是被觀察者,用戶界面觀察業(yè)務(wù)數(shù)據(jù)的變化,發(fā)現(xiàn)數(shù)據(jù)變化后,就同步顯示在界面上。這樣可以確保界面和數(shù)據(jù)之間劃清界限,假定應(yīng)用程序的需求發(fā)生變化,需要修改界面的表現(xiàn),只需要重新構(gòu)建一個用戶界面,業(yè)務(wù)數(shù)據(jù)不需要發(fā)生變化。有以下幾個角色:

  • 抽象主題(Subject):提供一個接口,把所有觀察者對象的引用保存到一個集合里,可以增加和刪除觀察者對象。
  • 具體主題(Concrete Subject):將有關(guān)狀態(tài)信息存入觀察者對象,在本身的內(nèi)部狀態(tài)改變時,給所有登記過的觀察者發(fā)出通知。
  • 抽象觀察者(Observer):為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
  • 具體觀察者(Concrete Observer):實現(xiàn)更新接口。

Vue2 和 Vue3 的數(shù)據(jù)綁定都是觀察者模式的實現(xiàn),前者使用 Object.defineProperty,后者使用的是 Proxy。

有以下 HTML:

<div id="app">
  <input type="radio" name="hello" id="hello" value="hello" v-model="title">
  <label for="hello">hello</label>
  <input type="radio" name="hello" id="hello2" value="hello2" v-model="title">
  <label for="hello2">hello2</label>
  <div v-bind="title"></div>
  <input v-model="content">
  <select v-model="content">
    <option>world</option>
    <option>world1</option>
    <option>world2</option>
  </select>
  <div v-bind="content"></div>
  <input type="checkbox" id="hobby1" value="hobby1" v-model="hobby">
  <label for="hobby1">hobby1</label>
  <input type="checkbox" id="hobby2" value="hobby2" v-model="hobby">
  <label for="hobby2">hobby2</label>
  <input type="checkbox" id="hobby3" value="hobby3" v-model="hobby">
  <label for="hobby3">hobby3</label>
  <br>
  {{ hobby }}
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      title: 'hello',
      content: 'world2',
      hobby: ['hobby2'],
    }
  });
</script>

下面使用兩種方法進行簡單實現(xiàn)上面的雙向綁定。

Object.defineProperty

語法:

Object.defineProperty(obj, prop, descriptor)
  • obj:要定義屬性的對象。
  • prop:要定義或修改的屬性的名稱或 Symbol 。
  • descriptor:要定義或修改的屬性描述符。
  • 返回值:被傳遞給函數(shù)的對象。

首先定義一個觀察者構(gòu)造函數(shù),并實現(xiàn)得到主題通知時更新自己的邏輯。第一行將當(dāng)前觀察者綁定到函數(shù)屬性上面,是為了避免全局作用域變量。

function Observer(vm, node, name, nodeType) {
  // 構(gòu)造函數(shù)被調(diào)用時,將當(dāng)前對象綁定到函數(shù)屬性上面,接下來觸發(fā) getter 時使用
  Observer.target = this;
  this.update = () => {
    // 這里 vm[name] 讀取操作會觸發(fā) getter
    if (node.type === 'radio') node.checked = node.value === vm[name];
    else if (node.type !== 'checkbox') node[nodeType] = vm[name];
  };
  this.update();
  Observer.target = null; // 設(shè)置為空,避免首次觸發(fā)get后重復(fù)添加
}

然后定義 Vue 構(gòu)造函數(shù),遍歷 options.data 對象,為每個屬性都生成一個主題(包含當(dāng)前屬性的觀察者數(shù)組),然后使用 Object.defineProperty 劫持屬性的讀取和寫入操作,在首次讀取時添加一個對應(yīng)的觀察者對象,為了避免后面讀取操作重復(fù)添加,在觀察者構(gòu)造函數(shù)里面首次更新操作完成后設(shè)置了空。

function Vue(options) {
  const obj = options.data;
  Object.keys(obj).forEach(key => {
    const subjects = [];
    Object.defineProperty(this, key, {
      get() {
        if (Observer.target) subjects.push(Observer.target);
        return obj[key];
      },
      set(newVal) {
        if (newVal === obj[key]) return;
        obj[key] = newVal;
        // 給當(dāng)前主題所有登記過的觀察者發(fā)出通知
        subjects.forEach(observer => observer.update());
      }
    });
  });
}

接下來就是遍歷根節(jié)點(這里只遍歷一層),根據(jù)子節(jié)點的類型,傳入不同的參數(shù)調(diào)用 Observer 構(gòu)造函數(shù),然后首次更新視圖,并觸發(fā) getter 將觀察者對象都對應(yīng)放到 options.data 的每個屬性主題中,然后按屬性類型添加不同的事件監(jiān)聽。

const el = document.querySelector(options.el);
el.childNodes.forEach(node => {
  if (node.nodeType === 1) {
    if (node.hasAttribute('v-model')) {
      const name = node.getAttribute('v-model');
      if (node.type === 'checkbox') node.checked = this[name].includes(node.value);
      const eventType = (node.tagName === 'INPUT' && node.type === 'text') || node.tagName == 'TEXTAREA' ? 'input' : 'change';
      node.addEventListener(eventType, e => {
        // 這里 this[name] 寫入操作會觸發(fā) setter
        if (node.type === 'checkbox') {
          if (node.checked) this[name] = this[name].concat(node.value).sort();
          else this[name] = this[name].filter(v => v !== node.value).sort();
        } else this[name] = node.value;
      });
      new Observer(this, node, name, 'value');
    } else if (node.hasAttribute('v-bind')) {
      new Observer(this, node, node.getAttribute('v-bind'), 'textContent');
    }
  } else if (node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.nodeValue)) {
    new Observer(this, node, RegExp.$1.trim(), 'nodeValue');
  }
});
<div id="app">
  <input type="radio" name="hello" id="hello" value="hello" v-model="title">
  <label for="hello">hello</label>
  <input type="radio" name="hello" id="hello2" value="hello2" v-model="title">
  <label for="hello2">hello2</label>
  <p v-bind="title"></p>
  <input v-model="content">
  <select v-model="content">
    <option>world</option>
    <option>world1</option>
    <option>world2</option>
  </select>
  <p v-bind="content"></p>
  <input type="checkbox" id="hobby1" value="hobby1" v-model="hobby">
  <label for="hobby1">hobby1</label>
  <input type="checkbox" id="hobby2" value="hobby2" v-model="hobby">
  <label for="hobby2">hobby2</label>
  <input type="checkbox" id="hobby3" value="hobby3" v-model="hobby">
  <label for="hobby3">hobby3</label>
  <br>
  {{ hobby }}
</div>

運行:

Proxy

語法:

new Proxy(target, handler)
  • target:被代理的對象
  • handler:被代理對象上的自定義行為,和 Reflect 對象的所有靜態(tài)方法對應(yīng),所以可以在其中調(diào)用對應(yīng)的 Reflect 方法,完成默認行為,然后再部署額外的功能。

第一步定義觀察者構(gòu)造函數(shù),和 Object.defineProperty 方式相同。

第二步也是定義 Vue 構(gòu)造函數(shù),不同的是使用 Proxy 劫持屬性的讀取和寫入操作,不需要為 options.data 對象每個屬性都添加主題了。其他和 Object.defineProperty 方式相同。

function Vue(options) {
  const subjects = [];
  this.proxy = new Proxy(options.data, {
    get(obj, key, receiver) {
      if (Observer.target) subjects.push(Observer.target);
      const value = Reflect.get(...arguments);
      return value;
    },
    set(obj, key, value, receiver) {
      if (value === obj[key]) return;
      const result = Reflect.set(...arguments);
      subjects.forEach(observer => observer.update());
      return result;
    }
  });
}

第三步遍歷根節(jié)點,觸發(fā) getter 將觀察者對象都放到主題的數(shù)組中,然后添加事件監(jiān)聽時,要觸發(fā) Proxy 的寫入操作,而不是原對象。

const el = document.querySelector(options.el);
el.childNodes.forEach(node => {
  if (node.nodeType === 1) {
    if (node.hasAttribute('v-model')) {
      const name = node.getAttribute('v-model');
      if (node.type === 'checkbox') node.checked = this.proxy[name].includes(node.value);
      const eventType = (node.tagName === 'INPUT' && node.type === 'text') || node.tagName == 'TEXTAREA' ? 'input' : 'change';
      node.addEventListener(eventType, e => {
        // 這里 this.proxy[name] 寫入操作會觸發(fā) setter
        if (node.type === 'checkbox') {
          let value = this.proxy[name];
          if (node.checked) {
            this.proxy[name] = value.concat(node.value).sort();
          } else this.proxy[name] = value.filter(v => v !== node.value).sort();
        } else this.proxy[name] = node.value;
      });
      new Observer(this.proxy, node, name, 'value');
    } else if (node.hasAttribute('v-bind')) {
      new Observer(this.proxy, node, node.getAttribute('v-bind'), 'textContent');
    }
  } else if (node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.nodeValue)) {
    new Observer(this.proxy, node, RegExp.$1.trim(), 'nodeValue');
  }
});
<div id="app">
  <input type="radio" name="hello" id="hello" value="hello" v-model="title">
  <label for="hello">hello</label>
  <input type="radio" name="hello" id="hello2" value="hello2" v-model="title">
  <label for="hello2">hello2</label>
  <p v-bind="title"></p>
  <input v-model="content">
  <select v-model="content">
    <option>world</option>
    <option>world1</option>
    <option>world2</option>
  </select>
  <p v-bind="content"></p>
  <input type="checkbox" id="hobby1" value="hobby1" v-model="hobby">
  <label for="hobby1">hobby1</label>
  <input type="checkbox" id="hobby2" value="hobby2" v-model="hobby">
  <label for="hobby2">hobby2</label>
  <input type="checkbox" id="hobby3" value="hobby3" v-model="hobby">
  <label for="hobby3">hobby3</label>
  <br>
  {{ hobby }}
</div>

 運行:

到此這篇關(guān)于Vue2 與 Vue3 的數(shù)據(jù)綁定原理及實現(xiàn)的文章就介紹到這了,更多相關(guān)Vue數(shù)據(jù)綁定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue.js項目實戰(zhàn)之多語種網(wǎng)站的功能實現(xiàn)(租車)

    Vue.js項目實戰(zhàn)之多語種網(wǎng)站的功能實現(xiàn)(租車)

    這篇文章主要介紹了Vue.js項目實戰(zhàn)之多語種網(wǎng)站(租車)的功能實現(xiàn) ,需要的朋友可以參考下
    2019-08-08
  • 詳解Vue3的七種組件通信方式

    詳解Vue3的七種組件通信方式

    本篇文章將詳解介紹Vue3中如下七種組件通信方式:props、emit、v-model、refs、provide/inject、eventBus、vuex/pinia(狀態(tài)管理工具)。感興趣的可以了解一下
    2022-02-02
  • vue選項卡組件的實現(xiàn)方法

    vue選項卡組件的實現(xiàn)方法

    這篇文章主要為大家詳細介紹了vue選項卡組件的實現(xiàn)方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue實現(xiàn)虛擬滾動渲染成千上萬條數(shù)據(jù)

    vue實現(xiàn)虛擬滾動渲染成千上萬條數(shù)據(jù)

    本文主要介紹了vue實現(xiàn)虛擬滾動渲染成千上萬條數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • vue二級菜單導(dǎo)航點擊選中事件的方法

    vue二級菜單導(dǎo)航點擊選中事件的方法

    今天小編就為大家分享一篇vue二級菜單導(dǎo)航點擊選中事件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue中require與import的區(qū)別詳解

    vue中require與import的區(qū)別詳解

    這篇文章主要介紹了vue中require與import的區(qū)別詳解,require相當(dāng)于module.exports的傳送門,module.exports后面的內(nèi)容是什么,require的結(jié)果就是什么,對象、數(shù)字、字符串、函數(shù),再把require的結(jié)果賦值給某個變量,需要的朋友可以參考下
    2023-10-10
  • Vue?非常實用的自定義指令分享

    Vue?非常實用的自定義指令分享

    這篇文章主要介紹了Vue?非常實用的自定義指令分享,Vue自定義指令有全局注冊和局部注冊兩種方式,在?Vue,除了核心功能默認內(nèi)置的指令?(?v-model?和?v-show?),Vue?也允許注冊自定義指令,下文小編給大家分享那些常用到的Vue自定義指令
    2022-02-02
  • Vue 全局loading組件實例詳解

    Vue 全局loading組件實例詳解

    這篇文章主要介紹了Vue 全局loading組件,需要的朋友可以參考下
    2018-05-05
  • vue項目接口訪問地址設(shè)置方式

    vue項目接口訪問地址設(shè)置方式

    這篇文章主要介紹了vue項目接口訪問地址設(shè)置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 基于Vue的延遲加載插件vue-view-lazy

    基于Vue的延遲加載插件vue-view-lazy

    這篇文章主要介紹了基于Vue的延遲加載插件vue-view-lazy,可以使圖片或者其他資源進入可視區(qū)域后加載,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05

最新評論