日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Vue Object.defineProperty及ProxyVue實現雙向數據綁定

瀏覽:299日期:2022-11-30 10:48:04

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

Vue Object.defineProperty及ProxyVue實現雙向數據綁定

以下的方案中的實現思路:

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

1.object.defineproperty方式實現雙向數據綁定

<!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構造函數 function myVue(option) { this._init(option) } myVue.prototype._init = function (options) { // 傳了一個配置對象 this.$options = options // options 為上面使用時傳入的結構體,包括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的映射關系,也就是我們前面定義的Watcher的實例。當model改變時,我們會觸發其中的指令類更新,保證view也能實時更新 this._binding = {} this._obsever(this.$data) this._compile(this.$el) } // 數據劫持:更新數據 myVue.prototype._obsever = function (obj) { let _this = this Object.keys(obj).forEach((key) => { // 遍歷obj對象 if (obj.hasOwnProperty(key)) { // 判斷 obj 對象是否包含 key屬性 _this._binding[key] = [] // 按照前面的數據,_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) => { // 當number改變時,觸發_binding[number] 中的綁定的Watcher類的更新 item.update() // 調 Watcher 實例的 update 方法更新 DOM }) } } }) }) } // 訂閱者模式: 綁定更新函數,實現對 DOM 元素的更新 function Watcher(el, data, key, attr) { this.el = el // 指令對應的DOM元素 this.data = data // this.$data 數據: {number: 0, count: 0} this.key = key // 指令綁定的值,本例如'number' this.attr = attr // 綁定的屬性值,本例為'innerHTML','value' this.update() } // 比如 H3.innerHTML = this.data.number; 當number改變時,會觸發這個update函數,保證對應的DOM內容進行了更新 Watcher.prototype.update = function () { this.el[this.attr] = this.data[this.key] } // 將view與model進行綁定,解析指令(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) // 將為數組轉化為真正的數組 nodes.map(node => { if (node.children.length && node.children.length > 0) { // 對所有元素進行遍歷,并進行處理 _this._compile(node) } if (node.hasAttribute(’v-click’)) { // 如果有v-click屬性,我們監聽它的onclick事件,觸發increment事件,即number++ let attrVal = node.getAttribute(’v-click’) node.onclick = _this.$methods[attrVal].bind(_this.$data) // bind是使data的作用域與method函數的作用域保持一致 } // 如果有v-model屬性,并且元素是INPUT或者TEXTAREA,我們監聽它的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, // 對應的 DOM 節點 _this.$data, attrVal, // v-model 綁定的值 ’value’ )) node.addEventListener(’input’, () => { _this.$data[attrVal] = node.value // 使number 的值與 node的value保持一致,已經實現了雙向綁定 }) } 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 = () => { // 當文檔內容完全加載完成會觸發該事件,避免獲取不到對象的情況 new myVue({ el: ’#app’, data: { number: 0, count: 0 }, methods: { increment() { this.number++ }, incre() { this.count++ } } }) }</script> </html>

2.Proxy 實現雙向數據綁定

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持好吧啦網。

標簽: Vue
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产一区二区三区四区五区传媒| 国内自拍视频一区二区三区| 老司机精品久久| 亚洲精品日本| 欧美91在线| 岛国av在线网站| 香蕉国产精品| 欧美日韩xxxx| 激情综合五月| 欧美日韩四区| 国产伦理一区| 久久影院一区| 日韩一区精品| 色一区二区三区| 在线一区二区三区视频| 国产欧美日韩一区二区三区四区| 国产精品久久观看| 模特精品在线| 欧美成人一二区| 欧美日韩国产精品一区二区亚洲| 免费黄网站欧美| 欧美a在线观看| 蜜桃国内精品久久久久软件9| 日韩亚洲精品在线观看| 91日韩免费| 亚洲精品乱码日韩| 日韩成人高清| 欧美日韩在线精品一区二区三区激情综合| 成午夜精品一区二区三区软件| 国产一区导航| 国产精东传媒成人av电影| 亚洲v在线看| 国产精品三级| 一区二区自拍| 国产一区二区三区亚洲| 美女少妇全过程你懂的久久| 国产欧美丝祙| 国产精品美女| 性一交一乱一区二区洋洋av| 国产成人精品一区二区免费看京| 男女精品网站| 日韩精品永久网址| 日韩一区二区三区精品视频第3页 日韩一区二区三区免费视频 | 丝袜国产日韩另类美女| 麻豆一区二区三区| 免费国产自久久久久三四区久久| 国产精品密蕾丝视频下载| 91精品国产福利在线观看麻豆| 欧美日韩一区二区三区四区在线观看 | 欧美特黄一级| 国产成人精品三级高清久久91| 中文字幕日韩高清在线| 九九久久婷婷| 国产精品久久久久9999高清| 成人av二区| 国产精品男女| 日韩高清电影一区| av中文字幕在线观看第一页| 日韩va亚洲va欧美va久久| 91精品蜜臀一区二区三区在线| 久久wwww| 日本特黄久久久高潮| 国产模特精品视频久久久久| 91精品一区二区三区综合在线爱| 精品一区二区三区中文字幕视频| 青青草91视频| 亚洲综合婷婷| 国产亚洲在线观看| 国产传媒在线| 久久亚洲电影| 国产aⅴ精品一区二区四区| 中文国产一区| 国产91在线播放精品| 亚洲精品日本| 神马日本精品| 国产精品mv在线观看| 亚洲免费影院| 伊人久久视频| 动漫av一区| 视频在线观看一区二区三区| 亚洲先锋成人| 欧美sss在线视频| 国产精品伦理久久久久久| 久久久久黄色| 老司机精品视频网| 国产伦精品一区二区三区千人斩 | 国产+成+人+亚洲欧洲在线| 色婷婷成人网| 亚洲最新av| 亚洲伊人影院| 首页亚洲欧美制服丝腿| 亚洲综合二区| 久久不射网站| 蜜桃久久久久久| 日韩中文字幕1| 视频一区免费在线观看| 亚洲综合欧美| 亚洲一区二区三区四区电影| 蜜臀久久99精品久久久久宅男| 香蕉久久国产| 亚洲精品动态| 日韩精品高清不卡| 欧美精品国产白浆久久久久| 亚洲欧美久久久| 婷婷综合亚洲| 激情欧美丁香| 免费一二一二在线视频| 久久久久伊人| 久久av超碰| 国产亚洲一区| 日韩av一区二区在线影视| 蜜臀91精品一区二区三区| 国产综合视频| 国产一区二区中文| 香蕉久久精品| 亚洲综合精品| 在线天堂中文资源最新版| 久久av资源| 麻豆国产欧美日韩综合精品二区| 国产亚洲欧美日韩精品一区二区三区 | 久久精品欧洲| 欧美视频一区| 欧美久久亚洲| 国产精品一页| 国产精品免费不| 嫩呦国产一区二区三区av| 国产精品www994| 久久的色偷偷| 成人在线黄色| 日本美女一区| 激情久久五月| 久久夜色精品| 亚洲69av| 国产日韩三级| 国产suv精品一区二区四区视频| 欧美激情国产在线| 久久久久久久久久久妇女| 在线观看精品| 樱桃成人精品视频在线播放| 亚洲欧美日韩国产一区二区| 91亚洲自偷观看高清| 精品日韩在线| av综合电影网站| 免费观看久久av| 视频一区二区欧美| 日本va欧美va瓶| 国产精品白丝一区二区三区| 精品免费av| 日韩av首页| 午夜av一区| 亚洲男人在线| 国产精品入口久久| 麻豆久久久久久| 久久久久久黄| 亚洲综合色婷婷在线观看| 国产一精品一av一免费爽爽| 久久精品国产网站| 欧美日韩精品一区二区视频| 午夜久久一区| 日本aⅴ精品一区二区三区| 国产精品对白久久久久粗| 麻豆mv在线观看| 亚洲欧美一区在线| 亚洲久久一区| 精品国产黄a∨片高清在线| 色88888久久久久久影院| 91精品91| 日韩在线观看中文字幕| 久久免费视频66| 五月婷婷六月综合| 欧美亚洲三区| 久久xxxx精品视频| 开心激情综合| 久久精品主播| 欧美日韩亚洲一区二区三区在线 | 日韩精品一二三区| 91免费精品| 国产亚洲一区二区三区啪| 欧美日韩国产欧| 国产精品99视频| 亚洲精品乱码| 欧美日韩国产亚洲一区| 国产66精品| 欧美精品三级在线| 免播放器亚洲| 久久蜜桃精品| 美女久久久精品| 亚洲精品在线国产| 亚洲一区国产| 午夜精品福利影院| 九九精品调教| 欧美久久久网站| 午夜电影亚洲| 精品国产不卡| 五月天av在线| 日韩一区精品字幕| 欧美午夜不卡影院在线观看完整版免费| 亚洲欧洲美洲国产香蕉| 久久激情综合网| 91久久黄色|