1.Proxy和Reflect
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
代理是什么:目标对象的抽象,即允许我们拦截并重新定义对一个对象的基本操作
怎么创建:使用Proxy构造函数创建
目的:定义捕获器(trap)
捕获器(trap)
什么是捕获器:在处理程序对象中——基本操作的拦截器;
在操作系统中——程序流中的一个同步中断
const target = {
id: "target",
}; //目标对象
const handler = {};//是一组trap(get(){},set(){})
const proxy = new Proxy(target, handler); //代理对象
console.log(target.id === proxy.id); //true
console.log(target === proxy); //false
//目标对象、代理对象共享属性
target.id = "foo";
console.log("原对象:", target.id, " 代理对象:", proxy.id);
proxy.id = "bar";
console.log("原对象:", target.id, " 代理对象:", proxy.id);
console.log("原型:", Proxy.prototype); //undefined
原型详解 js 原型链,继承详解
const target = {
id: "target",
}; //目标对象
const handler = {
get(trapTarget, property, receiver) {
// console.log(trapTarget === target); //true
// console.log(receiver === proxy); //true
return trapTarget[property];
}, //目标对象,要查询的属性,代理对象
};
const proxy = new Proxy(target, handler); //代理对象
console.log(target.id, proxy.id); //target target
处理程序中所有可捕获的方法都有对应的反射(Reflect)API方法
const target = {
id: "target",
};
const handler = {
// get() {
// return Reflect.get(...arguments);
// },
get: Reflect.get,
};
const proxy = new Proxy(target, handler);
console.log(target.id, proxy.id); //target target
const target = {
id: "target",
};
const proxy = new Proxy(target, Reflect);
console.log(target.id, proxy.id); //target target
console.log(Reflect.get(target, "id")); //target
2.Object.defineProperty
Object.defineProperty()
允许精确地添加或修改对象上的属性。
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 37,
enumerable: true,
configurable: true,
writable: true,
});
// 默认情况下,它们不可枚举、不可配置、不可写
console.log(object1.property1);
Vue3为什么用Proxy代替Object.defineProperty
Vue2.js中的响应式数据是基于 Object.defineProperty()
实现的
Vue3.js中的响应式数据是基于Proxy实现的
相同点:
- 都可以用来修改对象属性的行为。
- 都可以用来实现数据劫持,提供get、set方法。
- 都可以用来实现对象的代理。
不同点:
-
Object.defineProperty()
只能监听对象属性的读取和写入操作,而Proxy可以监听对象属性的更多操作,比如in运算符、delete运算符等。 -
Object.defineProperty()
无法检测到以下变化: property 的添加或移除;利用索引直接设置一个数组项;修改数组的长度。而Proxy可以监听。 -
var vm = new Vue({ data:{ a:1 } }) // `vm.a` 是响应式的 vm.b = 2// `vm.b` 是非响应式的 //响应式的 Vue.set(vm.someObject, 'b', 2) this.$set(this.someObject,'b',2)
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的 //响应性的 // Vue.set Vue.set(vm.items, indexOfItem, newValue) vm.$set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) vm.items.splice(newLength)
-
Object.defineProperty()
只能监听单个属性的变化,而Proxy可以监听整个对象的变化。 -
Object.defineProperty()
需要知道要监听的属性名,而Proxy可以通过Reflect对象拦截所有属性操作。 -
Object.defineProperty()
不支持撤销监听,而Proxy可以使用Proxy.revocable()方法创建一个可撤销的代理对象。
Vue2中,后添加的数据不是响应式的,由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data
对象上存在才能让 Vue 将它转换为响应式的。解决方法:
(1)对于数组来说,只有pop、push、shift、unshit、sort、splice、reverse等改变原数组的方法能被监听到(Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。)
Vue.set( target, propertyName/index, value )(2)Vue.set( target, propertyName/index, value )
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi'
)
注意对象不能是 Vue 实例(vm),或者 Vue 实例的根数据对象(vm._data)。
3.ref 和 reactive ——原始值与非原始值的响应方案
ref
是通过一个中间对象RefImpl
持有数据,并通过重写它的set和get方法实现数据劫持的,本质上依旧是通过Object.defineProperty()
对RefImpl
的value
属性进行劫持。
reactive
则是通过Proxy进行劫持的。Proxy无法对基本数据类型进行操作,进而导致reactive
在面对基本数据类型时的束手无策。
Vue 3 还提供了 toRefs
函数,它可以将一个 reactive
对象的所有属性转换为 ref
对象,并返回一个响应式对象,其中每个属性都是一个 ref
。这在需要将 reactive
对象的属性传递给其他组件或函数时特别有用,因为它允许你保持响应式并避免手动解包 .value
。
import { ref,reactive } from 'vue';
const msg =ref('hello')
const msg1 =ref({key:'hello'})
const msg2 =reactive({key:'hello'})
console.log(msg)
console.log(msg1)
console.log(msg2)
ref
可以存储原始类型(Number、Boolean、String),若存储对象类型,是对reactive的封装。ref
需要通过.value
访问数据。- 可以重新分配一个全新的对象给
ref
的value
属性,而reactive()
不能。 ref()
返回的是一个持有原始数据的RefImpl
实例,而reactive()
返回的类型则是原始数据的代理Proxy
实例。watch
默认只观察ref
的value
,而对reactive
则执行深度监听。ref
默认会用reactive
对象类型的原始值进行深层响应转换。
4.v-bind和v-model
- v-bind指令单向绑定,数据影响视图。v-bind:title="message"简写 :title="message"
- v-model指令双向绑定,只能用于表单元素(如input、select、textarea等,输入元素,有value值)。v-model:value="name" 简写 v-model="name"