阅读 60

Vue数据响应式-vue对data做了什么

对应Vue文档深入响应式原理。

ES6 getter和setter

  • gettersetter就是不加括号的函数

举个栗子一,主要打印出log1

let obj1 = {     姓:“金”,     名:“泰熙”,     姓名(){       return this.姓 + this.名;     }     age:18 }; console.log("需求一:" + obj1.姓名())  // 需求一:金泰熙 复制代码

尝试去掉姓名()里面的小括号:

let obj2 = {     姓:“金”,     名:“泰熙”,     get 姓名(){   // getter 就是不加括号的函数       return this.姓 + this.名;     }     age:18 }; console.log("需求二:" + obj2.姓名)  // 需求二:金泰熙 复制代码

  • 尝试修改属性:

let obj3 = {     姓:“金”,     名:“泰熙”,     get 姓名(){   // get 就是不加括号的函数       return this.姓 + this.名;     },     set 姓名(xxx){         this.姓 = xxx[0]         this.名 = xxx.substring(1)     },     age:18 }; obj3.姓名 = “刘诗诗”  // 触发了setter函数 console.log(`需求三:姓 $(obj3.姓), 名$(obj3.名`)  // 需求三:姓 刘 名诗诗 复制代码

  • log1输出结果:

image.png

举个栗子二,主要打印出log2log3

const myData = {     n:0 } console.log(myData)  // log2 const vm = new Vue({     data:myData,     template:`       <div>{{n}}<button @click="add">+10</button></div>     `,     methods:{         add(){           this.n += 10         }     } }).$mount("#app"); setTimeout(()=>{     myData.n += 10     console.log(myData) // log3 }, 3000) 复制代码

  • log2输出结果:

image.png

  • log3输出结果:

image.png

小结:

  • 栗子一结论:输出的姓名:(...)。可以通过get 姓名set 姓名属性和属性进行读和写,但是并不存在一个叫做姓名的一个属性;

  • 栗子二结论:一开始是{n:0},传给new Vue之后立马变成{n:(...)},并不存在一个叫做n的属性,而是通过get nset n来模拟对n的某些操作.

疑问?为什么要变成get nset n呢?

Object.defineProperty

作用

  1. 可以给对象添加属性 value

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

  • 在定义完对象的属性之后,可通过Object.defineProperty额外添加新的属性,再举个栗子:

let obj3 = {     姓:“金”,     名:“泰熙”,     get 姓名(){   // get 就是不加括号的函数       return this.姓 + this.名;     },     set 姓名(xxx){         this.姓 = xxx[0]         this.名 = xxx.substring(1)     },     age:18 }; var _xxx = 0  // 存放xxx的值 // 第一个参数是要define哪个对象,第二个参数是要define的属性或方法名 Object.defineProperty(obj3, 'xxx', {     get(){       return _xxx     },     set(value){       _xxx = value     } }) obj3.姓名 = “刘诗诗”  // 触发了setter函数 console.log(`需求三:姓 $(obj3.姓), 名$(obj3.名`)  // 需求三:姓 刘 名诗诗 复制代码

代理(设计模式)和监听

  • 需求一:用 Object.defineProperty 定义 n:

let data0 = { n:0 } let data1 = {} Object.defineProperty(data2, 'n', {     get(){ return this._n }     set(value){       if(value < 0) return       this._n = value     } }) console.log(`需求一:${data1.n}`) // 需求一:0 复制代码

  • 需求二:n 不能小于0

let data2 = {} data2._n = 0 // _n 用来存储 n 的值 Object.defineProperty(data1, 'n', {value:0}) console.log(`需求二:${data2.n}`) // 需求二:0 data2.n = -1 console.log(`需求二:${data2.n} 设置 -1 失败`) // 需求二:0 设置 -1 失败 data2.n = 1 console.log(`需求二:${data2.n} 设置 1 成功`) // 需求二:1 设置 1 成功 复制代码

  • 需求三:使用代理,不让别人访问 _n(房屋中介租房代理)

// 小括号里面的是匿名对象{n:0},无法访问和修改 let data3 = proxy({ data:{n:0} })  function proxy(options){  // options 可以替换为析构赋值 {data}     const {data} = options;     const obj = {}     Object.defineProperty(obj, 'n', {       // 对 obj.n 做什么就同时对 data.n 做什么       get(){ return data.n },       set(value){         if(value<0)return         data.n = value       }     })     return obj // obj 就是代理 } // data3 就是 obj console.log(`需求三:${data3.n}`) // 需求三:0 data3.n = -1 console.log(`需求三:${data3.n} 设置 -1 失败`) // 需求三:0 设置 -1 失败 data3.n = 1 console.log(`需求三:${data3.n} 设置 1 成功`) // 需求三:1 设置 1 成功 复制代码

  • 需求四:绕过代理,定义变量myData引用 n

let myData = {n:0} let data4 = proxy({ data:myData }) console.log(`'杠精':${data4.n}`) // 杠精:0 myData.n = -1 console.log(`'杠精':${data4.n}, 设置-1 失败了吗`) // 杠精:-1, 设置-1 失败了吗 复制代码

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

let myData5 = {n:0} let data5 = proxy2({ data:myData5 }) function proxy2({data}){     // 以下为监听逻辑     let value = data.n     Object.defineProperty(data, 'n', {       // 对 obj.n 做什么就同时对 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       }     })     return obj //obj 就是代理 } console.log(`需求五:${data5.n}`) // 需求五:0 data5.n = -1 console.log(`需求五:${data5.n} 设置 -1 失败`) // 需求五:0 设置 -1 失败 data5.n = 1 console.log(`需求五:${data5.n} 设置 1 成功`) // 需求五:1 设置 1 成功 复制代码

小结:需求五

let data5 = proxy2({ data:myData5 }) // 相当于下行代码 const vm = new Vue({ data:{n:0} }) 复制代码

vue 对 data 做了什么

const vm = new Vue({ data:myData }) 复制代码

上行代码做了以下两件事:

  • 会让vm成为myData的代理 proxy,可以通过this访问到vm

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

监控的原因是为了防止myData的属性变了,vm不知道;

只有vm知道属性变了就可以调用render(data)去渲染UI

Vue的data是响应式

  • 修改vm.n,那么UI中的n就会响应

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

  • Vue 3 使用Proxy代替Object.defineProperty 来实现数据响应式

Object.defineProperty的bug:

  • 对象中新增的key,Vue没有办法事先监听和代理
    解决办法:使用Vue.setthis.$set来新增key,且创建监听和代理,更新UI

  • 数组中新增的key,对象可以提前把属性都写出来,数组做不到不新增key
    解决办法:也用Vue.setthis.$set来新增key,且创建监听和代理,更新UI;vue作者篡改了数组的7个API自动处理监听和代理。

Vue.setthis.$set

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

  • 例子:

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


作者:jianlong
链接:https://juejin.cn/post/7169219751657340958

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