欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue Object.defineProperty及ProxyVue實現(xiàn)雙向數(shù)據(jù)綁定

 更新時間:2020年09月02日 15:55:26   作者:vickylinj  
這篇文章主要介紹了Vue Object.defineProperty及ProxyVue實現(xiàn)雙向數(shù)據(jù)綁定,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

雙向數(shù)據(jù)綁定無非就是,視圖 => 數(shù)據(jù),數(shù)據(jù) => 視圖的更新過程

以下的方案中的實現(xiàn)思路:

  • 定義一個Vue的構(gòu)造函數(shù)并初始化這個函數(shù)(myVue.prototype._init)
  • 實現(xiàn)數(shù)據(jù)層的更新:數(shù)據(jù)劫持,定義一個 obverse 函數(shù)重寫data的set和get(myVue.prototype._obsever)
  • 實現(xiàn)視圖層的更新:訂閱者模式,定義個 Watcher 函數(shù)實現(xiàn)對DOM的更新(Watcher)
  • 將數(shù)據(jù)和視圖層進(jìn)行綁定,解析指令v-bind、v-model、v-click(myVue.prototype._compile)
  • 創(chuàng)建Vue實例(new myVue)

1.object.defineproperty方式實現(xiàn)雙向數(shù)據(jù)綁定

<!DOCTYPE html>
<html>
 
<head>
 <title>myVue</title>
 <style>
  #app{
  text-align: center;
 }
</style>
</head>
 
<body>
 <div id="app">
  <form>
   <input type="text" v-model="number" />
   <button type="button" v-click="increment">增加</button>
  </form>
  <h3 v-bind="number"></h3>
 </div>
</body>
<script>
 
 // 定義一個myVue構(gòu)造函數(shù)
 function myVue(option) {
  this._init(option)
 }
 
 myVue.prototype._init = function (options) { // 傳了一個配置對象
  this.$options = options // options 為上面使用時傳入的結(jié)構(gòu)體,包括el,data,methods
  this.$el = document.querySelector(options.el) // el是 #app, this.$el是id為app的Element元素
  this.$data = options.data // this.$data = {number: 0}
  this.$methods = options.methods // this.$methods = {increment: function(){}}
 
 
  // _binding保存著model與view的映射關(guān)系,也就是我們前面定義的Watcher的實例。當(dāng)model改變時,我們會觸發(fā)其中的指令類更新,保證view也能實時更新
  this._binding = {}
 
  this._obsever(this.$data)
  this._compile(this.$el)
 }
 
 // 數(shù)據(jù)劫持:更新數(shù)據(jù)
 myVue.prototype._obsever = function (obj) {
  let _this = this
  Object.keys(obj).forEach((key) => { // 遍歷obj對象
   if (obj.hasOwnProperty(key)) { // 判斷 obj 對象是否包含 key屬性
    _this._binding[key] = [] // 按照前面的數(shù)據(jù),_binding = {number: []} 存儲 每一個 new Watcher
   }
   let value = obj[key]
   if (typeof value === 'object') { //如果值還是對象,則遍歷處理
    _this._obsever(value)
   }
   Object.defineProperty(_this.$data, key, {
    enumerable: true,
    configurable: true,
    get: () => { // 獲取 value 值
     return value
    },
    set: (newVal) => { // 更新 value 值
     if (value !== newVal) {
      value = newVal
      _this._binding[key].forEach((item) => { // 當(dāng)number改變時,觸發(fā)_binding[number] 中的綁定的Watcher類的更新
       item.update() // 調(diào) Watcher 實例的 update 方法更新 DOM
      })
     }
    }
   })
  })
 }
 
 // 訂閱者模式: 綁定更新函數(shù),實現(xiàn)對 DOM 元素的更新
 function Watcher(el, data, key, attr) {
  this.el = el // 指令對應(yīng)的DOM元素
  this.data = data // this.$data 數(shù)據(jù): {number: 0, count: 0}
  this.key = key // 指令綁定的值,本例如"number"
  this.attr = attr // 綁定的屬性值,本例為"innerHTML","value"
 
  this.update()
 }
 // 比如 H3.innerHTML = this.data.number; 當(dāng)number改變時,會觸發(fā)這個update函數(shù),保證對應(yīng)的DOM內(nèi)容進(jìn)行了更新
 Watcher.prototype.update = function () {
  this.el[this.attr] = this.data[this.key]
 }
 
 // 將view與model進(jìn)行綁定,解析指令(v-bind,v-model,v-clickde)等
 myVue.prototype._compile = function (el) { // root 為id為app的Element元素,也就是我們的根元素
  let _this = this
  let nodes = Array.prototype.slice.call(el.children) // 將為數(shù)組轉(zhuǎn)化為真正的數(shù)組
  nodes.map(node => {
   if (node.children.length && node.children.length > 0) { // 對所有元素進(jìn)行遍歷,并進(jìn)行處理
    _this._compile(node)
   }
   if (node.hasAttribute('v-click')) { // 如果有v-click屬性,我們監(jiān)聽它的onclick事件,觸發(fā)increment事件,即number++
    let attrVal = node.getAttribute('v-click')
    node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind是使data的作用域與method函數(shù)的作用域保持一致
   }
 
   // 如果有v-model屬性,并且元素是INPUT或者TEXTAREA,我們監(jiān)聽它的input事件
   if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
    let attrVal = node.getAttribute('v-model')
 
    _this._binding[attrVal].push(new Watcher(
     node, // 對應(yīng)的 DOM 節(jié)點
     _this.$data,
     attrVal, // v-model 綁定的值
     'value'
    ))
    node.addEventListener('input', () => {
     _this.$data[attrVal] = node.value // 使number 的值與 node的value保持一致,已經(jīng)實現(xiàn)了雙向綁定
    })
   }
   if (node.hasAttribute('v-bind')) {
    let attrVal = node.getAttribute('v-bind')
    _this._binding[attrVal].push(new Watcher(
     node,
     _this.$data,
     attrVal, // v-bind 綁定的值
     'innerHTML'
    ))
   }
  })
 }
 
 
 window.onload = () => { // 當(dāng)文檔內(nèi)容完全加載完成會觸發(fā)該事件,避免獲取不到對象的情況
  new myVue({
   el: '#app',
   data: {
    number: 0,
    count: 0
   },
   methods: {
    increment() {
     this.number++
    },
    incre() {
     this.count++
    }
   }
  })
 }
