Vue 数据响应式原理

357 阅读2分钟

数据响应式即数据改变,UI页面做出响应进行更新。data 是响应式,当修改 Vue 的构造选项 data 中的属性时,UI页面会做出响应,我们知道 Vue 是通过 Object.defineProperty 来实现数据响应的。那么具体原理是怎样的呢?

Object.defineProperty()

  • 给对象定义新属性 value
  • 给对象添加 getter/setter
  • getter/setter 方法用于对属性的读写进行监控

getter/setter

  • 实际上是对伪属性的读写,并不是真实属性
let obj = {
  姓: '小',
  名: '白',
  get 姓名() {
    return this.姓 + this.名
  },
  set 姓名(xxx) {
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  }
}
console.log(`姓名:${obj.姓名}`)		//姓名:小白
obj.姓名 = '太阳'		//触发 set 函数
console.log(`姓:${obj.姓},名:${obj.名}`)		//姓:太,名:阳
console.dir(obj)	//姓名并不是一个真正的属性,而是使用 getter/setter 方法来模拟操作

实际上,Vue data 中的 xxx 属性在经过了 new Vue()之后也是变成了 xxx:(...)

在Object.defineProperty()中使用 getter/setter

var _n = 0		//用来存储伪属性 n 的值
Object.defineProperty(obj,'n',{
  get(){
    return _n	//不可使用 this.n ,是不存在的
  },
  set(value){
    _n = value
  }
})
console.log(`${obj.n}`)		//0
obj.n = '5'
console.log(`${obj.n}`)		//5
_n = 6
console.log(`${obj.n}`)		//6

const vm = new Vue({data:myData})

  • 让 vm 成为 myData 的代理 (proxy)
  • 对 myData 所有属性进行监控

代理 (proxy)

  • 对 myData 对象的属性读写,都由另一个对象 vm 负责。
  • 比如 myData.n 不用,使用 vm.n 来操作 myData.n。
    例:
let myData = {
  n: 0
}
let data2 = proxy({
  data: myData
})
function proxy({data}) { //解构赋值
  const obj = {}
  Object.defineProperty(obj,'n', {
      get() {
          return data.n
        },
        set(value) {
          if (value > 10) return //监控
          data.n = value
        }
    })
  return obj  //obj 是 myData 的代理
}
data2.n = 9
console.log(`${data2.n}`)		//	9
data2.n = 11
console.log(`${data2.n}`)		//	9
myData.n = 11
console.log(`${data2.n}`)		//  11! 不是9?

obj 定义了一个伪属性,代理使得 obj 通过 getter/setter 来操作 myData 的值,并且实行了监控。
但是有一个问题,当直接对 myData 的值进行了修改,obj 是无法监测进行操作到的。如以上代码, 就相当于 在 Vue 中,直接修改 myData.n ,绕过了 vm,UI页面也因此没有响应。 那么 如何解决的呢? 给对象添加数据监听

添加数据监听

  • 用一个 value 储存 属性值 (data.n)
  • 将属性 n 删去
  • 使用 Object.defineProperty() 给 对象 data 定义一个伪属性 n
  • 实际上这个伪属性 n 是取代了真正的 data.n,第二步操作在代码实践时可直接省略
  • 通过 getter/setter 模拟操作 n 来监听 data


监听 data 只需要添加以下代码

let value = data.n
  Object.defineProperty(data,
    'n', {
      get() {
          return value
        },
        set(newValue) {
          if (value > 10) return //监控
          value = newValue
        }
    })

总结

  • 对 data 数据进行篡改并监听,将属性变成伪属性
  • 让 vm 成为 myData 的代理
  • 对 myData 的所有属性进行监控
  • 修改 vm.n ,UI页面做出响应