替換數(shù)據(jù)劫持對象
??上一篇實現(xiàn)了 mvvm 實現(xiàn)思路获洲,可是不夠優(yōu)雅還有很多問題,我先解決這個問題數(shù)據(jù)劫持的問題豪嚎。
之前的數(shù)據(jù)劫持
之前數(shù)據(jù)的劫持試是這么做的
// 重寫data 的 get set 更改數(shù)據(jù)的時候,觸發(fā)watch 更新視圖
myVue.prototype._observer = function (obj) {
var _this = this;
for (key in obj){ // 遍歷數(shù)據(jù)
//訂閱池
// _this._watcherTpl.a = [];
// _this._watcherTpl.b = [];
_this._watcherTpl[key] = {
_directives: []
};
let value = obj[key]; // 獲取屬`性值
let watcherTpl = _this._watcherTpl[key]; // 數(shù)據(jù)的訂閱池
Object.defineProperty(_this._data, key, { // 數(shù)據(jù)劫持
configurable: true, // 可以刪除
enumerable: true, // 可以遍歷
get() {
console.log(`${key}獲取值:${value}`);
return value; // 獲取值的時候 直接返回
},
set(newVal) { // 改變值的時候 觸發(fā)set
console.log(`${key}更新:${newVal}`);
if (value !== newVal) {
value = newVal;
//_this._watcherTpl.xxx.forEach(item)
//[{update:function(){}}]
watcherTpl._directives.forEach((item) => { // 遍歷訂閱池
item.update();
// 遍歷所有訂閱的地方(v-model+v-bind+{{}}) 觸發(fā)this._compile()中發(fā)布的訂閱Watcher 更新視圖
});
}
}
})
};
};
這么做是可以實現(xiàn)可是,可以看到有這么一些缺點(diǎn):
- 對象必須是存在的蚊锹。
- 循環(huán)耗費(fèi)性能。
- 代碼可讀性可拓展性不是很好
- 等等..
那么我們能不能換一種方式去解決數(shù)據(jù)的劫持問題稚瘾?
Proxy 橫空出世
Proxy 是 ECMAScript 2015 的新特性牡昆,唯一的 缺點(diǎn)是 兼容性不是非常好。但我們要團(tuán)結(jié)啊摊欠,哈哈哈丢烘。 廢棄 IE。些椒。播瞳。
下面我們將使用 Proxy 實現(xiàn)數(shù)據(jù)的劫持 和 代理。關(guān)于 Proxy 可以看這么兩篇文章免糕,一個是 阮一峰老師 的,不管阮一峰怎么樣赢乓,當(dāng)初竟然幫助過我們,我覺得就可以稱之為老師 ,還有一篇 抱歉石窑,學(xué)會 Proxy 真的可以為所欲為
// 重寫data 的 get set 更改數(shù)據(jù)的時候牌芋,觸發(fā)watch 更新視圖
myVue.prototype._observer = function (obj) {
const _this = this;
this._data = new Proxy(obj, { // 數(shù)據(jù)劫持
get(target, key, receiver) {
return Reflect.get(target, key, receiver); // 獲取值的時候 直接返回
},
set(target, key, newVal) { // 改變值的時候 觸發(fā)set
if (_this.value !== newVal) {
_this.value = newVal;
//先將數(shù)據(jù)更新完成后
let res = Reflect.set(target,key,newVal);
_this._watcherTpl[key]._directives.forEach((item) => { // 遍歷訂閱池
item.update();
});
return res
}
}
});
};
看到代碼不用說了,量級的差距尼斧,簡潔多了姜贡,這里直接將 VUE 的data 變成了一個 Proxy 對象。進(jìn)行數(shù)據(jù)的操作棺棵。
既然這里更改了楼咳,那么我們之前的訂閱池其實是廢除了熄捍,因為沒有循環(huán)了不存在 key:
_this._watcherTpl[key] = {
_directives: []
};
所以我這里單獨(dú)在_compile 處理了訂閱池。
const attrVal = node.getAttribute('v-model'); // 獲取綁定的data
_this.hasDirectives(attrVal);
//工具類判斷是否有訂閱池
myVue.prototype.hasDirectives = function (attr) {
const _this = this;
// 沒有事件池 創(chuàng)建事件池
if (!_this._watcherTpl[attr]) {
_this._watcherTpl[attr] = {};
_this._watcherTpl[attr]._directives = [];
} else {
if (!_this._watcherTpl[attr]._directives) {
_this._watcherTpl[attr]._directives = []
}
}
};
這樣就解決了連接池的問題 母怜,這里的連接池使用的是數(shù)組余耽,后面我們將會替換為map