前言
Vue.js 3.0開始使用Proxy作為數(shù)據(jù)監(jiān)聽手段,今天來學(xué)習(xí)一下衰粹。
今天我們要做的事是:
- 學(xué)習(xí)語法
- 與Object.defineProperty的對比
- 寫一個類似于vue的v-model的案例
MDN文檔
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
阮一峰ES6手冊
http://es6.ruanyifeng.com/#docs/proxy
基本語法
非常簡單锣光,就2個參數(shù),多一個都沒有寄猩。
let p = new Proxy(target, handler);
范例:
const originalObj = {
name: 'xialei'
};
const publicObj = new Proxy(originalObj, {
set(target, prop, value) {
// 將屬性值轉(zhuǎn)化為大寫
target[prop] = value.toString().toUpperCase();
}
});
publicObj.name = 'xialei';
console.log(publicObj.name); // XIALEI
監(jiān)聽對象屬性的值變化
所謂監(jiān)聽嫉晶,并不是真的監(jiān)聽,而是重寫對象的set方法田篇,在賦值的同時觸發(fā)一個函數(shù),就好像是在監(jiān)聽一樣箍铭。
const originalObj = {
name: 'xialei'
};
const publicObj = new Proxy(originalObj, {
set(target, prop, value) {
target[prop] = value;
watchOriginalObj(value);
}
});
function watchOriginalObj(value) {
console.log('新值', value)
}
publicObj.name = 'xialei';
其結(jié)果是泊柬,盡管只有一個賦值操作,但是觸發(fā)了一個函數(shù)诈火。
13個方法
從阮一峰的手冊可以看到兽赁,ES6 Proxy有13個方法,其中大多數(shù)是少用的方法冷守,主要還是get刀崖、set。
Proxy劫持?jǐn)?shù)組的范例
let arr = [2,4];
let proxy = new Proxy(arr, {
set(target, prop, value) {
target[prop] = value * 2.5
return true
}
})
proxy[4] = 11
console.log(proxy[4]) // 27.5
proxy.push(22)
console.log(proxy) // 末位已經(jīng)追加上了55
與Object.defineProperty的對比
Object.defineProperty(obj, prop, descriptor)
參數(shù)
obj
要在其上定義屬性的對象拍摇。
prop
要定義或修改的屬性的名稱亮钦。
descriptor
將被定義或修改的屬性描述符。
可見:
- Object.defineProperty是針對一個對象的單一屬性來設(shè)置setter和getter充活,Proxy是劫持對象自身蜂莉,給對象自身來設(shè)置setter和getter。
- Object.defineProperty返回的是修飾過后的對象混卵,Proxy是返回劫持之后的對象映穗,這一點比較類似。
- Proxy能劫持?jǐn)?shù)組幕随,Object.defineProperty沒有這個能力蚁滋。
- Proxy的劫持方法非常多,而Object.defineProperty就很單一,因為名字就決定了只是給屬性增加修飾辕录。
用Proxy寫一個雙向綁定案例
我在2018年9月澄阳,參考了一些文檔之后寫了一個基于Object.defineProperty的雙向綁定案例(http://www.reibang.com/p/34fd579dfc0b),Vue 2.0正是基于它的√ぐ荩現(xiàn)在碎赢,Vue 3.0將在2020年第一季度上線,今天寫一個基于Proxy的案例速梗。大量代碼繼續(xù)沿用去年文章肮塞,就這個案例來講,代碼唯一要變動的就是_observer原型方法姻锁。
原本的寫法是:
Vue.prototype._observer = function (data) {
var self = this;
Object.keys(data).forEach(function (key) {
var oldValue = data[key];
self._binding[key] = {
_updaterList: []
}
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
return oldValue;
},
set: function (newValue) {
if (oldValue === newValue) return;
oldValue = newValue;
self._binding[key]._updaterList.forEach(function (updater) {
updater.update();
})
}
});
})
}
現(xiàn)在的寫法是:
Vue.prototype._observer = function (data) {
Object.keys(data).forEach(key => {
this._binding[key] = {
_updaterList: []
}
});
this.$data = new Proxy(data, {
get: (target, prop, value) => {
return target[prop]
},
set: (target, prop, value) => {
if (target[prop] === value) return;
target[prop] = value
this._binding[prop]._updaterList.forEach(function (updater) {
updater.update();
})
}
});
}