Vue3.0中的响应式原理
vue2.x的响应式
1.实现原理:
-
- 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
- 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
get () {}, //对象有数据属性,和可隐藏的访问器属性
set () {} //访问器属性就是get(){}和set(){}
})
Object.defineProperty()
语法:Object.defineProperty(obj, prop, descriptor)
obj | prop | descriptor |
---|---|---|
要定义属性的对象。 | 要定义或修改的属性的名称或 Symbol 。 | 要定义或修改的属性描述符。 |
示例:
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false //属性值是否可以修改
});
object1.property1 =77;
// object1.property1值不变
console.log(object1.property1);
// expected output: 42
2.存在问题:
-
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
<template>
<div>
<div>{{person.name}} </div>
<div>{{person.age}} </div>
<div v-if="person.sex">{{person.sex}} </div>
<button @click="change()">增加性别</button>
<button @click="del()">删除年龄</button>
</div>
</template>
<script>
export default {
data(){
return {
person:{name:"gcshi",age:"8"}
}
},
methods:{
change(){
this.person.sex = "女";
console.log(this.person)
},
del(){
delete this.person.age
console.log(this.person)
}
}
}
</script>
如图,再点击【增加性别】和【删除年龄】时,数据均发生了改变,但页面没有发生变化
2.解决方法:
使用 Vue.set 或 this.$set
语法: Vue.set( target, key, value ) / this.$set( target, key, value )
target | key | value |
---|---|---|
要更改的数据源(可以是对象或者数组) | 要更改的具体数据 | 重新赋的值 |
change(){
//this.person.sex = "女";
this.$set(this.person,"sex","女")
console.log(this.person)
},
//或
import Vue from "vue"
change(){
//this.person.sex = "女";
Vue.set(this.person,"sex","女")
console.log(this.person)
},
删除方法名为delete,其他用法 一致。如
change(){
//this.person.sex = "女";
this.$delete(this.person,"sex","女")
console.log(this.person)
},
Vue3.0的响应式
- 实现原理:
-
- 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作。
- MDN文档中描述的Proxy与Reflect:
- Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
- Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'