reactive 是 vue3 中对数据进行劫持的核心,主要是利用了 Proxy 进行劫持。
Proxy
本质上是对某个对象的劫持,这样它不仅仅可以监听对象某个属性值的变化,还可以监听对象属性的新增和删除;而 Object.defineProperty
是给对象的某个已存在的属性添加对应的 getter
和 setter
,所以它只能监听这个属性值的变化,而不能去监听对象属性的新增和删除。
reactive.ts文件
官方地址:https://github.com/vuejs/core
export function reactive(target: object) {
// 如果是只读的,则不进行劫持
if (isReadonly(target)) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
其中参数中的方法展示如下:
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: /*#__PURE__*/ createInstrumentationGetter(false, false)
}
export const reactiveMap = new WeakMap<Target, any>()
其中mytableHandlers对象定义了get、set、deleteProperty、has、ownkeys方法。
reactive只是暴露给外部的一个api,真正处理的核心在createReactiveObject这个方法。如果不是对象类型直接返回。target 已经是个 proxy ,而且不是要求这个proxy 是已读的,但这个 proxy 是个响应式的,则直接返回这个 target。target已经是proxy对象,直接返回。target是不可观察的(__v_skip或者不可扩展对象),直接返回。只有Object、Array、Map、Set、WeakMap、WeakSet才可被监听。baseHandlers 是对进行基本类型的劫持,即 [Object,Array] ,collectionHandlers 是对集合类型的劫持, 即 [Set, Map, WeakMap, WeakSet]。
// 根据target 生成proxy实例
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only specific value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxy的第二个参数,是根据targetType判断的,如果是Object、Array返回baseHandlers,否则返回collectionHandlers。其中baseHandlers中包含了get、set、deleteProperty、has、ownkeys方法。这些方法的封装,都利用了Reflect(反射)的api。
什么是反射呢?
Reflect
是一个内置对象,它提供可拦截 JavaScript 操作的方法。这些方法与proxy代理的方法相同。Reflect
不是一个函数对象,所以它是不可构造的。
- Reflect上的操作能返回操作成功与否的反馈
- 能够确保this的指向正确,从而总能在Reflect上获取对象的原始行为