</script>
 
</html>

2.Proxy 實現(xiàn)雙向數(shù)據(jù)綁定

<!DOCTYPE html>
<html>
 
<head>
 <title>myVue</title>
 <style>
  #app{
  text-align: center;
 }
</style>
</head>
 
<body>
 <div id="app">
  <form>
   <input type="text" v-model="number" />
   <button type="button" v-click="increment">增加</button>
  </form>
  <h3 v-bind="number"></h3>
 </div>
</body>
<script>
 
 // 定義一個myVue構(gòu)造函數(shù)
 function myVue(option) {
  this._init(option)
 }
 
 myVue.prototype._init = function (options) { // 傳了一個配置對象
  this.$options = options // options 為上面使用時傳入的結(jié)構(gòu)體,包括el,data,methods
  this.$el = document.querySelector(options.el) // el是 #app, this.$el是id為app的Element元素
  this.$data = options.data // this.$data = {number: 0}
  this.$methods = options.methods // this.$methods = {increment: function(){}}
 
  this._binding = {}
  this._obsever(this.$data)
  this._complie(this.$el)
 
 }
 
// 數(shù)據(jù)劫持:更新數(shù)據(jù)
myVue.prototype._obsever = function (data) {
  let _this = this
  let handler = {
   get(target, key) {
    return target[key]; // 獲取該對象上key的值
   },
   set(target, key, newValue) {
    let res = Reflect.set(target, key, newValue); // 將新值分配給屬性的函數(shù)
    _this._binding[key].map(item => {
     item.update();
    });
    return res;
   }
  };
  // 把代理器返回的對象代理到this.$data,即this.$data是代理后的對象,外部每次對this.$data進(jìn)行操作時,實際上執(zhí)行的是這段代碼里handler對象上的方法
  this.$data = new Proxy(data, handler);
 }
 
 // 將view與model進(jìn)行綁定,解析指令(v-bind,v-model,v-clickde)等
 myVue.prototype._complie = function (el) { // el 為id為app的Element元素,也就是我們的根元素
  let _this = this
  let nodes = Array.prototype.slice.call(el.children) // 將為數(shù)組轉(zhuǎn)化為真正的數(shù)組
 
  nodes.map(node => {
   if (node.children.length && node.children.length > 0) this._complie(node)
   if (node.hasAttribute('v-click')) { // 如果有v-click屬性,我們監(jiān)聽它的onclick事件,觸發(fā)increment事件,即number++
    let attrVal = node.getAttribute('v-click')
    node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind是使data的作用域與method函數(shù)的作用域保持一致
   }
 
   // 如果有v-model屬性,并且元素是INPUT或者TEXTAREA,我們監(jiān)聽它的input事件
   if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
    let attrVal = node.getAttribute('v-model')
    
    console.log(_this._binding)
    if (!_this._binding[attrVal]) _this._binding[attrVal] = []
    _this._binding[attrVal].push(new Watcher(
     node, // 對應(yīng)的 DOM 節(jié)點
     _this.$data,
     attrVal, // v-model 綁定的值
     'value',
    ))
    node.addEventListener('input', () => {
     _this.$data[attrVal] = node.value // 使number 的值與 node的value保持一致,已經(jīng)實現(xiàn)了雙向綁定
    })
   }
   if (node.hasAttribute('v-bind')) {
    let attrVal = node.getAttribute('v-bind')
    if (!_this._binding[attrVal]) _this._binding[attrVal] = []
    _this._binding[attrVal].push(new Watcher(
     node,
     _this.$data,
     attrVal, // v-bind 綁定的值
     'innerHTML',
    ))
   }
 
  })
 }
 // 綁定更新函數(shù),實現(xiàn)對 DOM 元素的更新
 function Watcher(el, data, key, attr) {
  this.el = el // 指令對應(yīng)的DOM元素
  this.data = data // 代理的對象 this.$data 數(shù)據(jù): {number: 0, count: 0}
  this.key = key // 指令綁定的值,本例如"num"
  this.attr = attr // 綁定的屬性值,本例為"innerHTML","value"
 
  this.update()
 }
 // 比如 H3.innerHTML = this.data.number; 當(dāng)number改變時,會觸發(fā)這個update函數(shù),保證對應(yīng)的DOM內(nèi)容進(jìn)行了更新
 Watcher.prototype.update = function () {
  this.el[this.attr] = this.data[this.key]
 }
 
 window.onload = () => { // 當(dāng)文檔內(nèi)容完全加載完成會觸發(fā)該事件,避免獲取不到對象的情況
  new myVue({
   el: '#app',
   data: {
    number: 0,
    count: 0
   },
   methods: {
    increment() {
     this.number++
    },
    incre() {
     this.count++
    }
   }
  })
 }
