Vue-浅析数据响应式

144 阅读3分钟

一、什么是数据响应式

简单来说,数据响应式是指页面视图会随着数据的变化而变化。 比如:

<div id="app">{{ data }}</div>
const vm = new Vue({
    el:'#app',
    data:{
        data:'hello'
    }
})

执行代码后,页面中出现 hello

vm.data = 'hi'

执行代码后,页面中 hello 更新为 hi

二、制造可响应数据

当你把一个普通的JavaScript对象传入Vue实例作为 data 选项,Vue将遍历对象所有属性,并使用Object.defineProperty()把这些属性全部转为getter/setter

getter/setter

getter/setter用于对属性的读写进行监控


var obj = {
  姓: "张",
  名: "三",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xx) {
    this.姓 = xx[0]
    this.名 = xx.slice(1)
  }
};
console.log(obj.姓名) // 张三
obj.姓名 = "李四"
console.log(obj.姓名); // 李四

get 可以使得函数obj.姓名()转变成属性obj.姓名来读取 set 接受一个新的传参,这个参数就是后来传进来的obj.姓名 = "李四",我们可以对其进行操作

Object.defineProperty()

Object.defineProperty()用来给已经声明过的对象添加gettersetter操作的方法。

我们现在给刚刚的obj添加一个属性 xx,值为 0 ,这个属性是可响应的

var _xx = 0 // 隐藏属性,用来存储'xx'的值
Object.defineProperty(obj, 'xx', { // 使用Object.defineProperty 给 obj 对象添加一个可响应的属性 'xx'
  get() {
    console.log("正在读取属性")
    return _xx
  },
  set(value) {
    _xx = value
    console.log("已经修改好属性")
  }
})

console.log(obj.xx) // 正在读取属性 0
obj.xx = '王五' // 已经修改好属性
console.log(obj.xx) // 正在读取属性 王五

当读取 xx 时候,不但返回 xx 的值,还会打印"正在读取属性",当修改完 xx 的值,会返回"已经修改好属性"

使用代理制造可响应的数据

上面Object.defineProperty()的例子中,我们给obj创建了一个隐藏属性 "_xx",用来存储 xx 的值。在对 xx 进行读写操作时,其实都是在操作 _xx ,如果我们直接用 this.xx 作为返回值,就会陷入死循环,因为 this.xx 返回之前,浏览器会读取这个属性,就再一次出发了get 操作,无限循环。所以需要一个 _xx 属性来存储 xx 的值,这样我们在 return 的时候就不会陷入死循环。但是如果我们直接操作 _xx 的话也可以修改 xx ,不会触发我们设置的 setter/getter。设置代理就可以解决这个问题

var myData = { xx: 0 }
var obj = proxy({ data: myData });
// proxy 就是代理,返回一个和 data 属性一样的对象
function proxy({ data }) {
  let value = data.xx
  Object.defineProperty(data, 'xx', {
    get() {
      return value
    },
    set(newValue) {
      value = newValue
    }
  })
  // 上面监听 data , 下面进行代理
  const obj1 = {} // 这个对象是代理对象
  Object.defineProperty(obj1, 'xx', {
    get() {
      return data.xx;
    },
    set(value) {
      data.xx = value;
    }
  })
  return obj1
}
console.log(obj.xx); // 0
obj.xx = "王五";
console.log(obj.xx); // 王五

三、Vue检测对象和数组的变化

对于对象

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。

可以使用Vue.set或this.$set方法向嵌套对象添加响应式属性

Vue.set(vm.object, propertyName, value)
this.$set(this.object, propertyName, value)

对于数组

Vue无法检测到数组新增了下标或利用下标设置数组项。

可以使用Vue.set或this.$set方法

Vue.set(vm.items, indexOfItem, newValue)

也可以使用Vue对数组的一些变更方法:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()