Vue数据响应式

数据响应式

主要原理:深入响应式原理
内容:深入理解options.data

Vue对data做了什么?

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);

3s后n变成10,这次没有在vm里面加,而是在外面,说明在外面也可以变更myData。

一般我们是在vm内变更myData的

new Vue({
  template:`
  <div>
    {{n}}
    <button @click="add"> +1 </button>
  </div>
  `,
  methods:{
    add(){
    //this.n +=10
      myData.n +=10
    }
  }
}).$mount("#app")

平常我们都用this.n,今天试试myData.n
分别打印出myData刚声明和3s后的结果,如果第1次是n:0,那第2次就应该是n:10,看下结果:
[图片上传失败...(image-9e0436-1648768426411)]

第2次并不是n:10,那这个n:(...)是什么呢?
我们需要先学习ES6getter、setter
示例:要想得到姓名就要调用函数obj1.姓名()所以括号不能省,但是ES6的get语法可以实现obj1.姓名删掉括号。

// 需求一,姓名不要括号也能得出值
let obj1 = {
  姓: "高",
  名: "圆圆",
  get 姓名() { //以函数的形式定义的属性
    return this.姓 + this.名; 
  }
};
console.log("需求一:" + obj1.姓名);
// 总结:getter就是不加括号的函数而已。

// 需求二:姓名可以被写
let obj2 = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.substring(1)
  }
};
obj2.姓名 = '高媛媛'
console.log(`需求二:姓 ${obj2.姓},名 ${obj2.名}`)
// 总结:setter用= xxx触发set函数

把obj2打出来
[图片上传失败...(image-945ae2-1648768426411)]

浏览器说你确实可以对姓名进行读和写,但是并不存在一个叫姓名的属性。但是你可以通过get和set设置它。

推断1: 由此推断之前得到的n也是一个getter、setter
get 姓名、set 姓名说明姓名:(...)不是一个真实的属性
推断2:n:(...)并不存在属性n,而是有个get n、set n,它们来模拟对n的读写操作。

那为什么要把n变成get n、set n呢?
需要再学下Object.defineProperty()
之前在使用getter、setter时,是在定义这个对象时直接使用的。在定义完一个对象之后,要想再添加新的get set时只能用Object.defineProperty()

Object.defineProperty(obj2,'xxx',{ 
//给obj2添加虚拟属性xxx,注意xxx是不存在的。
  var _xxx=0 //_xxx是用来存放set值的
  get(){
    return _xxx
  },
  set(value){
    _xxx=value
  }
})
let data0 = { n: 0 }
// 需求一:用Object.defineProperty定义n
let data1 = {}
Object.defineProperty(data1, 'n', { //给data1添加虚拟属性n,n=0
  value: 0
})
console.log(`需求一:${data1.n}`) //0
// 这语法把事情搞复杂了?非也,继续看。

