Vue源码学习-番外篇-proxy实现数据监听

proxy在中文里往往翻译成代理的意思,在JS中,它可以在对象间架设一层中间层,访问一个对象时需要先通过这一层:

var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  }
});

可以看到和我们之前使用Object.defineProperty来实现数据绑定很相似。

Proxy是ES6中的新特性,它非常强大,但这里我们不详细解释,更多关于Proxy的内容可以参考这里

首先我们来试一下简单的数据类型:

const person = {
      name: 'jack',
      age: 25,
      info: {
        prof: 'teacher',
        city: 'beijng'
      }
    }

const observer = data => new Proxy(data, {
  get(target, prop) {
    console.log(`getting the ${prop}`)
    return target[prop]
  },
  set(target, prop, value) {
    console.log(`setting the ${prop} to ${value}`)
    target[prop] = value
  }
})

let proxy = observer(person)

效果如图:

1.gif

可以看到这个效果和我们之前所使用的Object.defineProperty所达成的是一样的。那么对于属性的值为数组和对象的复杂类型呢?

直接按上述的代码中的Proxy实例来操作属性其实是不行的,但是我们可以对数组或对象类型的属性也代理一层:

const person = {
  name: 'jack',
  age: 25,
  info: {
    prof: 'teacher',
    city: 'beijng'
  },
  children: ['lucy', 'lily', 'luc']
}

const observer = data => new Proxy(data, {
  get(target, prop) {
    console.log(`getting the ${prop}`)
    return target[prop]
  },
  set(target, prop, value) {
    console.log(`setting the ${prop} to ${value}`)
    target[prop] = value
    // 注意这里返回的true
    return true
  },
})

let proxy = observer(person)
let proxyChildren = observer(person.children)

此时我们对proxyChildren进行操作,也是可以实现对数据的监听:

2.gif

值得注意的是,之前如果set函数没有返回一个true值,那么会抛出TypeError: 'set' on proxy: trap returned falsish for property '3'错误,MDN上对这一点其实有记录:

Return value

The set method should return a boolean value. Return true to indicate that assignment succeeded. If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.

总结

结合上述的代码,我们已经可以使用proxy来拦截一个对象的属性,以达成数据监听的目的,对于它的属性为复杂类型时,也只需要再次将其包装为一个proxy实例。

下一篇我们再继续深入来尝试实现数据和视图的双向绑定。