vue 3 使用了 proxy api,有些手癢,就弄一個簡單的結構玩玩吧。Proxy API 見 MDN Proxy
效果圖
思路
依賴收集:Mvvm 初始化時劫持數據,並設置觀察者 dep。模仿 vue 結構是在 get 時往觀察者 dep 推入被觀察者 watcher,然後 set 時讓觀察者通知所有被觀察者開始更新。
數據響應:這裏只是簡單在 compiler 裏面去掃描了一遍所有帶着 v-text 和 v-model 鉤子的標籤做了處理:定義被觀察者 watcher,被觀察者的觸發函數寫上節點 DOM 的更新。
當然我們知道 vue 的數據響應過程比這個複雜多了,有着虛擬 DOM 和複雜的 diff 算法。
代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div v-text="text1"></div>
<input type="text" v-model="text1">
<div v-text="text2"></div>
<input type="text" v-model="text2">
</div>
<script>
class Watcher {
constructor(cb) {
this.cb = cb;
}
run() {
this.cb();
}
}
class Dep {
constructor() {
this.subs = [];
this.target = null;
}
notify() {
this.subs.forEach(item => {
item.run();
})
}
}
class Mvvm {
constructor(data) {
let that = this;
this.dep = new Dep();
this.data = new Proxy(data, {
get(obj, key, prox) {
that.dep.target && that.dep.subs.push(that.dep.target);
return obj.data[key];
},
set(obj, key, value, prox) {
obj.data[key] = value;
that.dep.notify();
return true;
}
});
this.compiler();
}
compiler() {
let that = this;
let app = document.getElementById('app');
let bindTextNodes = app.querySelectorAll('[v-text]');
let bindInputNodes = app.querySelectorAll('[v-model]');
bindTextNodes.forEach(bindTextNode => {
let textModel = bindTextNode.getAttribute('v-text');
let watcher = new Watcher(function() {
bindTextNode.innerText = that.data[textModel];
});
that.dep.target = watcher;
bindTextNode.innerText = that.data[textModel];
this.dep.target = null;
})
bindInputNodes.forEach(bindInputNode => {
let inputModel = bindInputNode.getAttribute('v-model');
let watcher = new Watcher(function() {
bindInputNode.value = that.data[inputModel];
});
this.dep.target = watcher;
bindInputNode.value = that.data[inputModel];
bindInputNode.addEventListener('input', function(evt) {
that.data[inputModel] = evt.target.value;
})
that.dep.target = null;
})
}
}
new Mvvm({
data: {
text1: '123',
text2: '789'
}
})
</script>
</body>
</html>
爲什麼時 Proxy 而不是 Object.defineProperty ?
我們知道 Vue2.0 的反應系統是使用 Object.defineProperty 的 getter 和 setter。 但是,Vue 3 將使用 ES2015 Proxy 作爲其觀察者機制。 這消除了以前存在的警告,使速度加倍,並節省了一半的內存開銷。
而爲什麼使用 Proxy 替代 Object.defineProperty?Proxy 可以劫持整個對象,並返回一個新的對象。Proxy 不僅可以代理對象,還可以代理數組,還可以代理動態增加的屬性。節省內存,速度加倍。
爲了繼續支持 IE11,Vue 3 將發佈一個支持舊觀察者機制和新 Proxy 版本的構建