vue3.0响应式原理(一)

6 篇文章 0 订阅
3 篇文章 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 user
  if (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 Proxy
  let observed = toProxy.get(target)
  if (observed !== void 0) {
    return observed
  }
  // target is already a Proxy
  if (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
    : 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
}

这里分别为每段代码做个解释


  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中进行依赖收集做准备的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值