Vue.js最著名的功能就是響應式系統,想要詳細介紹Vue的響應式系統需要大量的篇幅。因此,本節的目的只是介紹響應式系統的大體工作流程,使你在以后深入學習的時候不至于一頭霧水。
## 核心概念
### 1、Object.defineProperty
為對象的屬性設置getter和setter。
```js
var obj={};
var bind=[];
//觸發obj對象set和get方法的時候,趁機來輸出或修改bind數組的內容
Object.defineProperty(obj,'s',{
set:function(val){
console.log('正在設置obj對象的s屬性')
bind['s']=val;
},
get:function(){
console.log('正在讀取obj對象的s屬性')
return bind['s'];
}
})
obj['s'] = 1; // 觸發了obj的set方法,等于1賦值給bind['s']。 打印 正在設置obj對象的s屬性
var ts = obj['s']; // 觸發了obj的get方法。 打印 正在讀取obj對象的s屬性
```
IE9以下的瀏覽器不能兼容Object.defineProperty。所以vue只能兼容ie9以上的瀏覽器了。
## 相關概念
先來看看 Vue 的配置,以組件為例
```
{
render(h) { ...... },
props: {
props1: Number
},
data() {
return {
data1: 1,
data2: 0
}
},
computed: {
computed1() {
return this.data1 + this.props1
}
},
watch: {
computed1(newValue) {
setTimeout(()=>{
console.log(newValue)
}, 0)
}
}
}
```
響應式實現過程大概可以分為三塊:
#### 數據 Data
初始化` data、props、computed `時(初始化 render 時還有一些其他內置屬性),通過 `Object.defineProperty `設置屬性的 `getter` 和 `setter`,并為每個屬性綁定一個 Dep 實例。
#### 組件 Component
可以把整個 Vue 應用認為是大大小小的組件堆砌而成。Vue 為每個組件都定義了兩塊重要的內容:`Watcher`和` Render`。
執行` $mount `時調用 `mountComponent`、初始化 `watch、computed`時、調用實例方法` $watch`時,都會給實例化一個 Watcher。而 Watcher 初始化時會進行一次求值(執行實例中的` getter `方法),首先會將當前實例設置為 Dep 當前目標,然后觸發數據的相關屬性的 ` getter ` 方法。
#### 依賴 Dep
Dep 類就是用來關聯數據與組件的 Watcher 的。利用 **訂閱發布設計模式** 實現數據響應式變化。
在初始化 Watcher 時,通過執行了對應屬性的 `getter` 方法,將Watcher 實例放入數據對應的 Dep 實例的訂閱列表中。而當數據發生變化時,對應屬性執行 `set` 方法,會通知到對應的 Dep 實例,再通知訂閱列表中的 Watcher。
此時,Watcher 將通知組件執行 Render,重新計算出新的 VNode,通過新舊 VNode 的對比,進行視圖更新。
#### Observer 類
Vue的響應式數據都會有一個__ob__的屬性作為標記,里面存放了該屬性的觀察器,也就是Observer的實例,防止重復綁定。
Observer為數據加上響應式屬性進行雙向綁定。如果是對象則進行深度遍歷,為每一個子對象都綁定上方法,如果是數組則為每一個成員都綁定上方法。
>相當于對響應式數據進行一層包,方便監控數據的變化,以便通知 Watcher 實例。
## 響應式原理

在Vue組件創建過程中, 為 `data、props、computed ` 三類數據添加__ob__的屬性作為標記。即用 Observer 包裝響應式數據,監控數據的`getter` 和 `setter` 操作。每一個數據都有與之對應的Dep實例。
利用 **訂閱發布** 設計模式, 將 Dep 于 Watcher 關聯。
解析 `template` 中的指令,為每一條數據綁定指令創建 一個 Watcher,使之關聯 對應的 數據。
在對數據進行修改時,數據綁定的Dep.subs中的 Watcher 實例 推入 Watcher隊列中,等同一事件循環中的所有數據變化完成之后,再統一進行視圖更新。