vue3响应式原理- Proxy和Reflect
Proxy
就是代理的意思,为对象创建一个代理,从而实现对对象基本操作的拦截,在目标对象访问之前有一层"拦截",外界访问这个对象都要通过这层拦截,从而实现对外界的访问进行过滤或者改写
js
const test = new Proxy(target, handler)
- target 需要拦截的目标对象
- handler 处理这个拦截的具体行为操作
js
const obj = {
name: "Jack",
age: 18
}
const handler = {
get(target, key, receiver){
console.log('receiver',receiver)
return target[key]
},
set(target, key, value, receiver){
console.log('receiver',receiver)
target[key] = value
},
deleteProperty(target, key){
return delete target[key]
}
}
const newObj = new Proxy(obj, handler)
console.log(newObj.age) // 18
newObj.age = 20
console.log(delete newObj.age)
target
: 是目标对象key
: 目标属性名称value
: 目标属性的值receiver
:指向的是当前操作 正确的上下文。如果目标属性是一个getter
访问器属性,则receiver
就是本次读取属性所指向的this
对象。通常,receiver
这就是newObj
对象本身,但是如果我们从newObj
继承,则receiver
指的是从该newObj
继承的对象
Reflect
反射的意思,就是将代理的内容反射出去,与Proxy一样,也是ES6为了操作对象提供的新的API. 提供拦截JavaScript操作的方法,与Proxy的handler提供的方法一一对应,只要是proxy对象的方法,就能在reflect对象上找到,而且reflect不是一个函数对象,所有的属性和方法都是静态的。不能进行实例化。
js
const obj = {
name: "Jack",
age: 18
}
const handler = {
get(target, key, receiver){
console.log('recevier',receiver)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver){
console.log('recevier',receiver)
return Reflect.set(target,key,value,receiver)
},
deleteProperty(target, key){
return Reflect.deleteProperty(target, key)
}
}
const newObj = new Proxy(obj, handler)
console.log(newObj.age) // 18
newObj.age = 20
console.log(delete newObj.age)
Reflect.get()
代替target[key]
操作Reflect.set()
代替target[key] = value
操作Reflect.deleteProperty()
代替delete target[key]
操作 当然除了上面的方法还有一些常用的Reflect
方法:
js
Reflect.construct(target, args)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
vue3响应式源码解读- reactive,ref
reactive源码
打开源文件,找到文件packages/reactivity/src/reactive.ts
查看源码
ts
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (isReadonly(target)) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap,
)
}
- 对target进行响应式只读判断,如果true直接返回target,createReactiveObject()方法是reactive实现的核心
ts
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
}
createReactiveObject()有五个参数
- target: 传入的原始目标对象
- isReadonly: 是否只读
- baseHandlers: 为普通对象创建proxy时的第二个参数handler
- collectionHandlers: 为collection类型对象创建proxy时的第二个参数handler
- proxyMap: WeakMap类型的map,用于存储target与他的proxy之间的对应关系
-
源码可以看出他将对象分为COMMON、COLLECTION目的是为了根据不同的对象类型进行不同的handler
-
首先先进行了一系列的判断
- 先判断target是否为对象,不是直接返回
- 判断target是否是响应式对象,是则return
- 判断是否已经为target创建过proxy,是则返回
- 判断是否是COMMON/COLLECTION类型的对象,不是则返回
- 如果是以上类型对象,则为target创建proxy,并返回这个proxy
-
在来就是根据不同类型的对象,进行不同的逻辑处理
-
主要关注这个baseHandlers 位置:
packages/reactivity/src/baseHandlers.ts
ts
export const mutableHandlers: ProxyHandler<object> = new MutableReactiveHandler()
// 返回一个对象 { get, set, deleteProperty, has, ownKeys }
get和依赖收集
- mutableHandlers里头返回的对象中,都是各种钩子函数,当对proxy对象进行访问和修改时,会调用相应的函数进行处理。看看get里如何对target进行收集 源码解读:
ts
get(target: Target, key: string | symbol, receiver: object) {
const isReadonly = this._isReadonly,
shallow = this._shallow
if (key === ReactiveFlags.IS_REACTIVE) {
// 如果`key`值为`__v_isReactive`
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// key值为 `__v_isReadonly`
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
} else if (key === ReactiveFlags.RAW) {
if (
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target) ||
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
) {
// 如果`key==='__v_raw'`并且`WeakMap`中`key`为`target`的值不为空,则返回`target`
return target
}
// early return undefined
return
}
const targetIsArray = isArray(target)
if (!isReadonly) {
// 如果target是数组,则重写/增强数组对应的方法; 在这些方法里头调用track()进行依赖收集
// 数组查找方法: includes,indexOf,lastIndexOf
// 修改原数组的方法:push,pop,unshift,shift,splice
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
const res = Reflect.get(target, key, receiver)
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
if (!isReadonly) {
// 如果是普通对象且非只读,则调用track进行依赖收集
track(target, TrackOpTypes.GET, key)
}
if (shallow) {
// 如果是浅层响应式对象,直接返回
return res
}
if (isRef(res)) {
// 如果是ref对象,返回其value值
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value
}
if (isObject(res)) {
// 如果是对象类型且只读,则调用readonly(),否则递归调用reactive()
return isReadonly ? readonly(res) : reactive(res)
}
// 如果都不满足,则返回对应的属性值
return res
}
}
track解读
ts
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!isTracking()) {
// 首先进行是否正在进行依赖收集的判断处理
return
}
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep()))
}
const eventInfo = __DEV__
? { effect: activeEffect, target, type, key }
: undefined
trackEffects(dep, eventInfo)
}
const targetMap = new WeakMap<any, KeyToDepMap>()
创建一个targetMap
容器,用于保存和当前响应式对象相关的依赖内容,本身是一个WeakMap
类型- targetMap键是target,值是一个depsMap【Map实例】,存储的就是和当前响应式对象的每个key对应的具体依赖
- depsMap键是响应式对象的key,值是一个deps
set和依赖更新
ts
set(
target: object,
key: string | symbol,
value: unknown,
receiver: object,
): boolean {
let oldValue = (target as any)[key]
if (!this._shallow) {
const isOldValueReadonly = isReadonly(oldValue)
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(value)
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
if (isOldValueReadonly) {
return false
} else {
oldValue.value = value
return true
}
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
}
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
- 旧值先保存到oldValue
- 如果不是浅层响应,target是普通对象,并且旧值是响应式对象,则赋值oldValue.value = value
- 判断是否存在key值
- Reflect.set设置对应的属性值
- 判断对象是原型链上的内容,不触发依赖更新
- 目标对象不存在对应的key 调用trigger依赖更新
ref()源码
packages/reactivity/src/ref.ts
ts
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
constructor(
value: T,
public readonly __v_isShallow: boolean,
) {
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, DirtyLevels.Dirty, newVal)
}
}
}
- 实现
ref
的核心就是实例化了一个RefImpl
对象。 RefImpl
对象解读:- _ value:用于
保存ref当前值
,如果参数是对象,则会保存reactive函数转化后的值 - _ rawValue:用于保存当前ref值对应
原始值
,如果参数是对象,它就保存转化前的原始值。toRaw()
是将的响应式对象转为普通对象 - dep:用来存储当前的
ref
值收集的依赖。Set避免重复 - _v_isRef :被
ref
定义的都会标识当前数据为一个Ref
RefImpl类
暴露给实例对象的get、set
方法是value对象,所以外部访问要加上.value读取- 如果传入的值是
对象
类型,会调用convert()
方法,这个方法里面会调用reactive()
方法对其进行响应式处理
- _ value:用于
RefImpl
实例关键就在于trackRefValue(this)
依赖收集和triggerRefValue(this, newVal)
依赖更新的两个函数的处理,原理基本与reactive
处理方式类似