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…函数