淺究Vue響應式原理

攝於2017年11月24日 09:02:42 By OnePlusA5000 in HangZhou

前言(概念)

Vue最明顯的特性之一即是它的響應式系統,其數據模型便是普通的 JavaScript 對象。而當你讀取或寫入它們時,視圖便會進行響應操做。文章簡要闡述下其實現原理,若有錯誤,還請不吝指正。我的博客地址:hiybm.cnhtml

響應式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 實例增長的數據,因此亦不屬於響應式。vue

關於Object.definePropery

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回這個對象。這個API是實現響應式數據的關鍵所在react

Syntax: Object.defineProperty(obj, prop, descriptor)git

  • obj: 要定義屬性的對象
  • prop: 要定義或修改的屬性的名稱
  • descriptor: 將被定義或修改的屬性描述符。

Tips: 要知道ECMAScript中有兩種屬性:數據屬性和訪問器屬性。這裏的descriptor可取值有數據屬性和訪問器屬性。 數據屬性: 包含一個數據值的位置,在此位置能夠進行讀寫操做,有如下特性:github

  • [[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這一特性來實現的響應系統。 示例代碼:bash

// 定義一個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 從新計算,從而導致它關聯的組件得以更新。ide

以下圖所示: 函數

Tips:中每一個指令/數據綁定都有一個對應的 watcher 對象。其中 watcher扮演的角色至關因而一個紐帶,這個紐帶的做用就是依賴收集。

END

文中還有部分深層細節沒有講述到,後續我也會接着更新系列文章來進一步深深深究vue底層的響應式原理,SYNT。性能

參考連接

Object.defineproperty

深刻響應式原理

深刻淺出之vue響應式原理

Javascript高級程序設計(第3版)6.1章節

相關文章
相關標籤/搜索