</script>
 
</html>

3.將上面代碼改成class的寫法

<!DOCTYPE html>
<html>
 
<head>
 <title>myVue</title>
 <style>
  #app{
  text-align: center;
 }
</style>
</head>
 
<body>
 <div id="app">
  <form>
   <input type="text" v-model="number" />
   <button type="button" v-click="increment">增加</button>
  </form>
  <h3 v-bind="number"></h3>
 </div>
</body>
<script>
 
 class MyVue {
  constructor(options) { // 接收了一個配置對象
   this.$options = options // options 為上面使用時傳入的結(jié)構(gòu)體,包括el,data,methods
   this.$el = document.querySelector(options.el) // el是 #app, this.$el是id為app的Element元素
   this.$data = options.data // this.$data = {number: 0}
   this.$methods = options.methods // this.$methods = {increment: function(){}}
 
   this._binding = {}
   this._obsever(this.$data)
   this._complie(this.$el)
  }
  _obsever (data) { // 數(shù)據(jù)劫持:更新數(shù)據(jù)
   let _this = this
   let handler = {
    get(target, key) {
     return target[key]; // 獲取該對象上key的值
    },
    set(target, key, newValue) {
     let res = Reflect.set(target, key, newValue); // 將新值分配給屬性的函數(shù)
     _this._binding[key].map(item => {
      item.update();
     });
     return res;
    }
   };
   // 把代理器返回的對象代理到this.$data,即this.$data是代理后的對象,外部每次對this.$data進(jìn)行操作時,實際上執(zhí)行的是這段代碼里handler對象上的方法
   this.$data = new Proxy(data, handler);
  }
  _complie(el) { // el 為id為app的Element元素,也就是我們的根元素
   let _this = this
   let nodes = Array.prototype.slice.call(el.children) // 將為數(shù)組轉(zhuǎn)化為真正的數(shù)組
 
   nodes.map(node => {
    if (node.children.length && node.children.length > 0) this._complie(node)
    if (node.hasAttribute('v-click')) { // 如果有v-click屬性,我們監(jiān)聽它的onclick事件,觸發(fā)increment事件,即number++
     let attrVal = node.getAttribute('v-click')
     node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind是使data的作用域與method函數(shù)的作用域保持一致
    }
 
    // 如果有v-model屬性,并且元素是INPUT或者TEXTAREA,我們監(jiān)聽它的input事件
    if (node.hasAttribute('v-model') && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA')) {
     let attrVal = node.getAttribute('v-model')
     if (!_this._binding[attrVal]) _this._binding[attrVal] = []
     _this._binding[attrVal].push(new Watcher(
      node, // 對應(yīng)的 DOM 節(jié)點
      _this.$data,
      attrVal, // v-model 綁定的值
      'value',
     ))
     node.addEventListener('input', () => {
      _this.$data[attrVal] = node.value // 使number 的值與 node的value保持一致,已經(jīng)實現(xiàn)了雙向綁定
     })
    }
    if (node.hasAttribute('v-bind')) {
     let attrVal = node.getAttribute('v-bind')
     if (!_this._binding[attrVal]) _this._binding[attrVal] = []
     _this._binding[attrVal].push(new Watcher(
      node,
      _this.$data,
      attrVal, // v-bind 綁定的值
      'innerHTML',
     ))
    }
 
   })
  }
 }
 
 class Watcher {
  constructor (el, data, key, attr) {
   this.el = el // 指令對應(yīng)的DOM元素
   this.data = data // 代理的對象 this.$data 數(shù)據(jù): {number: 0, count: 0}
   this.key = key // 指令綁定的值,本例如"num"
   this.attr = attr // 綁定的屬性值,本例為"innerHTML","value"
   this.update()
  }
 
  update () {
   this.el[this.attr] = this.data[this.key]
  }
 }
 
 
 
 window.onload = () => { // 當(dāng)文檔內(nèi)容完全加載完成會觸發(fā)該事件,避免獲取不到對象的情況
  new MyVue({
   el: '#app',
   data: {
    number: 0,
    count: 0
   },
   methods: {
    increment() {
     this.number++
    },
    incre() {
     this.count++
    }
   }
  })
 }
