Vue 数据响应式

202 阅读2分钟

就像响应式页面一样,Vue的数据响应式就是在数据发生改变时能够在页面上反应出来。例如 const vm = new Vue ({data:{n:0}),如果修改了vm.n,那么UI中的n就会响应。

1. 追踪数据变化

1.1 关于getter 和 setter

  • getter——用于获取值。在函数前面加上 get 即可获取到函数值,之后使用函数值时不需要加函数括号()。(可理解为不加括号的函数)(读属性)
  • setter——修改原值。有 get 就有 set ,setter需要传入参数。(写属性)
1: get
let obj2 = {
  get 姓名() {  //get——获取一个值
    return this.姓 + this.名;
  }
};
console.log("需求二:" + obj2.姓名);//这里不需要函数的括号()2: set
let obj3 = {
  姓: "高",
  名: "圆圆",
  set 姓名(xxx){
    this.姓 = xxx[0]   //不考虑复姓得到姓
    this.名 = xxx.slice(1)
  }
};
obj3.姓名 = '刘诗诗'
console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)

1.2 Object.defineProperty()

Object.defineProperty()——会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

在Vue里,定义完一个对象之后,想在这个对象添加新的属性,就使用Object.defineProperty()。

Object.defineProperty()内传入的参数:第一个是要操作的对象;第二个是要定义的属性的名字(例如xxx);第三个参数,如果含有get或set,就不需在其后面加属性名。但是由于xxx属性不存在,所以需要找一处放置xxx属性的值(例如x)。且get返回时不能返回属性xxx,因为xxx不存在会不停的调用get结果陷入死循环。

var x = 10   //放置 xxx 的值
Object.defineProperty(obj3,'xxx',{
  get(){
    return x //不能返回xxx,xxx属性不存在
  },
  set(value){
    x = value // 这里也是不能写成 xxx = value
  }
})

2. 新增key(对于对象)

Vue.set 或者 this.$set

由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

var vm = new Vue({
  data:{
    a:1
  }
})
// vm.a是可以显示的
// 但是 vm.b不会显示,因为 vm.b 不存在

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。

解决方法

  • 一开始将对象的key就声明好;
  • Vue.set 或者 this.set——添加key。(this.set——添加key。 (this.set(this.obj,'b',1) === Vue.set(this.obj,'b',1))
new Vue({
  data: {
    obj: {
      a: 0 // obj.a 是响应式的
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {     //新添 obj.b 响应式
      this.$set(this.obj,'b',1)
      //或 Vue.set(this.obj,'b',2)
    }
  }
}).$mount("#app");

Vue.set 和 this.$set的作用:

  • 新增key;
  • 自动创建代理和监听;
  • 触发UI更新。

3. 新增key(对于数组)

3.1 对于数组

Vue 不能检测以下数组的变动:

  • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  • 当你修改数组的长度时,例如:vm.items.length = newLength 例如:
var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

当data内是数组时,也可以通过Vue.set或者this.$set添加修改key,但是也可以用被包裹的数组变更方法。(具体可看3.2 变更方法)

3.2 变更方法

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。(Vue将数组的API进行了更改,当数组在Vue内时,会在数组的原型中加上一层原型链。)这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse() 这 7 个API会自动处理监听和代理,同时也将在响应式系统内触发状态更新。以.push()为例:
new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
       this.array.push('d')  // 更新显示[ "a", "b", "c", "d" ]
    }
  }
}).$mount("#app");

参考:Vue的深入响应式原理