vueconf(2018hangzhou)大會剛剛過去,vue做者尤大大向咱們展現了vue3.0的進展,並介紹vue3.0的一些改動,其中最令我期待的就是重寫數據監聽機制。javascript
談起vue的雙向數據綁定,咱們首先能想到的就是ES5中Object.defineProperty
,利用重寫屬性的get
、set
,咱們能夠完成數據劫持監聽,使用觀察者模式,在數據發生改變的時候,通知訂閱者更新狀態。
咱們就針對Observer
觀察者部分寫了一個簡易的代碼以下:html
function Observer (data) {
this.data = data
this.walk(data)
}
Observer.prototype = {
walk: function (data) {
let me = this
Object.keys(data).forEach(function (key) {
me.convert(key, data[key])
})
},
convert: function (key, val) {
this.defineReactive(this.data, key, val)
},
defineReactive: function (data, key, val) {
let dep = new Dep()
let childObj = observe()
Object.defineProperty(data, key, {
enumerable: true,
configurable: false,
get: function () {
if (Dep.target) {
dep.depend() // 添加訂閱者
}
return val
},
set: function (newVal) {
if (newVal === val) return
val = newVal
childObj = observe(newVal)
dep.notify() // 通知訂閱器
}
})
}
}
function observe (value, vm) {
if (!value || typeof value !== 'object') {
return
}
return new Observer(value)
}
複製代碼
以上代碼中咱們定義了一個Observer
構造函數,即觀察者。利用Object.defineProperty
咱們將傳入對象的全部屬性(包含子屬性)所有進行數據監聽,並在get
方法中,在訂閱器裏添加一條訂閱。一旦某屬性發生改變,通知到訂閱器。vue
Dep訂閱器,Compile指令,Watcher訂閱者的代碼就再也不分析,mvvm的整體結構能夠由下圖看出java
Proxy是ES6中新增的構造函數,它能夠理解爲在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾改寫。Proxy原意是代理,在這裏咱們能夠理解爲「代理」某些操做。ios
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`proxy get ${key}`)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, receiver) {
console.log(`proxy set ${key}`)
return Reflect.set(target, key, value, receiver)
}
})
複製代碼
上面代碼對一個空對象架設了一層攔截,咱們能夠在Proxy的第二個參數中傳入一個handler
對象,對象中能夠定義攔截行爲。
在get
和set
中,咱們都用到了Reflect
。Reflect
對象與Proxy
對象同樣,也是ES6位了操做對象而提供的新API。Reflect
對象的方法與Proxy
對象的方法一一對應,好比Proxy
方法攔截target
對象的屬性賦值行爲。它採用Reflect.set
方法將值賦值給對象的屬性,確保完成原有的行爲,而後再部署額外的功能。
根據以上代碼咱們寫一段測試代碼:git
obj.text = 'hello world!'
// proxy set text
var _text = obj.text
// proxy get text
複製代碼
利用以上Proxy
的一些特性,咱們修改代碼以下:es6
function observe (value, vm) {
if (!value || typeof value !== 'object') {
return
}
let dep = new Dep()
return new Proxy(value, {
get: function (target, key, receiver) {
if (Dep.target) {
dep.depend()
}
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, receiver) {
dep.notify()
return Reflect.set(target, key, value, receiver)
}
})
}
複製代碼
咱們將傳入的對象直接替換爲Proxy
對象,入參handler
的get
和set
中的添加訂閱者和通知訂閱器邏輯保持不變。
整個過程沒有作其餘多餘的判斷,因爲Vue3.0尚未發佈,沒有實際源碼能夠借鑑,因此以上只是我的實現的簡單版本(完整代碼)。將整個mvvm運用到html中,如下是運行後的效果(沒作gif,湊合看吧):github
Object.defineProperty
,基於 Proxy 觀察者機制以知足全語言覆蓋及更好的性能。加上其它方法的優化改動,vue3.0能夠提速一倍/內存使用下降一半;Observer
模塊將能夠單獨做爲一個庫來使用。很遺憾的是,ES6的Proxy
沒法被轉譯爲ES5,因此它將不被IE所支持。對於這個問題,Vue3.0將給出IE11的兼容方案,即在IE11下,仍是使用的Object.defineProperty
機制。mvvm
ECMAScript 6 入門(阮一峯):es6.ruanyifeng.com/#docs/proxy
vue 3.0 更新計劃:更快,更小,讓開發者更輕鬆: www.oschina.net/news/101906…
爲何Proxy能夠優化vue的數據監聽機制: juejin.im/post/5bfe33…函數