阅读 141

浅析 Vue 响应式

什么是响应式?

追踪数据的变化,在数据被访问时做出一定的动作(翻译翻译:数据发生变化后,会重新渲染页面)。

Vue2

Vue2 官网:当把一个 JS 对象传入 Vue 实例作为 data 选项时,Vue 将遍历这个对象的所有属性,并使用 Object.defineProperty() 把这些属性全部转化为 getter/setter。这些 gettet/setter 对用户来说是不可见的,但是在内部它们让 Vue 可以追踪依赖,在属性被修改时通知变更。

Object.defineProperty

所以我们得先搞清楚如何使用 Object.defineProperty

let obj = {
  a: 0
}

Object.defineProperty(obj, 'a', {
  get: () => {
    console.log('get') // 获取属性 a 时执行的动作
    return a
  },
  set: (newVal) => {
    console.log('set') // 设置属性 a 时执行的动作
    a = newVal
  }
})

obj.a = 100 // 输出 'set'
obj.a // 输出 'get'
复制代码

封装

然后简单封装一下:

现在我们有一个已知的复杂对象 obj

let obj = {
  a: 0,
  b: [1, 2, 3],
  c: {
    d: 4
  }
}
复制代码

我们想办法设计一个 observe 函数将这个 obj 变成响应式的:

observe(obj) // 将 obj 传递给 observe 来实现 obj 的响应式
复制代码

observe 函数:

function observe(data) {
  if (!data || typeof data !== 'object') {
    return
  }
  for (var key in data) {
    let val = data[key]
    Object.defineProperty(data, key, {
      enumerable: true, // 当前key是可枚举的
      configurable: true, // 当前key是可配置的(可修改、可删除...)
      get: () => {
        console.log('get') // 获取属性时执行的动作
        return val
      },
      set: (newVal) => {
        console.log('set') // 设置属性时执行的动作
        val = newVal
      }
    })
    if (typeof val === 'object') {
      observe(val) // 发现属性也是对象时,继续调用observe(深度遍历)
    }
  }
}
复制代码

这样执行 observe(obj) 后,obj 对象里面的 abb[0]b[1]b[2]cc.d 都是响应式的。但如果是新增属性,例如 obj.e 或者 obj.b[3] 就不是响应式的。

Vue3

Proxy

Object.defineProperty 升级为 Proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

先看看 Proxy 的使用方式:

let obj = {
  a: 0
}

let handler = {
  get(target, prop) {
    console.log('get') // 获取属性时执行的动作
    return Reflect.get(...arguments) // return target[prop]
  },
  set(target, key, value) {
    console.log('set') // 设置属性时执行的动作
    return Reflect.set(...arguments) // return target[key] = value
  }
}

let proxy = new Proxy(obj, handler) // 将对象obj进行代理

proxy.a // 输出 'get'
proxy.a = 1 // 输出 'set'
复制代码

封装

然后简单的封装一下:

let obj = {
  a: 0,
  b: [1, 2, 3],
  c: {
    d: 4
  }
}

function reactive(data) {
  let handler = {
    get(target, prop, receiver) {
      console.log('get') // 获取属性时执行的动作
      let value =  Reflect.get(...arguments)
      if (typeof value === 'object') {
        return reactive(value) // 发现属性也是对象时,继续调用 reactive 来进行代理
      } else {
        return value
      }
    },
    set(target, key, value, receiver) {
      console.log('set') // 设置属性时执行的动作
      return Reflect.set(...arguments)
    }
  }
  return new Proxy(data, handler)
}

let proxy = reactive(obj) // 将对象obj进行代理
复制代码

这样执行 reactive(obj) 后,proxy 对象里面的 abb[0]b[1]b[2]cc.d 都是响应式的。并且如果有新增属性,例如 proxy.e 或者 proxy.b[3] 也是响应式的。

文章分类
前端
文章标签