Vue-数据响应式

  • 一个物体能对外界的刺激作出反应,那他就是响应式的
    1. const vm = new Vue({data:{n:0}})
    2. 我如果修改 vm.n ,那么UI中的 n 就回来响应我
    3. Vue通过Object.defineProperty来实现数据响应式
  • Vue通过Object.defineProperty的 getter/setter 对收集的依赖项进行监听,在属性被访问和修改时通知变化,进而更新视图数据

getter/setter

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

getter

let obj1 = {
  姓: "高",
  名: "圆圆",
  姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log( obj1.姓名());
// 高圆圆
let obj2 = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log( obj2.姓名);
// 高圆圆
  • 对比代码,发现只是在姓名函数处添加了 get ,最后使用的时候就不再需要加 ()
  • getter 就是这样用的,不加括号的函数

setter

let obj3 = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  age: 18
};

obj3.姓名 = '刘诗诗'

console.log(`姓 ${obj3.姓},名 ${obj3.名}`)
// 姓 刘,名 诗诗 
  • setter 就是这样用的,用 = xxx 触发 set 函数

Object.defineProperty

用于给对象添加新属性,也可以给对象添加 getter/setter

let obj = {}

Object.defineProperty(obj, 'x', {value: 1})

Object.defineProperty(obj, 'y', {
  get(){...}
  set(value){...}
})

监听与代理

  • 对 myData 对象的属性读写,全权由另一个对象 vm 负责
  • 那么 vm 就是 myData 的代理 (类似房东租房通过中介)
  • 比如 myData.n 不用,偏要用 vm.n 来操作 myData.n
    代码案例:bold-heyrovsky-8jcm2 - CodeSandbox

vm = new Vue({data: myData}) 就做了和上面代码类似的事情。

  1. vm 成为了 myData 的代理
  2. 会对 mydata 的所有属性进行监控

目的: 无论直接修改 myData.n 还是修改 vm.n,vm 都会收到通知,然后调用触发重新渲染

Vue 对 methods 和 computed 也有类似处理。

Object.defineProperty 的问题

Object.defineProperty(obj, 'n', {...}
必须有 'n' 才可以监听&代理 obj.n

//无 data.n
new Vue({
  data: {},
  template: `
    
{{n}}
` }).$mount("#app");

无 data.n 或 data.n 的值为 undefined ,此时 n 被引用,不会有显示,控制台会报警告

//有 data.obj,但无 data.obj.n
new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    
{{obj.b}}
`, methods: { setB() { this.obj.b = 1; //不显示1 } } }).$mount("#app");

data.obj 中开始并没有 b,后面运行 this.obj.b = 1 并不会让 1 出现在页面中,因为 vm 没有监听&代理 data.obj.b

解决办法:

  1. 在开始就定义好 data.obj.n = undefined
  2. 使用 Vue.set 或者 this.$set
    一个特例:
//html
{{obj.a}} {{obj.b}}
js //js var app = new Vue({ el: '#app', data: { obj: { a: 'a', } }, }) app.obj.a = 'a2' app.obj.b = 'b'

最终 span-a 中会显示 a2,span-b 中显示 b
这是因为视图更新是异步的,a1 变成 a2 时,Vue 监听到这个变化,并不会马上更新视图,而是创建一个视图更新任务到任务队列里。然后继续运行代码 app.obj.b = 'b'。视图更新时,Vue 会去做 diff,发现 a 和 b 都变了,于是去更新 span-a 和 span-b。

Vue.set 和 this.$set

作用:

  1. 新增key
  2. 自动创建代理和监听(如果没有创建过)
  3. 触发UI更新(不会立即更新)
this.$set(this.object,'m',100)

变异方法

在数组中,数组的长度一直增加,下标就是key,没办法提前声明所有下标,难道每次改数组都要Vue.set 或者 this.$set?

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    
{{array}}
`, methods: { setD() { this.array[3] = "d"; //不显示d } } }).$mount("#app");

使用变异方法
push()、pop()、shift()、unshift()、splice()、sort()、reverse()

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    
{{array}}
`, methods: { setD() { this.array.push('d'); //修改后的push可完成我们想要的操作 } } }).$mount("#app");

你可能感兴趣的:(Vue-数据响应式)