首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Vue3.0 源码解读-响应式系统之reactive

reactive 是 vue3 中对数据进行劫持的核心,主要是利用了 Proxy 进行劫持,相比于 Object.defineproperty 能够劫持的类型和范围都更好,再也不用像 vue2 中那样对数组进行类似 hack 方式的劫持了。

下面快速看看 vue3 是怎么劫持。首先看看这个对象是是不是 __v_isReadonly 只读的,这个枚举在后面进行讲述,如果是,直接返回,否者调用 createReactiveObject 进行创建。

export function reactive(target: object) {

// if trying to observe a readonly proxy, return the readonly version.

if (target && (target as Target).__v_isReadonly) {

return target

}

return createReactiveObject(

target,

false,

mutableHandlers,

mutableCollectionHandlers

)

}

createReactiveObject 中,有个四个参数,target 就是我们需要传入的对象,isReadonly 表示要创建的代理是不是只可读的,baseHandlers 是对进行基本类型的劫持,即 [Object,Array] ,collectionHandlers 是对集合类型的劫持, 即 [Set, Map, WeakMap, WeakSet]。

function createReactiveObject(

target: Target,

isReadonly: boolean,

baseHandlers: ProxyHandler,

collectionHandlers: ProxyHandler

) {

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.__v_raw && !(isReadonly && target.__v_isReactive)) {

return target

}

// target already has corresponding Proxy

if (

hasOwn(target, isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive)

) {

return isReadonly ? target.__v_readonly : target.__v_reactive

}

// only a whitelist of value types can be observed.

if (!canObserve(target)) {

return target

}

const observed = new Proxy(

target,

collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers

)

def(

target,

isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive,

observed

)

return observed

}

如果我们传入是 target 不是object,直接返回。而如果 target 已经是个 proxy ,而且不是要求这个proxy 是已读的,但这个 proxy 是个响应式的,则直接返回这个 target。什么意思呢?我们创建的 proxy 有两种类型,一种是响应式的,另外一种是只读的。

而如果我们传入的 target 上面有挂载了响应式的 proxy,则直接返回上面挂载的 proxy 。

如果上面都不满足,则需要检查一下我们传进去的 target 是否可以进行劫持观察,如果 target 上面挂载了 __v_skip 属性 为 true 或者 不是我们再在上面讲参数时候讲的六种类型,或者 对象被freeze 了,还是不能进行劫持。

const canObserve = (value: Target): boolean => {

return (

!value.__v_skip &&

isObservableType(toRawType(value)) &&

!Object.isFrozen(value)

)

}

如果上面条件满足,则进行劫持,可以看到我们会根据 target 类型的不同进行不同的 handler,最后根据把 observed 挂载到原对象上,同时返回 observed。

const observed = new Proxy(

target,

collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers

)

def(

target,

isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive,

observed

)

return observed

现在继续讲讲上面 ReactiveFlags 枚举,skip 用于标记对象不可以进行代理,可以用于 创建 component 的时候,把options 进行 markRaw,isReactive 和 isReadonly 都是由 proxy 劫持返回值,表示 proxy 的属性,raw 是 proxy 上面的 原始target ,reactive 和 readonly 是挂载在 target 上面的 proxy

export const enum ReactiveFlags {

skip = '__v_skip',

isReactive = '__v_isReactive',

isReadonly = '__v_isReadonly',

raw = '__v_raw',

reactive = '__v_reactive',

readonly = '__v_readonly'

}

再讲讲可以创建的四种 proxy, 分别是reactive、 shallowReactive 、readonly 和 shallowReadonly。其实从字面意思就可以看出他们的区别了。具体细节会在 collectionHandlers 和 baseHandlers 进行讲解。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200531A0EVZE00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券