天天看点

vue的双向绑定原理---defineProperty->Proxy

可以实现双向绑定的方法有很多,基于观察者模式,基于数据模型,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

    ,收集依赖它的

    Watcher

    ,在set的时候调用

    dep

    notify

    方法去通知

    Watcher

    更新
  • Dep:消息管理者,

    是Observer和Watcher之间的纽带,在

    set

    过程中用

    subs

    数组存储

    Watcher

    ,同时调用

    watcher

    实例方法,让

    watcher

    保存

    dep

  • Watcher:订阅者

    使用

    depIds:{}

    对象存储该

    watcher

    依赖的每一个对象属性的

    dep

    vue的双向绑定原理---defineProperty->Proxy
缺点:
  1. Object.defineProperty的第一个缺陷,无法监听数组变化。 然而Vue的文档提到了Vue是可以检测到数组变化的,其实作者在这里用了一些技巧,把无法监听数组的情况hack掉了。但是只有以下八种方法,vm.items[indexOfItem] = newValue这种是无法检测的。

    push()、pop()、shift()、unshift()、splice()、sort()、reverse()

  2. 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

vue