Vue:数据式响应

上一篇写了构造选项options,data作为内部数据,还有很多东西值得探讨。Vue官方文档在data下提供了参考链接深入响应式原理,这篇文章主要记录对响应式原理的深度学习。

3个例子

例1

import Vue from "vue/dist/vue.js"; 

Vue.config.productionTip = false;

const myData = {
  n: 0
}
console.log(myData)

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

setTimeout(()=>{
  myData.n += 10
  console.log(myData)
},3000)
复制代码

一开始是{n:0},传给new Vue之后立马变成{n:(...)}

image-20210608155120155.png

例2

getter和setter

gettersetter是ES6的新属性

MDN-getter、setter

举例

let obj0 = {
  姓: "坏",
  名: "东西",
  age: 18
};

// 需求一,得到姓名

let obj1 = {
  姓: "坏",
  名: "东西",
  姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求一:" + obj1.姓名());
// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?

// 需求二,姓名不要括号也能得出值

let obj2 = {
  姓: "坏",
  名: "东西",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求二:" + obj2.姓名);

// 总结:getter 就是这样用的。不加括号的函数,仅此而已。

// 需求三:姓名可以被写

let obj3 = {
  姓: "坏",
  名: "东西",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx) {
    this.姓 = xxx[0];
    this.名 = xxx.substring(1);
  },
  age: 18
};

obj3.姓名 = "好东西";

console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`);

// 总结:setter 就是这样用的。用 = xxx 触发 set 函数
复制代码

console

image-20210608155912102.png

打印obj3

image-20210608201522957.png

可以看出,可以对姓名读写,但是并没有姓名这个属性,是get 姓名set 姓名来模拟对姓名的操作。

Object.defineProperty

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

如果想要改obj3姓名,需要重新定义一个_xxx,因为xxx这个属性实际上不存在。

let obj0 = {
  姓: "坏",
  名: "东西",
  age: 18
};

// 需求一,得到姓名

let obj1 = {
  姓: "坏",
  名: "东西",
  姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求一:" + obj1.姓名());
// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?

// 需求二,姓名不要括号也能得出值

let obj2 = {
  姓: "坏",
  名: "东西",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求二:" + obj2.姓名);

// 总结:getter 就是这样用的。不加括号的函数,仅此而已。

// 需求三:姓名可以被写

let obj3 = {
  姓: "坏",
  名: "东西",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx) {
    this.姓 = xxx[0];
    this.名 = xxx.substring(1);
  },
  age: 18
};

// 改
var _xxx = 0
Object.defineProperty(obj3,'xxx',{
  get(){
    return _xxx
  },
  set(value){
    _xxx = value
  }
})

obj3.姓名 = "好东西";

console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`);
console.log(obj3)

// 总结:setter 就是这样用的。用 = xxx 触发 set 函数
复制代码

例3

总结

  1. Object.defineProperty

    可以给对象添加属性value,可以给对象添加getter/settergetter/setter用于对属性的读写进行监控。

  2. 代理

    一种设计模式,对myData对象的属性读写,全权由另一个对象vm负责,那么vm就是myData的代理,不如用vm.n来操作myData.n

  3. vm = new Vue({data:myData})

    会让vm称为myData的代理(proxy)

    会对myData的所有属性进行监控(为了防止vm不知道myData属性变了)

    vm知道myData属性变了,可以调用render(data)UI = render(data)

示意图

image-20210608215535910.png

如果data有多个属性m、n、k,那么就会有get m、get n、get k等

Vue的data——响应式

const vm = new Vue({data:{n:0}})

如果修改vm.n,那么UI中的n就会回应,Vue2通过Object.defineProperty来实现数据式响应

响应式网页是什么?

如果改变窗口大小,网页内容就会做出相应,就是响应式网页

Vue.setthis.$set

先看一个例子

import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
  data: {},
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");
复制代码

Vue有一条报错,意思是n没被定义,但是在render的时候被引用了

image-20210608231256975.png

这是Object.defineProperty的问题,Object.defineProperty(obj,'n',{...})必须要有一个'n',才能监听和代理obj.n,这里data为空。

再看下面这个例子

import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1; 
    }
  }
}).$mount("#app");
复制代码

点击页面的按钮,并不是显示1,但也不会报错

image-20210608231743211.png

这是为什么呢?

因为Vue只会检查第一层属性,第一层没问题就不会报错。点击也不会显示1,因为Vue没法监听一开始就不存在的obj.b

使用Vue.setthis.$set就可以解决这个问题

    setB() {
      // this.obj.b = 1;
      Vue.set(this.obj,'b',1)
      // 或
      this.$set(this.obj,'b',1)
    }
复制代码

作用:

新增key

自动创建代理和监听(如果没有创建过)

触发UI更新,但并不会立刻更新

变更方法

举个例子

import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      this.array[3] = "d"; //页面中不会显示 'd' 
      this.$set(this.array,3,"d");  // 页面显示'd'
    }
  }
}).$mount("#app");
复制代码

image-20210609001623322.png

比如上面这个数组,数组长度可以一直增加,下标就是key,这样没法提前声明所有key,Vue也不能检测到新增了下标,使用Vue.setthis.$set,并不会自动添加监听和代理。

尤雨溪的做法是篡改数组API,Vue文档-变更方法

push()
pop()
shift()
unshift()
splice()
sort()
reverse()
复制代码

这7个API被Vue篡改,调用后会更新UI

比如上面的例子,改写一下

setD(){
  // this.$set(this.array,3,"d"); 
  this.array.push('d');
}
复制代码

打印array,可以看到,原型上有这7个方法

image-20210609001959901.png

this.$set作用于数组时,并不会自动添加监听和代理。

使用Vue提供的数组变更API时,会自动处理监听和代理,并更新UI,所以数组新增key最好通过这7个API。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享