vue3.0响应式原理(一)

编程入门 行业动态 更新时间:2024-10-26 20:23:16

vue3.0响应式<a href=https://www.elefans.com/category/jswz/34/1770123.html style=原理(一)"/>

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响应式原理(一)

本文发布于:2024-03-13 23:02:22,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1735083.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:原理

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!