Vue数据响应式-vue对data做了什么
对应Vue文档深入响应式原理。博客内容来自jirengu.com
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)
转载自:https://juejin.cn/post/7169219751657340958