原理(一)"/>
vue3.0响应式原理(一)
vue 3.0响应式采用了原生的proxy,原理其实和2.0差不多,主要也是订阅收集的模式。
所有需要被代理的对象需要调用Vue的reactive方法,顾名思义就是响应式的意思,方法返回一个proxy
const original = {foo: 1}
const observed = Vue.reactive(original)
reactive方法的源码如下:
export function reactive(target: object) {// if trying to observe a readonly proxy, return the readonly version.if (readonlyToRaw.has(target)) {return target}// target is explicitly marked as readonly by userif (readonlyValues.has(target)) {return readonly(target)}return createReactiveObject(target,rawToReactive,reactiveToRaw,mutableHandlers,mutableCollectionHandlers)
}
readonlyToRaw与readonlyValues都是WeakMap对象
const readonlyValues = new WeakSet<any>()
const readonlyToRaw = new WeakMap<any, any>()
readonlyToRaw的作用的判断如果传进去的对象已经是一个经过Vue包装过的readonly proxy的话,直接返回。readonlyValues的作用是判断如果用户设置了一个对象为只读的,使用Vue.reactive返回一个只读的proxy,例如:
// readonlyToRaw
const original = {foo: 1}
const observed = Vue.readonly(original)
const observed2 = Vue.reactive(observed)
console.log(observed === observed2) // true
observed2.foo = 2
console.log(observed2.foo) // 1// readonlyValues
const original = {foo: 1}
Vue.markReadonly(original)
const observed = Vue.reactive(original)
observed.foo = 2
console.log(observed.foo) // 1
reactive方法核心在于createReactiveObject,顾名思义就是创建一个响应式的对象即proxy
function createReactiveObject(target: any,toProxy: WeakMap<any, any>,toRaw: WeakMap<any, any>,baseHandlers: ProxyHandler<any>,collectionHandlers: ProxyHandler<any>
) {if (!isObject(target)) {if (__DEV__) {console.warn(`value cannot be made reactive: ${String(target)}`)}return target}// target already has corresponding Proxylet observed = toProxy.get(target)if (observed !== void 0) {return observed}// target is already a Proxyif (toRaw.has(target)) {return target}// only a whitelist of value types can be observed.if (!canObserve(target)) {return target}const handlers = collectionTypes.has(target.constructor)? collectionHandlers: baseHandlersobserved = new Proxy(target, handlers)toProxy.set(target, observed)toRaw.set(observed, target)if (!targetMap.has(target)) {targetMap.set(target, new Map())}return observed
}
这里分别为每段代码做个解释
if (!isObject(target)) {if (__DEV__) {console.warn(`value cannot be made reactive: ${String(target)}`)}return target}
这段代码主要是判断如果传入的target不是对象,直接返回,例如:
const original = 1
const observed = Vue.reactive(1)
console.log(observed === original) // true
// target already has corresponding Proxy
let observed = toProxy.get(target)
if (observed !== void 0) {return observed
}
这段代码主要是判断如果一个对象已经被包装proxy,再次包装的话,返回之前的proxy,例如:
const original = {foo: 1}
const observed = Vue.reactive(original)
const observed2 = Vue.reactive(original)
console.log(observed === observed2) // true
// target is already a Proxy
if (toRaw.has(target)) {return target
}
这段代码主要是判断如果传入的对象是一个被包装的proxy,直接返回此对象,防止用户多次嵌套
const original = {foo: 1}
const observed = Vue.reactive(original)
const observed2 = Vue.reactive(observed)
console.log(observed === observed2) // true
// only a whitelist of value types can be observed.
if (!canObserve(target)) {return target
}
这段代码是设置一些白名单,白名单之外的target直接返回
const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
const canObserve = (value: any): boolean => {return (!value._isVue &&!value._isVNode &&observableValueRE.test(toTypeString(value)) &&!nonReactiveValues.has(value))
}
其中nonReactiveValues存储用户设置的不能被代理的对象,Vue3提供了对应的接口来设置
const original = {foo: 1}
Vue.markNonReactive(original)
const observed = Vue.reactive(original)
console.log(observed === original) // true
const handlers = collectionTypes.has(target.constructor)? collectionHandlers: baseHandlers
observed = new Proxy(target, handlers)
toProxy.set(target, observed)
toRaw.set(observed, target)
if (!targetMap.has(target)) {targetMap.set(target, new Map())
}
return observed
这段代码主要是两件事,首先判断如果传入的target是否为Set, Map, WeakMap, WeakSet对象,则使用collectionHandles proxy处理函数,否则使用baseHandlers proxy处理函数。
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
第二件事就是将对应的target与observed传入到WeakMap对象中,Vue3定了4中WeakMap,看变量名字就能发现,后两个是只读的,rawTo代表 object <-> observed ,toRaw代表 observed <-> object
const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>()
const rawToReadonly = new WeakMap<any, any>()
const readonlyToRaw = new WeakMap<any, any>()
还记得调用createReactiveObject时候一开始做的判断,即
let observed = toProxy.get(target)
if (observed !== void 0) {return observed
}
// target is already a Proxy
if (toRaw.has(target)) {return target
}
下次调用createReactiveObject时,会进行对应的判断,检查传入的target是否存是已经包装过的proxy或者已经有对应的proxy的对象了
至于最后的 targetMap.set(target, new Map())是为在后面proxy handle中进行依赖收集做准备的。
更多推荐
vue3.0响应式原理(一)
发布评论