監聽數據對象變化,最容易想到的是建立一個需要監視對象的表,定時掃描其值,有變化,則執行相應操作,不過這種實現方式,性能是個問題,如果需要監視的數據量大的話,每掃描一次全部的對象,需要的時間很長。當然,有些框架是采用的這種方式,不過他們用非常巧妙的算法提升性能,這不在我們的討論范圍之類。
Vue 中數據對象的監視,是通過設置 ES5 的新特性(ES7 都快出來了,ES5 的東西倒也真稱不得新)Object.defineProperty() 中的 set、get 來實現的。
目標
與官方文檔第一個例子相似,不過也有簡化,因為這篇只是介紹下數據對象的監聽,不涉及文本解析,所以文本解析相關的直接舍棄了:
<div id="app"></div>var app = new Vue({ el: 'app', data: { message: 'Hello Vue!' }});瀏覽器顯示:
Hello Vue!
在控制臺輸入諸如:
app.message = 'Changed!'
之類的命令,瀏覽器顯示內容會跟著修改。
Object.defineProperty
引用 MDN 上的定義:
Object.defineProperty()方法會直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性, 并返回這個對象。
與此相生相伴的還有一個 Object.getOwnPropertyDescriptor():
Object.getOwnPropertyDescriptor() 返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)
下面的例子用一種比較簡單、直觀的方式來設置 setter、getter:
var dep = [];function defineReactive(obj, key, val) { // 有自定義的 property,則用自定義的 property var property = Object.getOwnPropertyDescriptor(obj, key); if(property && property.configurable === false) { return; } var getter = property && property.get; var setter = property && property.set; Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function() { var value = getter ? getter.call(obj) : val; dep.push(value); return value; }, set: function(newVal) { var value = getter ? getter.call(obj) : val; // set 值與原值相同,則不更新 if(newVal === value) { return; } if(setter) { setter.call(obj, newVal); } else { val = newVal; } console.log(dep); } });}var a = {};defineReactive(a, 'a', 12);// 調用 getter,12 被壓入 dep,此時 dep 值為 [12]a.a;// 調用 setter,輸出 dep ([12])a.a = 24;// 調用 getter,24 被壓入 dep,此時 dep 值為 [12, 24]a.a;Observer
簡單說過 Object.defineProperty 之后,就要開始扯 Observer 了。observer,中文解釋為“觀察者”,觀察什么東西呢?觀察對象屬性值的變化。故此,所謂 observer,就是給對象的所有屬性加上 getter、setter,如果對象的屬性還有屬性,比如說 {a: {a: {a: 'a'}}},則通過遞歸給其屬性的屬性也加上 getter、setter:
新聞熱點
疑難解答
圖片精選