vue 的数据响应式 和 如何实现

117 阅读2分钟

什么是 vue 的数据响应式?

  • 数据变化时,依赖数据的函数会重新运行(也就是数据跟函数之间的关联)。

如何理解 -- (数据变化时,依赖数据的函数会重新运行)这句话呢?

  • 譬如在 vue3 当中: render, watchEffectwatchcomputed 它们传的是一个函数吧,这个函数在运行的过程中是不是会用到一些数据, 而这个函数就是依赖这个数据的 name 属性 , 依赖了这个数据的 age 属性,当这个数据的 name 属性或者 age 属性发生变化的时候,这个函数是不是会重新运行啊。

  • 平时我们所说的数据响应式,都是数据变化了,界面也就刷新了, 可以想想界面在 vue 中是啥呀,它不就是一个 render 函数吗, 这个 render 函数里面是不是用到了一些数据,当这个数据变化的时候,导致 render 函数会重新运行啊, 然后重新渲染页面啊。

  1. 不是任何函数都可以与数据关联的,必须是被监控的函数(普通的函数不会被监控)
  2. 函数运行期间用到了响应式的数据
  3. 响应式数据变化了会导致函数重新运行
watchEffect(() => {
  state.name, state.age
})
watch(()=> {
  state.xxx
})
computed(() => {
  state.xxxx
})

vue 的数据响应式是如何实现的?

  • vue2 的响应式是基于 Object.defineProperty实现的
  • vue3 的响应式是基于 ES6 的 Proxy来实现的

注意: 上面的回答抽象了一点,但确实是回答出了 Vue 的两个版本的响应式实现的核心原理,并且 vue 的两各版本响应式的好坏就体现在 Object.definePropertyProxy 的差异上面。

vue2 估计大家都知道 vue2 基于 Object.defineProperty 的, 下面举个例子说明下吧

function reactive(obj, key, value) {
  Object.defineProperty(obj, key, {
    get() {
      console.log(`访问了${key}属性`);
      return value;
    },
    set(newValue) {
      console.log(`将${key}由->${value}->设置成->${newValue}`)
      if (newValue !== value) {
        value = newValue;
      }
    },
  })
}

const data = {
  name: '岩柏',
  age: 18
}
Object.keys(data).forEach(key => reactive(data, key, data[key]))
console.log(data.name)

// 访问了name属性
// 岩柏

data.name = "yanbai"
console.log(data.name)
// 访问了name属性
// yanbai

通过上面的例子,对 Object.defineProperty 有了一定的认知,那么问题来了? 它到底有什么弊端呢?咱们接着往下看


// 接着上面代码  给data对象添加一个新属性
data.hobby = '打豆豆'
console.log(data.hobby) // 打豆豆
data.hobby = '打篮球'
console.log(data.hobby) // 打篮球

这样大家可以明白它有什么弊端了吧,就是不能监听到对象新增的属性

  • data新增了hobby属性,进行访问和设值,但是都不会触发get和set
  • 弊端就是:Object.defineProperty只对初始对象里的属性有监听作用,而对新增的属性无效
  • 也是为什么Vue2中对象新增属性的修改需要使用Vue.$set来设值的原因。