Vue原始碼分析之資料驅動
阿新 • • 發佈:2020-08-21
## 響應式特點
- 資料響應式
修改資料時,檢視自動更新,避免繁瑣Dom操作,提高開發效率
- 雙向繫結
資料改變,檢視隨之改變。檢視改變,資料隨之改變
- 資料驅動
開發時僅需要關注資料本身,不需要關心資料如何渲染到檢視
官方教程: https://cn.vuejs.org/v2/guide/reactivity.html
MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
## vue 2.x 基於 defineProperty 實現資料捕捉
> 當你把一個普通的 JavaScript 物件傳入 Vue 例項作為 data 選項,Vue 將遍歷此物件所有的 property,並使用 Object.defineProperty 把這些 property 全部轉為 getter/setter。
> Object.defineProperty 是 ES5 中一個無法 shim 的特性,這也就是 Vue 不支援 IE8 以及更低版本瀏覽器的原因。
下面是一段模仿 vue 實現資料捕捉的程式碼
``` js
interface Vue{
data: {
[prop: string]: any
}
[prop: string]: any
}
let vm: Vue = {
data: {
name: 'Tom',
age: 22,
},
}
//資料劫持
function proxyData(vm: Vue){
Object.keys(vm.data).forEach(key => {
console.log(key, vm.data[key])
vm[key] = vm.data[key];
Object.defineProperty(vm, key, {
enumerable: true, //可列舉
configurable: true, //可配置:刪除或重定義
get(){
console.log('getter:', vm.data[key]);
return vm.data[key];
},
set(newVal){
console.log('setter', newVal);
if (newVal === vm.data[key]){
return;
}
vm.data[key] = newVal;
document.querySelector('#app')!.textContent = vm.data[key];
}
})
})
}
proxyData(vm);
vm.name = 'karolina'; //模擬資料發生改變,檢視改變
console.log(vm);
// {
// name: "karolina"
// age: 33
// data:{
// name: "karolina"
// age: 33
// }
// }
```
## Vue 3.x 基於 Proxy 代理捕捉資料
MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
ES6 提供 Proxy 捕捉器, 相比於 `Object.defineProperty` 代理整個物件而非屬性,程式碼上更簡潔,效能上由瀏覽器優化更快
同樣下面是一段模仿 vue 實現資料捕捉的程式碼
```
let data ={
name: 'Tom',
age: 22,
};
let vm = new Proxy(data, {
get(target: any, key){
if (key in target){
console.log('getter: ',key, target[key]);
return target[key];
}
},
set(target: any, key, newVal,){
console.log('setter: ',key, target[key]);
if (target[key] === newVal){
return false;
}
target[key] = newVal;
document.querySelector('#app')!.textContent = target[key];
return true;
},
})
vm.name = 'Karolina';
console.log(vm);
console.log(vm.age);
```
## 釋出訂閱模式
> 在“釋出者-訂閱者”模式中,稱為釋出者的訊息傳送者不會將訊息程式設計為直接傳送給稱為訂閱者的特定接收者。
> 這意味著釋出者和訂閱者不知道彼此的存在。存在第三個元件,稱為代理或訊息代理或事件匯流排,它由釋出者和訂閱者都知道,它過濾所有傳入的訊息並相應地分發它們。
> 換句話說,pub-sub是用於在不同系統元件之間傳遞訊息的模式,而這些元件不知道關於彼此身份的任何資訊。經紀人如何過濾所有訊息?實際上,有幾個訊息過濾過程。最常用的方法有:基於主題和基於內容的。
- 訂閱者(subscriber)需要在 `事件中心` 註冊事件
- 釋出者(publisher)需要於 `事件中心` 觸發事件
- 訂閱者和釋出者無需知道對方身份
![](https://img2020.cnblogs.com/blog/1006989/202008/1006989-20200821114007538-1381650657.png)
#### Vue中的釋出訂閱模式
https://cn.vuejs.org/v2/guide/migration.html#dispatch-%E5%92%8C-broadcast-%E6%9B%BF%E6%8D%A2
下面是Vue的釋出訂閱虛擬碼,用於兄弟元件之間通訊
``` js
//事件中心
let eventHub = new Vue();
//ComponetA.vue 訂閱者
willDo: function(){
eventHub.$on('will-do', (text)=>{console.log(text)});
}
//ComponetB.vue 釋出者
willDo: function(){
eventHub.$emit('will-do', {text: 'Hello'});
}
```
下面手寫程式碼來模擬Vue的釋出訂閱模式實現
``` js
//儲存主題和控制代碼,主題為事件名,控制代碼為hanlder
interface ITopicMap {
[prop: string]: Array