</script>
 
</html>

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue修改對象的屬性值后頁面不重新渲染的實例

    vue修改對象的屬性值后頁面不重新渲染的實例

    今天小編就為大家分享一篇vue修改對象的屬性值后頁面不重新渲染的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • element-ui中el-form-item內(nèi)的el-select該如何自適應(yīng)寬度

    element-ui中el-form-item內(nèi)的el-select該如何自適應(yīng)寬度

    自從用了element-ui,確實好用,該有的組件都有,但是組件間的樣式都固定好了,下面這篇文章主要給大家介紹了關(guān)于element-ui中el-form-item內(nèi)的el-select該如何自適應(yīng)寬度的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • 基于vue.js快速搭建圖書管理平臺

    基于vue.js快速搭建圖書管理平臺

    Vue.js是當(dāng)下很火的一個JavaScript MVVM(Model-View-ViewModel)庫.這篇文章主要介紹了基于vue.js快速搭建圖書管理平臺 ,需要的朋友可以參考下
    2017-10-10
  • 在vue中更換字體,本地存儲字體非引用在線字體庫的方法

    在vue中更換字體,本地存儲字體非引用在線字體庫的方法

    今天小編就為大家分享一篇在vue中更換字體,本地存儲字體非引用在線字體庫的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • 前端token中4個存儲位置的優(yōu)缺點說明

    前端token中4個存儲位置的優(yōu)缺點說明

    這篇文章主要介紹了前端token中4個存儲位置的優(yōu)缺點說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue3沒有this的解決方案

    vue3沒有this的解決方案

    這篇文章主要介紹了vue3沒有this的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • vue分片上傳視頻并轉(zhuǎn)換為m3u8文件播放的實現(xiàn)示例

    vue分片上傳視頻并轉(zhuǎn)換為m3u8文件播放的實現(xiàn)示例

    前端上傳大文件、視頻的時候會出現(xiàn)超時、過大、很慢等情況,為了解決這一問題,跟后端配合做了一個切片的功能,本文主要介紹了vue分片上傳視頻并轉(zhuǎn)換為m3u8文件播放的實現(xiàn)示例,感興趣的可以了解一下
    2023-11-11
  • Vue NextTick介紹與使用原理

    Vue NextTick介紹與使用原理

    我們對Vue中data數(shù)據(jù)的修改會導(dǎo)致界面對應(yīng)的響應(yīng)變化,而通過nextTick方法,可以在傳入nextTick的回調(diào)函數(shù)中獲取到變化后的DOM,講起來可能還是有點夢幻,下面我們直接使用nextTick體驗一下效果
    2022-08-08
  • vue keep-alive列表頁緩存 詳情頁返回上一頁不刷新,定位到之前位置

    vue keep-alive列表頁緩存 詳情頁返回上一頁不刷新,定位到之前位置

    這篇文章主要介紹了vue keep-alive列表頁緩存 詳情頁返回上一頁不刷新,定位到之前位置,本文通過實例代碼效果圖展示給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2019-11-11
  • Vue實戰(zhàn)教程之仿肯德基宅急送App

    Vue實戰(zhàn)教程之仿肯德基宅急送App

    這篇文章主要介紹了Vue實戰(zhàn)教程之仿肯德基宅急送App,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-07-07

最新評論