实现原理:
对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
存在问题:
const person = {
name: 'ds',
age: 18,
};
// vue2
let p = {};
Object.defineProperty(p, 'name', {
get() {
console.log('有人读取了name');
return person.name;
},
set(value) {
console.log('有人修改了name');
person.name = value;
},
});
实现原理:
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
MDN文档中描述的Proxy与Reflect:
Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
//源数据
const person = {
name: 'ds',
age: 18,
};
//代理数据
//target就是目标对象person,propName就是操作的属性
const p = new Proxy(person, {
//有人读取p的某个属性时调用
get(target, propName) {
console.log(`有人读取了p的${propName}`);
console.log(target, propName);
return target[propName];
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value) {
console.log(`有人修改了p身上的${propName}`);
target[propName] = value;
},
//有人删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}`);
//这个函数需要一个结果来判断成功与否所以return
return delete target[propName];
},
});

Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
//源数据
let person = {
name:'张三',
age:18
}
//模拟Vue3中实现响应式
const p = new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`)
return Reflect.get(target,propName)
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
return Reflect.deleteProperty(target,propName)
}
})
通过Reflect操作的属性,报错时会返回false,这样就不要try-catch捕获异常了。
reactive转为代理对象。Object.defineProperty()的get与set来实现响应式(数据劫持)。.value,读取数据时模板中直接读取不需要.value。.value。1.ref通常用于声明基础类型响应式数据。
import {ref} from 'vue'
const age =ref(10) //声明响应式数据
2.ref返回的是被包装过的响应式对象,在setup中访问和修改ref需要使用.value属性
age.value=21
3.在模板中使用时无需使用.value,直接使用即可
<div>{{age}}div>
4.当ref数据作为props传递给子组件的时候,在子组件里需要使用toRef或者toRefs建立引用,否则数据不是响应式的。且需要注意,如果在子组件中直接操作了这个引用之后,则和父组件不在具有联系。
1.reactive用于声明复杂类型响应式数据。
import {reactive} from 'vue'
const man=ref({name:'jolin',age:21}) //声明响应式数据
2.reactive返回的是被包装过的响应式对象,在setup中访问和修改直接使用属性即可
man.age=20
3.声明时未定义,动态添加的属性也会是响应式的
man.weight = '50kg' //weight具有响应性
4.在模板中使用属性的形式
<div>{{man.name}}div>
5.将reactive声明的响应式数据传递给子组件时,在子组件可以直接使用。
6.当ref的值是数组时,我们可以通过索引来修改数组值是响应式的。
1.注意当ref用于在模板中作为真值判断时,直接使用ref恒为true, 需要使用.value才能正确显示
<div v-if="age">div> //恒为true
<div v-if="age.value">div> //正确
2.注意不能解构reactive数据,解构出的数据会失去响应式。3.在任何地方访问响应式数据都能拿到最新的。
4.同vue2的data,只有数据被应用到模板中时,数据的改变才会触发updated生命周期,否则即使数据被修改了,也不会触发updated生命周期,导致视图不更新。
5.同vue2,当将ref和reactive作为props传递给组件时,原则上不应该在子组件上修改props的值。
this.$attrs。props:[‘xxx’]接收,attr这个对象就能看到父组件传来的数据this.$slots。this.$emit。emits:['hello'], 这里要写父组件的自定义函数名称,不然会有警告App.vue
尚硅谷
尚硅谷
Demo.vue
一个人的信息
姓名:{{person.name}}
年龄:{{person.age}}