浅聊vue双向绑定原理Object.defineProperty-/-Proxy
什么是雙向綁定?
當(dāng)數(shù)據(jù)模型data變化時(shí),頁面視圖會得到響應(yīng)更新
vue又是怎么做的?
vue其實(shí)現(xiàn)原理是對data的getter/setter方法進(jìn)行攔截(Object.defineProperty或者Proxy),利用發(fā)布訂閱的設(shè)計(jì)模式,在getter方法中進(jìn)行訂閱,在setter方法中發(fā)布通知,讓所有訂閱者完成響應(yīng)。
說這些的時(shí)候我們在剛使用vue2.x的就會遇到過數(shù)據(jù)更新了啊,為何頁面不更新呢。這其實(shí)就是Object.defineProperty在作祟。
而在vue3還沒有發(fā)布時(shí),很火的一個(gè)話題就是Vue3將使用Proxy 取代Vue2 版本的Object.defineProperty。那么Proxy對比Object.defineProperty有什么優(yōu)勢。
Proxy對比Object.defineProperty
Proxy
- Proxy可以直接監(jiān)聽對象而非屬性
- Proxy可以直接監(jiān)聽數(shù)組的變化
- Proxy有多達(dá)13種攔截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具備的
- Proxy返回的是一個(gè)新對象,我們可以只操作新的對象達(dá)到目的,不需要像Object.defineProperty一樣遍歷每個(gè)屬性有一定的性能提升
- Proxy作為新標(biāo)準(zhǔn)將受到瀏覽器廠商重點(diǎn)持續(xù)的性能優(yōu)化,也就是傳說中的新標(biāo)準(zhǔn)的性能紅利
- Proxy直接實(shí)現(xiàn)對象屬性的新增/刪除
Object.defineProperty
-
Object.defineProperty只能劫持對象的屬性,需要遍歷對象的每一個(gè)屬性,如果屬性值也是對象,就需要遞歸進(jìn)行深度遍歷。
-
Object.defineProperty劫持的是對象的屬性,所以新增屬性時(shí),需要重新遍歷對象, 對其新增屬性再次使用Object.defineProperty進(jìn)行劫持。也就是Vue2.x中給數(shù)組和對象新增屬性時(shí),需要使用$set才能保證新增的屬性也是響應(yīng)式的, $set內(nèi)部也是通過調(diào)用Object.defineProperty去處理的。
冷知識
Object.defineProperty無法監(jiān)聽數(shù)組數(shù)據(jù)的變化,但是為什么數(shù)組在使用push pop等方法的時(shí)候可以觸發(fā)頁面更新呢,那是因?yàn)関ue內(nèi)部攔截了這些方法。比如數(shù)組在使用push pop等方法的時(shí)候?yàn)槭裁纯梢杂|發(fā)頁面更新呢,那是因?yàn)関ue內(nèi)部攔截了這些方法。
// 重寫push等方法,然后再把原型指回原方法var ARRAY_METHOD = [ 'push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice' ];var array_methods = Object.create(Array.prototype);ARRAY_METHOD.forEach(method => {array_methods[method] = function () {// 攔截方法return Array.prototype[method].apply(this, arguments);}});回歸正題我們分別用代碼簡單的實(shí)現(xiàn)下Proxy對比Object.defineProperty如何實(shí)現(xiàn)綁定的
Object.defineProperty實(shí)現(xiàn)
// 這是將要被劫持的對象 const data = {name: '', }; // 遍歷對象,對其屬性值進(jìn)行劫持 Object.keys(data).forEach(function(key) {console.log('1')Object.defineProperty(data, key, {enumerable: true,configurable: true,get: function(newVal) {return val},set: function(newVal) {// 當(dāng)屬性值發(fā)生變化時(shí)我們可以進(jìn)行額外操作console.log(`大家好,我是${newVal}我被劫持了`);val = newVal;},}); }); data.name = '111';Proxy實(shí)現(xiàn)
const target = {name: '控制' };const handler = {get: function(target, key) {console.log(`${key} 被讀取`);return target[key];},set: function(target, key, value) {console.log(`${key} 被設(shè)置為 ${value}`);target[key] = value;} };const testObj = new Proxy(target, handler);console.log(testObj.name); // name 被讀取 及輸出名字 控制 testObj.name = 1; // name 被設(shè)置為 1 輸出 1Proxy參數(shù)介紹
1.get(target, propKey, receiver)
該方法的含義是:用于攔截某個(gè)屬性的讀取操作。它有三個(gè)參數(shù),如下解析:
- target: 目標(biāo)對象。
- propKey: 目標(biāo)對象的屬性。
- receiver: (可選),該參數(shù)為上下文this對象
2.set(target, propKey, value, receiver)
該方法是用來攔截某個(gè)屬性的賦值操作,它可以接受四個(gè)參數(shù),參數(shù)解析分別如下:
- target: 目標(biāo)對象。
- propKey: 目標(biāo)對象的屬性名
- value: 屬性值
- receiver(可選): 一般情況下是Proxy實(shí)列
3.has(target, propKey)
該方法是判斷某個(gè)目標(biāo)對象是否有該屬性名。接收二個(gè)參數(shù),分別為目標(biāo)對象和屬性名。返回的是一個(gè)布爾型。
- target: 目標(biāo)對象。
- propKey: 目標(biāo)對象的屬性名
4.construct(target, args, newTarget)
該方法是用來攔截new命令的,它接收三個(gè)參數(shù),分別為 目標(biāo)對象,構(gòu)造函數(shù)的參數(shù)對象及創(chuàng)造實(shí)列的對象。
第三個(gè)參數(shù)是可選的。它的作用是攔截對象屬性。
- target: 目標(biāo)對象。
- args: 構(gòu)造函數(shù)的參數(shù)對象
- newTarget:創(chuàng)造實(shí)列的對象
5.apply(target, object, args)
該方法是攔截函數(shù)的調(diào)用的。該方法接收三個(gè)參數(shù),分別是目標(biāo)對象。目標(biāo)對象上下文this對象 和 目標(biāo)對象的數(shù)組;它和 Reflect.apply參數(shù)是一樣的
第三個(gè)參數(shù)是可選的。它的作用是攔截對象屬性。
- target: 目標(biāo)對象。
- object: 目標(biāo)對象上下文this對象
- args:目標(biāo)對象的數(shù)組
寫在最后的話大家不要忘記,點(diǎn)贊,評論,收藏
總結(jié)
以上是生活随笔為你收集整理的浅聊vue双向绑定原理Object.defineProperty-/-Proxy的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 翻拍何时才能结束啊
- 下一篇: html5倒计时秒杀怎么做,vue 设