前言(概念)
Vue最明顯的特性之一便是它的響應式系統,其資料模型即是普通的 JavaScript
物件。而當你讀取或寫入它們時,檢視便會進行響應操作。文章簡要闡述下其實現原理,如有錯誤,還請不吝指正。個人部落格地址:hiybm.cn
響應式data
<div id = "exp">{{ message }}</div>
const vm = new Vue({
el: '#exp',
data: {
message: 'This is A'
}
})
vm.message = 'This is B' // 響應式
vm._message = 'This is C' // 非響應式
複製程式碼
上述程式碼中,data
是Vue例項的資料物件,當例項初始化時,Vue
會遍歷 data
中的所有屬性,並且使用 Object.definePropery 把這些屬性全都轉為 getter/setter
,從而讓 data
的屬效能夠響應資料變化。另外,Object.defineProperty
是 ES5 中一個無法 shim(墊片)
的特性,這也就是為什麼 Vue 不支援 IE8 以及更低版本瀏覽器的原因。物件必須是純粹的物件 (含有零個或多個的 key/value 鍵值對):瀏覽器 API 建立的原生物件。所以,在data
中宣告過的message
是響應式資料,而由於_message
是在data
外使用 Vue
例項增加的資料,所以亦不屬於響應式。
關於Object.definePropery
Object.defineProperty()
方法會直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性,並返回這個物件。這個API是實現響應式資料的關鍵所在。
Syntax: Object.defineProperty(obj, prop, descriptor)
- obj: 要定義屬性的物件
- prop: 要定義或修改的屬性的名稱
- descriptor: 將被定義或修改的屬性描述符。
Tips: 要知道ECMAScript中有兩種屬性:資料屬性和訪問器屬性。這裡的descriptor
可取值有資料屬性和訪問器屬性。
資料屬性: 包含一個資料值的位置,在此位置可以進行讀寫操作,有以下特性:
[[Configurable]]
:對屬性的操作可配置性開關,如刪除,修改。預設值為true
。[[Enumberble]]
:是否可列舉(通過for-in
)。預設值為true
。[[Writable]]
:能否修改屬性的值。預設值為true
。[[value]]
:包含這個屬性的資料值
,讀取時從該位置讀,寫入時把新值存到該位置。預設值為undefined
。
訪問器屬性: 不包含資料值,包含一個函式對(getter/setter
)。特性如下:
[[Configurable]]
:對屬性的操作可配置性開關,如刪除,修改。預設值為true
。[[Enumberble]]
:是否可列舉(通過for-in
)。預設值為true
。[[Get]]
:讀取屬性時呼叫的函式。預設值為undefined
。[[Set]]
:寫入屬性時呼叫的函式。預設值為undefined
。
Tips: 在讀取訪問器屬性時,就會呼叫getter
函式,該函式負責返回有效的值;在寫入訪問器屬性時,會呼叫setter
函式並傳入新值,該函式負責決定如何處理資料,但是這兩個函式不一定非要同時存在。Vue
便是利用getter/setter
這一特性來實現的響應系統。
示例程式碼:
// 定義一個book物件,_year和edition都屬於資料屬性。
var book = {
_year : 2004,
edition : 1
};
// 對book物件建立 year 訪問器屬性。
Object.defineProperty(book, "year",{
// 讀取 year 訪問器屬性時,get() 方法返回 _year 的值。
get : function () {
console.info(this._year, 'get'); // 2004
return this._year;
},
// 寫入 year 訪問器屬性時,set() 方法對新值進行操作。
set : function (newValue) {
if (newValue > 2004) {
this._year = newValue;
console.info(this._year, 'set') // 2005
this.edition += newValue - 2004;
}
}
});
// 讀取 year 訪問器屬性時會返回_year的值。
book.year;
// 寫入 year 訪問器屬性時會呼叫set() 函式,進行操作。
book.year = 2005;
console.info(book.edition) // 2
console.info(book) // 此處藏有彩蛋。
複製程式碼
watcher
官方表述:每個元件例項都有相應的 watcher 例項物件,它會在元件渲染的過程中把屬性記錄為依賴,之後當依賴項的 setter 被呼叫時,會通知 watcher 重新計算,從而致使它關聯的元件得以更新。
如下圖所示:
Tips:中每個指令/資料繫結都有一個對應的watcher
物件。其中 watcher
扮演的角色相當於是一個紐帶,這個紐帶的作用就是依賴收集。
END
文中還有部分深層細節沒有講述到,後續我也會接著更新系列文章來進一步深深深究vue底層的響應式原理,SYNT。
參考連結