vue 存储对象 不要监听_Vue 数据响应式深度理解

v2-2a37b7a417c54b81e654c0bf4345523f_1440w.jpg?source=172ae18b

什么是响应式

我踢你一脚,你会喊疼,那你就是响应式的

若一个物体能对外界的刺激做出反应,它就是响应式的

响应式网页

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

比如前端著名响应式网页

Vue 的 data是响应式

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

我如果修改vm.n,那么UI中的n就会响应我

Vue 2通过Object.defineProperty来实现数据响应式

vue.js的追踪变化,使用Object.defineProperty把这些属性全部转为getter/setter,而受到defineProperty的限制,vue不能检测到对象属性的添加删除,这个Bug是使用Vue.set或vm:$set来解决的。

理解vue响应式原理前我们必须先知道getter和setter以及Object.defineProperty几个概念

下面将简单用几个例子理解一个getter和setter以及Object.defineProperty

先简单理解getter和setter

getter和setter

定义的时候直接设置一个getter/setter

let obj0 = {
  姓: "吴",
  名: "亦凡",
  age: 28
};

// 需求一,得到姓名

let obj1 = {
  姓: "吴",
  名: "亦凡",
  姓名() {
    return this.姓 + this.名;
  },
  age: 28
};

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

比如上面的例子,我要得到姓名,可以通过console.log("需求一:" + obj1.姓名());获得姓名,如果我要求直接obj1.姓名不加括号也可以获得姓名,怎么做?

getter

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

let obj2 = {
  姓: "吴",      //原始值
  名: "亦凡",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 28
};

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

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

setter

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

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

obj3.姓名 = '李嘉恒'        //setter用 xxx触发set函数

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

总结:setter 就是这样用的。用 = xxx 触发 set 函数

控制台出现的{n:(...)} 并不存在一个n,而是存在一个getter n和一个setter n来模拟对n的读写操作。

为什么要把n变为getter n和 setter n 呢?

我们再来理解Object.definePropery()

Object.defineProperty

定义完一个对象后(obj3),你想在它身上添加新的get,set的时候通Object.defineProperty放xxx的值

var_xxx  = 0    //先声明再用 否则会陷入死循环
                //_xxx 用来偷偷存储 n 的值

Object.defineProperty(data3, 'xxx', {         //obj3是对象,xxx是要监听的
  get(){
    return _xxx
  },
  set(value){
    _xxx = value       //value为所传的值
  }
})

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

代理和监听

代理(设计模式)

对myData对象的属性读写,会全权由另一个对象vm负责

那么vm就是myData的代理(类比房东和中介)

比如myData.n不用,偏要用vm.n来操作哦myData.n,这样用户不能直接的访问到原始数据,对数据是一个保护作用,比如你去通过房屋中介租房子,你并不能直接接触到房东。中介和房东之间就是一个全权代理的关系。

let data0 = {
  n: 0
}

// 需求一:用 Object.defineProperty 定义 n
let data1 = {}

Object.defineProperty(data1, 'n', {
  value: 0
})

console.log(`需求一:${data1.n}`)

// 需求二:n 不能小于 0
// 即 data2.n = -1 应该无效,但 data2.n = 1 有效

let data2 = {}

data2._n = 0 // _n 用来偷偷存储 n 的值

Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){
    if(value < 0) return
    this._n = value
  }
})

console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)

// 抬杠:那如果对方直接使用 data2._n 可以篡改我的代码?如何做到不让别人访问我的_n?使用代理

// 需求三:使用代理

let data3 = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问

function proxy({data}/* 解构赋值,别TM老问 */){
  const obj = {}
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  Object.defineProperty(obj, 'n', { 
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })
  return obj // obj 就是代理
}

// data3 就是 obj
console.log(`需求三:${data3.n}`)
data3.n = -1
console.log(`需求三:${data3.n},设置为 -1 失败`)
data3.n = 1
console.log(`需求三:${data3.n},设置为 1 成功`)

// 需求四

let myData = {n:0}
let data4 = proxy({ data:myData }) // 括号里是匿名对象,无法访问

// data3 就是 obj
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n},设置为 -1 失败了吗!?`)

// 我现在改 myData,还能改

监听

就算用户擅自修改 myData,也要拦截他,使用监听

// 需求五:就算用户擅自修改 myData,也要拦截他

let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问

function proxy2({data}/* 解构赋值 */){
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  let value = data.n             //通过value记录原始的值
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      if(value<0)return//这句话多余了
      data.n = value
    }
  })
  
  return obj // obj 就是代理
}

// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)


// 这代码看着眼熟吗?
// let data5 = proxy2({ data:myData5 }) 
// let vm = new Vue({data: myData})

// 现在我们可以说说 new Vue 做了什么了

Vue对data做了什么?

vm=new Vue({data:myData})

1.会让vm成为myData的代理(proxy)

2.会让myData的所有属性进行监控

监控的目的是为了防止myData的属性发生了变化 vm却不知道(知道了才能刷新页面,达到响应式,就可以调用render(data)),总之,就是你对data的任何修改我Vue必须都知道

Object.defineProperty的Bug问题

Object.defineProperty(obj,'n',{...})

必须要有一个'n', 才能监听&代理 obj.n,如果前端开发者比较水,没有给n怎么办?

Vue会给出一个警告,并且Vue只会检查第一层属性

只会检查第一层属性是什么意思呢?看下面例子

//一般没人这么写,我这样写是为了引用完整版 Vue
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; //请问,页面中会显示 1 吗?
    }
  }
}).$mount("#app");

请问上面例子中页面中会显示 1 吗?

答案是:不会

为什么:因为Vue没法监听一开始不存在的obj.b

解决办法

1.那我把key都声明好,后面就不用再加属性

2.使用Vue.set或者this.$set

Vue.set 和 this.$set

作用

新增key

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

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

举例

this.$set(this.object,'m',100)

如果data中有数组怎么办

你没办法提前声明所有key

数组长度可以一直增加,下标就是key,你没有办法提前把数组胡key都声明出来 ,Vue

也不能检测新增的下标,难道每次改数组都要用Vue.set /this.$set?

对此尤雨溪纂改了数组的API,调用会更新UI

对象中新增的key

Vue没有办法事先监听和代理

要使用set来新增 key,创建监听和代理,更新UI.

最好提前把属性都写出来,不要新增key

但数组做不到 不新增key

数组中新增key

也可以用 set 来新增key,更新UI,不过7个API更方便对数组增删,这7个API会自动处理监听和代理,并更新UI,结论:数组新增key最好通过API.

总结

Object.defineProperty

可以给对象添加属性value

可以给对象添加getter/setter

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

啥是代理(设计模式)

对myData对象的属性读写,会全权由另一个对象vm负责

那么vm就是myData的代理(类比房东和中介)

比如myData.n不用,偏要用vm.n来操作myData.n

vm=new Vue({data:myData})

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

会对myData的所有属性进行监控

为什么要监控?为了防止myData的属性变了,vm不知道

vm知道了又如何?知道属性变了就可以调用render(data)

UI=render(data)

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

Vue的Date的Bug

1.如果有多个key,需要提前声明

2.Vue.set(){} / this.$set(){}可以添加data数据

响应式原理

vm=new Vue({data:myData})

把options.data传给vue之后,data会被Vue监听,这个data会被篡改,本来的n会变成getter n,setter n

会被Vue实例代理,比如说new Vue 得到一个vm,这个vm就是data的代理,每次我们对data的读写,都会被Vue监控,Vue只要发现对它进行了修改就会更新UI

Vue目前通过Object.defineProperty来实现数据响应式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值