可以实现双向绑定的方法有很多,基于观察者模式,基于数据模型,Angular基于脏检查的双向绑定,本篇我们重点讲vue的基于数据劫持和发布订阅的双向绑定。
vue实例化过程中进行了以下操作
...
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
...
在
initState
过程中初始化
data
,
computed
等属性
...
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
...
在这
initData
过程中完成了基于数据劫持和发布订阅的双向绑定,常见的基于数据劫持的双向绑定有两种实现,一个是目前Vue在用的Object.defineProperty,另一个是ES2015中新增的Proxy,而Vue的作者宣称将在Vue3.0版本后加入Proxy从而代替Object.defineProperty
defineProperty方式
-
Observer:监听者
对外提供一个
observe
方法,
利用
在Object.defineProperty
的时候对变量的每一个属性get
一个new
,收集依赖它的Dep
,在set的时候调用Watcher
的dep
方法去通知notify
更新Watcher
-
Dep:消息管理者,
是Observer和Watcher之间的纽带,在
过程中用set
数组存储subs
,同时调用Watcher
实例方法,让watcher
保存watcher
dep
-
Watcher:订阅者
使用
对象存储该depIds:{}
依赖的每一个对象属性的watcher
dep
缺点:
Object.defineProperty的第一个缺陷,无法监听数组变化。 然而Vue的文档提到了Vue是可以检测到数组变化的,其实作者在这里用了一些技巧,把无法监听数组的情况hack掉了。但是只有以下八种方法,vm.items[indexOfItem] = newValue这种是无法检测的。
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Object.defineProperty的第二个缺陷,只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择。
Proxy方式
Proxy在ES2015规范中被正式发布,它在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,我们可以这样认为,Proxy是Object.defineProperty的全方位加强版,具体的文档可以查看文档
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log(target, key, value, receiver);
if (key === 'text') {
input.value = value;
p.innerHTML = value;
}
return Reflect.set(target, key, value, receiver);
},
});
-
Proxy可以直接监听对象而非属性
Proxy直接可以劫持整个对象,并返回一个新对象,不管是操作便利程度还是底层功能上都远强于Object.defineProperty
-
Proxy可以直接监听数组的变化
当我们对数组进行操作(push、shift、splice等)时,会触发对应的方法名称和length的变化,我们可以借此进行操作
参考资料:
https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf#heading-11
https://www.cnblogs.com/canfoo/p/6891868.html
https://segmentfault.com/a/1190000006599500#item-6