// 需求二:n不能小于0
let data2 = {}
data2._n = 0 //用_n存储n的值
Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){ 
  //set可以添加判断:小于0直接return,否则将n值置为最新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可以直接设置为-1呀

// 需求三:使用代理obj
//括号里是匿名对象,直接改为没有名字的对象{n:0},data可有可无
let data3 = proxy({ data:{n:0} }) 
function proxy({data}){ //接收data属性
  const obj = {}
  //理论应该遍历data的所有key,这里做了简化
  Object.defineProperty(obj, 'n', { 
    get(){
      return data.n //当你取obj.n,就返回data.n
    },
    set(value){
      if(value<0)return
      data.n = value //当你设置obj.n,就设置data.n
    }
  })
  return 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 })//括号里是匿名对象,无法访问
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n},设置为 -1 失败了吗!?`)

// 需求五:就算用户擅自修改myData,也要拦截他
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) //data5就是myData5的代理对象
//监听data
function proxy2({data}){ 
  let value = data.n //拿到n,记录下来
  Object.defineProperty(data, 'n', { 
    get(){ return value },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
//代理的逻辑
  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){ return data.n },
    set(value){
      if(value<0)return
      data.n = value //通过替身value就不能直接修改data.n啦
    }
  })
  return 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 做了什么了

[图片上传失败...(image-151890-1648768426411)]

小结

4.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)

注意: 全程这个对象n:0都没有被我扔掉过,一直在改这个对象里面的东西,不是搞出了一个新对象。我是把这个对象的n给覆盖掉了,变成get n、set n,我没有把这个对象删掉,因为如果我把这个对象删掉生成新的对象,那关联就断开了,不是同一个对象了。

我全程都是在这个对象上面修改,修改后得到一个被修改的对象。然后在被修改的基础上,生成了一个新的代理。

明白2件事
1.Vue会对data后面的{n:0}进行窜改,给它加监听。
2.会新生成一个对象,这个对象会代理篡改后的对象。

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

数据响应式

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

Vue的data是响应式
const vm=new Vue({data:{n:0}})
我如果修改vm.n,那么UI中的n就会响应我
Vue 2通过Object.defineProperty来实现数据响应式
响应式网页是啥?
如果我改变窗口大小,网页内容会做出响应,那就是响应式网页。

Vue的data的bug

目前你已经知道了数据响应式,但面试不会考常态一般考变态。

Object.defineProperty的问题
Object.defineProperty(obj,'n',{...})
必须要有一个'n',才能监听 & 代理obj.n对吧
如果前端开发者比较水,没有给n怎么办
示例1. Vue会给出一个警告

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

示例2. Vue只会检查第一层属性

new Vue({
  data: {
    obj: {
      a: 0 //obj.a会被Vue监听 & 代理
    //b:undefined  
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1; //页面中不会显示1
    //Vue.set(this.obj,'b',1)
    //this.$set(this.obj,'b',1) 
    }
  }
}).$mount("#app");

解决办法
1.把k都声明好,后面不再加属性 比如b:undefined
2.使用Vue.set或者this.$set

$是为了防止重名
比如Vue.set(this.obj,'b')或者this.$set(this.obj,'b',1)

Vue.set和this.$set作用:
新增key
自动创建代理和监听(如果没有创建过)
触发UI更新(但不会立刻更新)

数组的变异方法

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

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') 增加下标的方式实现添加
    this.array.push('d')//尤雨溪的做法
    console.log(this.array)
    }
  }
}).$mount("#app");

[图片上传失败...(image-76f371-1648768426411)]

尤雨溪的做法:篡改数组的API
Vue在这个对象,你以为这个是数组对象,你传给Vue之后,Vue就会篡改这个数组。它会在中间加一层原型。这个原型有7个方法,这7个方法跟以前是同名的但是代码被尤雨溪改了,会帮你set(监听,每次push都会通知Vue)。也就是说push会做2件事情:调以前的push,调完后通知Vue添加监听和代理。
这7个API都会被Vue篡改,调用后会更新UI

总结
1.对象中新增的key
Vue无法事先监听和代理
要使用set来新增key,创建监听和代理,更新UI
最好提前把属性都写出来,不要新增key
但数组做不到「不新增key」
2.数组中新增的key
也可用set来新增key,更新UI
不过尤雨溪篡改了7个API方便你对数组进行增删
这7个API会自动处理监听和代理,并更新UI
结论:数组新增key最后通过7个API
[图片上传失败...(image-151872-1648768426411)]

面试题
说说你对Vue数据响应式的理解
Vue数据响应式,使得数据更新时得到及时的渲染。vue通过Object.defineProperty()给数据对象添加value属性,设置getter和setter监控属性的读写,并使用vm对象负责数据对象的代理,当属性更新时,调用rander()更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容

  • Vue到底对 data 做了什么? 我们有如上的代码,其中 data 中的的数据引用外面的变量 myData; 我...
    MrTon_1965阅读 234评论 0 0
  • 写在前面 我相信很多同学对Vue的数据响应式是通过Vue.js文档[https://link.zhihu.com/...
    luci_dity阅读 305评论 0 0
  • 一个物体能对外界的刺激作出反应,那他就是响应式的 const vm = new Vue({data:{n:0}})...
    Shigure_Rain阅读 249评论 0 0
  • Vue数据响应式主要研究的是 Vue 构造选项中 data 属性的特性 深入响应式 官方文档 网址: http...
    雨溪滩阅读 517评论 0 0
  • getter和setter getter 示例代码 ```let obj1 = { //以前写js函数调用姓:卢,...
    卢卢2020阅读 236评论 0 0