【Vue】数据响应式

176 阅读7分钟

get set

  • get可以给对象的虚拟属性姓名赋值,具体赋啥值是由里面的代码决定的。用obj.姓名可以读取该值

  • set必须给虚拟属性姓名一个值,然后对这个值做些啥。用obj.姓名 = 高圆圆

  • 虚拟属性 不存在 不能在其他地方调用

  • Object.defineProperty给一个对象再定义完之后添加新属性 具体语法

Object.defineProperty(obj,'xxx',{
    get(){
        return 0
    },
    set(value){
        value+=1
    }
})
obj.xxx  //0
obj.xxx = 3 

//给obj对象一个虚拟属性xxx,get用来给这个虚拟属性xxx赋了个值0;set用来给虚拟属性xxx一个值,然后对这个值干了些啥。

Vue对data做了些啥

注意n的值就是0,不是{value:0}

  • get给虚拟属性n赋值为this._n。直接data2.n就可以拿到
  • set要求必须给虚拟属性一个值,如果这个值小于0就没啥,大于0就把这个值给this._n。使用data2.n = value
  • 代理obj(data3)的n就是data的n;给代理obj的n一个大于0的值,才会把这个值给data的n。
  • data3就是obj ,代理的n就是数据的n,
  • 你永远无法接触到n
  • 对data的n读写完全不用他自己,而是通过对obj的n读写来实现

  • 监听mydata(data)中的n:先把data的n偷来当原始值,再从data中删掉n,再给data一个虚拟的n。这个虚拟属性n的值就是之前偷来的n的值,如果给虚拟属性n一个大于0的值就把该值给之前偷来的n值。这样data中的n有任何改变我们都知道看得见。只有满足我们的条件我们才会允许data中的n改变。
  • 所以虽然刚开始myData5(data)被声明在前面了,但是只要一对他执行proxy2函数,就把它的n偷来了,还把他在原来的位置删掉了,我们给还了它一个虚假的n,虚假的n被我们用get set监控着。它要是在外面想改变自己的虚假的n,必须经过我们的同意满足我们的条件才行。
  • obj(data5)就是代理 ,obj(data5)的n就是data(mydata)的n

vm = new Vue({data: myData})

首先,会马上对myData的所有属性进行监控,也就是对myData进行改造

  • 防止myData的属性在外面变了,vm不知道,知道属性变了就可以调用render(data)呀!UI = render(data)

  • 做法: 先把myData的属性n偷来变成我的value,在他的原位置删掉,我给你再补偿一个虚假的n,这个虚假的n一直被我用get(value) set(value)控制,听我的话,你在外面修改n,其实是我给你的虚假的n,都被我控制了。

  • 你对data的任何修改,我Vue必须知道,不然我怎么对数据去渲染

其次,会让vm成为改造后的myData的代理(proxy)

  • 所以vm(中介)就是myData(房东)的代理,vm的n就是myData的(虚假的)n;对vm的n赋值,就相当于对myData的(虚假的)n赋值;完全不直接操作myData的你,而是操作vm的n
  • 而且this就是vm。所以this.n === vm.n === myData.假n

Object.defineProperty

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

啥是代理(设计模式)

  • 对myData对象的属性读写,全权由另一个对象vm负责
  • 那么vm就是myData的代理(类比房东租房)
  • 比如myData.n不用,偏要用vm.n来操作myData.n

数据响应式

“响应式”,是指当数据改变后,Vue会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。

  • options.data
  • 会被Vue监听
  • 会被Vue实例代理
  • 每次对data的读写都会被Vue监控
  • Vue会在data变化时更新UI(因为UI用到了data,data变UI也变)

定义

  1. 什么是响应式

我打你一拳,你会喊疼,那你就是响应式的。若一个物体能对外界的刺激做出反应,它就是响应式的。

  1. Vue的data是响应式

const vm = new Vue({data: {n: 0}) 我如果修改vm.n(data.n this.n),那么因为视图是对数据的渲染,所以视图中的n(UI中的n)也会改变做出反应也就是响应。这就是数据响应式。

  1. 如何实现数据响应式 Vue 2通过Object.defineProperty(和get set)实现对数据的监听从而实现数据响应式

  2. 题外话:响应式网页是啥?

  • 如果我改变窗口大小,网页内容会做出响应,那就是响应式网页
  • 比如这个网站
  • 但是你要注意,用户没事不会拖动网页大小的

Vue的data有bug:如果数据中有新增加的内容,那该怎么去监听?

现象

监听数据的话,用Object.defineProperty(obj, 'n' ,.})的话,数据中必须要有真的n,才能监听&代理obj.(假的)n

如果前端开发者比较水,没有给真的n怎么办,根本没有n的监听

示例一 让视图中出现一个没有被声明的数据n

  • vue对于undefined或者null就不会显示
  • Vue会给出一个警告:属性或者方法n没有定义在实例上,但是却被你在渲染时引用了

示例二:

  • data中定义了obj,obj中有个a,所以obj和obj.a都被Vue监听&代理了(vm.obj==this.obj===myData.obj),对a变化了因为视图因为用了a所以也会改变
  • 但是并没有声明obj中还有个b,所以没有对obj.b监听&代理。所以对obj.b赋值并且放在视图,视图根本没变化,Vue认为根本没这样的存在。

解决办法

  • 方法一:那我把数据中每个key一次性都声明好,也就全部监听好了
obj{
    a:0,
    b:undefined
}
  • 方法二:使用Vue.set或者this. $set
setB(){
Vue.set(this.obj,'b',1)

//或者,多了个$是为了防止和data中万一有个set咋办
this.$set(this.obj,'b',1)
}

作用

①新增key

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

③触发UI更新(但并不会立刻更新)

数组解决办法

data中有数组怎么办?你没法提前声明所有key

  • 方法一:使用Vue.set或者this. $set ,新增key,但是不会创建监听和代理,会更新UI

示例1:数组的长度可以一直增加,下标就是key 你看,你没有办法提前把数组的key都声明出来 Vue也不能检测对你新增了下标 难道每次改数组都要用Vue.set或者this. $set

  • 方法二:尤雨溪篡改了数组的API,见官网中「变异方法」章节,这7个API都会被Vue篡改,这7个新API会调用数组原来的API并且自动增加监听和代理,并更新UI

尤雨溪是如何篡改的:新增加一层原型,这层原型继承了数组原型,这层原型有七个API,这七个API都会调用之前的数组原型中对应的那个API,之后set该set的东西。

push为例

class VueArray extends Array{   //新增加一层原型VueArray,继承以前的Array原型
push(...args){  //新原型的push方法
const oldLength = this.length // this就是当前数组
super.push(...args)       //会调用上一层原型(原来数组)的push方法
console.log(' push ')
for(let i = oldLength; i<this.length; i++){  //把之前的下标和现在新的下标找出来,中间的不就是新增加的,那就set他们就行了。
Vue.set(this, i, this[i])
// key Vue
} } }  

总结

  1. 数据的对象中想新增的key,Vue没有办法事先监听和代理,要使用set来新增key,创建监听和代理,更新UI。结论:数据的对象的话提前把属性都写出来,不要新增key
  2. 但数据的数组做不到「不新增keyJ,数组中新增的key,也可用set来新增key,但是不会创建监听和代理,会更新UI;还有一种方法就是尤玉溪篡改了7个API方便你对数组进行增删(改不用监听,查不用监听),这7个API会自动处理监听和代理,并更新UI。因此结论:数据的数组新增key最好通过7个API