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

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)系,當它本身的狀態(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)得到主題通知時更新自己的邏輯。第一行將當前觀察者綁定到函數(shù)屬性上面,是為了避免全局作用域變量。

function Observer(vm, node, name, nodeType) {
  // 構(gòu)造函數(shù)被調(diào)用時,將當前對象綁定到函數(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 對象,為每個屬性都生成一個主題(包含當前屬性的觀察者數(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;
        // 給當前主題所有登記過的觀察者發(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封裝全局toast組件的完整實例

    Vue封裝全局toast組件的完整實例

    組件(Component)是 Vue.js 最強大的功能之一,組件可以擴展 HTML 元素,封裝可重用的代碼,這篇文章主要給大家介紹了關(guān)于Vue封裝全局toast組件,需要的朋友可以參考下
    2021-07-07
  • Vue如何優(yōu)雅的清除定時器

    Vue如何優(yōu)雅的清除定時器

    定時器如果不及時合理地清除,會造成業(yè)務(wù)邏輯混亂甚至應(yīng)用卡死的情況,這個時就需要清除定時器,本文就介紹了Vue如何優(yōu)雅的清除定時器,感興趣的可以了解一下
    2021-07-07
  • vue通過?API?監(jiān)聽數(shù)組的變化

    vue通過?API?監(jiān)聽數(shù)組的變化

    這篇文章主要介紹了vue通過?API?監(jiān)聽數(shù)組的變化,在?Vue?中,你可以通過監(jiān)聽數(shù)組的變化來更新界面,Vue?提供了一些特殊的語法以及?API?來實現(xiàn)對數(shù)組的監(jiān)聽,本文通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • 詳解vue3的沙箱機制

    詳解vue3的沙箱機制

    這篇文章主要介紹了vue3的沙箱機制的相關(guān)資料,幫助大家更好的理解和學(xué)習使用vue框架,感興趣的朋友可以了解下
    2021-04-04
  • Vue實現(xiàn)記住賬號密碼功能的操作過程

    Vue實現(xiàn)記住賬號密碼功能的操作過程

    這篇文章主要介紹了Vue實現(xiàn)記住賬號密碼功能,用戶登錄時若勾選“記住我”功能選項,則將登錄名和密碼(加密后)存入本地緩存,下次登錄頁面加載時自動獲取保存好的賬號和密碼(需解密),回顯到登錄輸入框中,下面分享我實現(xiàn)的具體步驟,需要的朋友可以參考下
    2022-07-07
  • vue3-HOOKS模塊化處理方式

    vue3-HOOKS模塊化處理方式

    這篇文章主要介紹了vue3-HOOKS模塊化處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 淺談vue.use()方法從源碼到使用

    淺談vue.use()方法從源碼到使用

    這篇文章主要介紹了淺談vue.use()方法從源碼到使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2019-05-05
  • vue.js基于ElementUI封裝了CRUD的彈框組件

    vue.js基于ElementUI封裝了CRUD的彈框組件

    這篇文章主要介紹了vue.js基于ElementUI封裝了CRUD的彈框組件,問咋會給你圍繞主題展開詳細的內(nèi)容介紹,感興趣的小伙伴可以參考一下
    2022-07-07
  • Vue + CSS實現(xiàn)漸變柵格進度條效果

    Vue + CSS實現(xiàn)漸變柵格進度條效果

    基于Vue和CSS實現(xiàn)漸變柵格進度條,通過劃分柵格單元、顏色漸變、標簽與刻度的絕對定位來實現(xiàn)高度靈活和交互性,進度條劃分為多個柵格,每個單元格顏色漸變并且連續(xù),可以根據(jù)不同的場景配置個性化屬性,具有極高的靈活度和交互性,感興趣的朋友一起看看吧
    2025-03-03
  • Vue中v-for的數(shù)據(jù)分組實例

    Vue中v-for的數(shù)據(jù)分組實例

    下面小編就為大家分享一篇Vue中v-for的數(shù)據(jù)分組實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03

最新評論

凌云县| 玉溪市| 分宜县| 清流县| 宝兴县| 大渡口区| 新昌县| 肇庆市| 麻江县| 天全县| 吴忠市| 凉城县| 秦皇岛市| 惠州市| 益阳市| 崇礼县| 重庆市| 楚雄市| 定西市| 林芝县| 花莲县| 平原县| 长兴县| 扶绥县| 佛冈县| 泾源县| 青神县| 大厂| 大埔区| 出国| 宣恩县| 大渡口区| 望江县| 左贡县| 泸定县| 东光县| 瑞昌市| 南郑县| 日喀则市| 青岛市| 乾安县|