电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

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

瀏覽:171日期: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
相關文章:
主站蜘蛛池模板: 电子天平-华志电子天平厂家| 工业设计,人工智能,体验式3D展示的智能技术交流服务平台-纳金网 J.S.Bach 圣巴赫_高端背景音乐系统_官网 | 铝合金电阻-无源谐波滤波器-上海稳达电讯设备厂 | 广东风淋室_广东风淋室厂家_广东风淋室价格_广州开源_传递窗_FFU-广州开源净化科技有限公司 | 湖南专升本-湖南省专升本报名-湖南统招专升本考试网 | 横河变送器-横河压力变送器-EJA变送器-EJA压力变送器-「泉蕴仪表」 | 耙式干燥机_真空耙式干燥机厂家-无锡鹏茂化工装备有限公司 | 企业彩铃制作_移动、联通、电信集团彩铃上传开通_彩铃定制_商务彩铃管理平台-集团彩铃网 | 青岛侦探_青岛侦探事务所_青岛劝退小三_青岛调查出轨取证公司_青岛婚外情取证-青岛探真调查事务所 | 光栅尺厂家_数显表维修-苏州泽升精密机械 | 广东教师资格网-广东教师资格证考试网 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库 | 首页-瓜尔胶系列-化工单体系列-油田压裂助剂-瓜尔胶厂家-山东广浦生物科技有限公司 | 点焊机-缝焊机-闪光对焊机-电阻焊设备生产厂家-上海骏腾发智能设备有限公司 | 砖机托板价格|免烧砖托板|空心砖托板厂家_山东宏升砖机托板厂 | 酒万铺-酒水招商-酒水代理| 手机存放柜,超市储物柜,电子储物柜,自动寄存柜,行李寄存柜,自动存包柜,条码存包柜-上海天琪实业有限公司 | 逗网红-抖音网红-快手网红-各大平台网红物品导航 | 龙门加工中心-数控龙门加工中心厂家价格-山东海特数控机床有限公司_龙门加工中心-数控龙门加工中心厂家价格-山东海特数控机床有限公司 | 电脑知识|软件|系统|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网 | 江苏大隆凯科技有限公司| BAUER减速机|ROSSI-MERSEN熔断器-APTECH调压阀-上海爱泽工业设备有限公司 | 防渗膜厂家|养殖防渗膜|水产养殖防渗膜-泰安佳路通工程材料有限公司 | 济南品牌包装设计公司_济南VI标志设计公司_山东锐尚文化传播 | 蔬菜配送公司|蔬菜配送中心|食材配送|饭堂配送|食堂配送-首宏公司 | IP检测-检测您的IP质量 | 液压升降平台_剪叉式液压/导轨式升降机_传菜机定做「宁波日腾升降机厂家」 | 螺钉式热电偶_便携式温度传感器_压簧式热电偶|无锡联泰仪表有限公司|首页 | 房屋质量检测-厂房抗震鉴定-玻璃幕墙检测-房屋安全鉴定机构 | 锂电池生产厂家-电动自行车航模无人机锂电池定制-世豹新能源 | 杭州ROHS检测仪-XRF测试仪价格-百科 | 防爆电机_防爆电机型号_河南省南洋防爆电机有限公司 | 企业管理培训,企业培训公开课,企业内训课程,企业培训师 - 名课堂企业管理培训网 | 创绿家招商加盟网-除甲醛加盟-甲醛治理加盟-室内除甲醛加盟-创绿家招商官网 | 上海网站建设-上海网站制作-上海网站设计-上海做网站公司-咏熠软件 | 碳钢法兰厂家,非标法兰,定制异型,法兰生产厂家-河北九瑞管道 | 低气压试验箱_高低温低气压试验箱_低气压实验箱 |林频试验设备品牌 | 爱佩恒温恒湿测试箱|高低温实验箱|高低温冲击试验箱|冷热冲击试验箱-您身边的模拟环境试验设备技术专家-合作热线:400-6727-800-广东爱佩试验设备有限公司 | 100国际学校招生 - 专业国际学校择校升学规划| 污水提升器,污水提升泵,地下室排水,增压泵,雨水泵,智能供排水控制器-上海智流泵业有限公司 | 撕碎机,撕破机,双轴破碎机-大件垃圾破碎机厂家 |