Vue数据响应式-vue对data做了什么
对应Vue文档深入响应式原理。
ES6 getter和setter
getter
和setter
就是不加括号的函数
举个栗子一,主要打印出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输出结果:
举个栗子二,主要打印出log2
和log3
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输出结果:
log3输出结果:
小结:
栗子一结论:输出的
姓名:(...)
。可以通过get 姓名
和set 姓名
对姓
属性和名
属性进行读和写,但是并不存在一个叫做姓名
的一个属性;栗子二结论:一开始是
{n:0}
,传给new Vue
之后立马变成{n:(...)}
,并不存在一个叫做n
的属性,而是通过get n
和set n
来模拟对n
的某些操作.
疑问?为什么要变成get n
和set n
呢?
Object.defineProperty
作用
可以给对象添加属性 value
可以给对象添加 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.set
或this.$set
来新增key,且创建监听和代理,更新UI数组中新增的key,对象可以提前把属性都写出来,数组做不到不新增key
解决办法:也用Vue.set
或this.$set
来新增key,且创建监听和代理,更新UI;vue作者篡改了数组的7个API自动处理监听和代理。
Vue.set
或this.$set
作用:新增key、自动创建代理和监听(如果没有创建过)、触发UI更新(但不会立刻更新)
例子:
this.$set(this.object, 'm', 100)
作者:jianlong
链接:https://juejin.cn/post/7169219751657340958