vue响应式原理:从defineReactive学习Object.defineProperty用法

众所周知,vue响应式原理就是用的Object.defineProperty方法去定义setter和getter的。多数人一上来就觉得很简单,然后写出如下代码:

let person = {age: 18}

Object.defineProperty(person, 'age', {
    get: function(){
        console.log('getter call')
        return person.age
    },
    set: function(val){
        console.log('setter call')
        person.age = val
    }
})

然后一调用,报错了:

VM560:4 Uncaught RangeError: Maximum call stack size exceeded
    at Object.get [as age] (<anonymous>:4:18)
    at Object.get [as age] (<anonymous>:6:23)
    at Object.get [as age] (<anonymous>:6:23)
    at Object.get [as age] (<anonymous>:6:23)

问题就在于,上面的代码会递归调用get和set方法。 那么如何才能避免这个问题呢,答案就是用闭包

使用闭包之前,先来学习下人家尤大大是怎么写的,下面是简化修改后留下关键部分的代码:

export function defineReactive (
  obj: Object,
  key: string
) {
  // ... 省略源码中收集依赖通知变更的代码
  let val = obj[key]  // 源码这里是传入的参数
  const property = Object.getOwnPropertyDescriptor(obj, key)
  const getter = property && property.get
  const setter = property && property.set

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      // 收集依赖
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      // 通知变更
    }
  })
}                                                                                                                                                                                        

从上面代码中可以看出,执行defineReactive会产生一个闭包,闭包中的变量val, property, getter, setter都是被新定义的属性的getter, setter持有引用的。这样,当属性没有定义getter,setter函数时,经过defineReactive函数修饰后,每次修改属性值,reactiveSetter函数就会把新的值赋值给val变量, 这样再调用reactiveGetter时就会返回这个val值.
以上就是这篇文章所讲的全部了。关于依赖收集和通知变更相关逻辑,有兴趣可以看登录GitHub查看vue源码,源码在src/core/observer/index.js

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容