在之前的一篇文章Vue3源码解析--ref篇,详细阅读分析了vue3响应式ref相关的源码实现,本编文章,带领大家一起学习vue3响应式API reactive源码的实现。
相比于vue2中数据响应式实现方式的不同,Vue3使用Proxy代替在vue2中使用的Object.defineProperty的数据代理拦截操作,在响应式数据代理处理的性能方面有很大的提升。
一起来看reactive相关的api具体源码实现:
1.reactive
官方描述:返回对象的响应式副本,响应式转换是“深层”的——它影响所有嵌套 property,返回的 proxy 是不等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。
1. 1 源码定义
- 文件定义:packages\reactivity\src\reactive.ts
在了解reactive源码实现之前,我们先看文件开头定义的一些有关reactive的类型和接口的定义,理解了这些类型的定义,更有助于接下来的源码分析阅读
// reactive相关标识
export const enum ReactiveFlags {
SKIP = '__v_skip', // 跳过代理
IS_REACTIVE = '__v_isReactive', // reactive代理对象标识
IS_READONLY = '__v_isReadonly', // 只读代理标识
RAW = '__v_raw' // 代理对象的原始值标识
}
// 代理对象接口描述
export interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
[ReactiveFlags.IS_READONLY]?: boolean
[ReactiveFlags.RAW]?: any
}
// 代理对象类型
const enum TargetType {
INVALID = 0, // 无效的
COMMON = 1, // 可以代理的object,array类型
COLLECTION = 2 // 可以代理的集合类型,map set weakMap,weakSet
}
// 在此定义了4类reactive相关的map用来缓存代理对象
export const reactiveMap = new WeakMap<Target, any>()
export const shallowReactiveMap = new WeakMap<Target, any>()
export const readonlyMap = new WeakMap<Target, any>()
export const shallowReadonlyMap = new WeakMap<Target, any>()
// 工具函数,检测代理对象的类型,并区分Object,Array和Map,Set,WeakMap,WeakSet的类型
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON // 1
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION // 2
default:
return TargetType.INVALID // 0
}
}
// 工具函数-获取代理对象的类型
function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
? TargetType.INVALID
: targetTypeMap(toRawType(value))
}
复制代码
1. 2 源码实现
export function reactive(target: object) {
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
复制代码
说明:
- 首先判断传入参数target是否已经是只读代理对象,如果已经是只读代理对象,直接返回target本身
- 调用createReactiveObject()方法进行代理操作
2. shallowReactive
官方描述:创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。
- 描述解读:shallowReactive即为浅代理的意思,只会使得对象自身属性值为响应式数据,加入对象属性对应值为复杂类型数据,则不会递归处理使其复杂类型值也成为响应式数据,即只处理一层。
2.1 源码实现
export function shallowReactive<T extends object>(target: T): T {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers,
shallowReactiveMap
)
}
复制代码
说明:内部直接调用createReactiveObject()方法返回,关于createReactiveObject()方法,我们放在下面再具体深入分析
3. readonly
官方描述:接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。
3. 1 源码实现
export function readonly<T extends object>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
return createReactiveObject(
target,
true,
readonlyHandlers,
readonlyCollectionHandlers,
readonlyMap
)
}
复制代码
4. shallowReadonly
官方描述:创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)
4.1 源码实现
export function shallowReadonly<T extends object>(
target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
shallowReadonlyCollectionHandlers,
shallowReadonlyMap
)
}
复制代码
5. isReadonly
官方描述:检查对象是否是由
readonly
创建的只读代理。扫描二维码关注公众号,回复: 13428824 查看本文章
5.1 源码实现
export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}
复制代码
说明:将传入参数value类型推断为前面提到声明的Target类型,并检查是否存在__v_isReadonly属性标识
6. isReactive
官方描述:检查对象是否是由
reactive
创建的响应式代理
6.1 源码实现
export function isReactive(value: unknown): boolean {
if (isReadonly(value)) {
return isReactive((value as Target)[ReactiveFlags.RAW])
}
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}
复制代码
说明:
- 首先检查是否由readonly API创建,如果是由readonly api创建,检查readonly源对象是否为reactive
- 否则,默认返回传入参数是否含有__v_isReactive标识
7. isProxy
7.1 源码定义
export function isProxy(value: unknown): boolean {
return isReactive(value) || isReadonly(value)
}
复制代码
isProxy源码非常简单,即调用isReactive和isReadonly方法
8. toRaw
8.1 源码定义
export function toRaw<T>(observed: T): T {
return (
(observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
)
}
复制代码
说明:
- 返回传入参数的__v_raw属性值或者直接原值返回
9. markRaw
官方描述:标记一个对象,使其永远不会转换为 proxy。
9.1 源码定义
export function markRaw<T extends object>(value: T): T {
def(value, ReactiveFlags.SKIP, true)
return value
}
// def 为源码公共模块的一个工具方法
export const def = (obj: object, key: string | symbol, value: any) => {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
value
})
}
复制代码
说明:
- markRaw内部调用def方法,传入接受参数value, __v_skip,true3个参数,并返回传入参数值
- def方法通过Object.defineProperty修改传入对象的配置属性和可枚举属性
10.createReactiveObject
在前面我们看到,reactive,shallowReactive,readonly,shallowReadonly这4个API,其内部都是通过调用createReactiveObject方法实现,我们放在最后来看下这个方法的源码实现
10.1源码定义
function createReactiveObject(
target: Target,
isReadonly: boolean, // reactive false
baseHandlers: ProxyHandler<any>, // reactive mutableHandlers
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any> // reactive reactiveMap
) {
if (!isObject(target)) {
// 如果不是对象,即不是Object,Array,Map,Set,WeakMap,WeakSet类型的,就不可以代理,直接返回
if (__DEV__) {
// 开发环境下给出警告
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// 如果target已经是个proxy对象,那么就直接返回该对象
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// 如果target已经存在对应的代理对象,则找到对应的代理对象并返回
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// 获取target的类型,如果其为INVALID,说明不能进行代理,所以直接返回target
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// 根据target生成对应的proxy对象
const proxy = new Proxy(
target,
// 区分 object array和map,set,weakMap,weakSet
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
// 将代理对象缓存在对应proxyMap中
proxyMap.set(target, proxy)
return proxy
}
复制代码
说明:
- reactive,shallowReactive,readonly,shallowReadonly这4个API在调用createReactiveObject方法上,分别传入各自对应的new Proxy()构造函数get和set操作函数,分别执行各自的内部实现
- createReactiveObject方法的最后一个参数proxyMap为reactive,shallowReactive,readonly,shallowReadonly这四个分别创建的weakMap用来缓存各自代理对象。
11. reactive Proxy get handler--mutableHandlers
我们接着深入看下reactive API最核心的实现之一Proxy get handlers读取操作函数
const get = /*#__PURE__*/ createGetter()
const set = /*#__PURE__*/ createSetter()
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
// 访问 __v_isReactive
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// 访问 __v_isReadonly
return isReadonly
} else if (
// 如果访问的 key 是 __v_raw则直接返回 target
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
const targetIsArray = isArray(target)
// 如果 target 是数组并且 key 属于八个方法之一 ['includes', 'indexOf', 'lastIndexOf','push', 'pop', 'shift', 'unshift', 'splice']
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
// 如果 key 是 symbol 并且属于 symbol 的内置方法之一,或者访问的是原型对象,直接返回结果,不收集依赖。
if (
isSymbol(key)
? builtInSymbols.has(key as symbol)
: isNonTrackableKeys(key)
) {
return res
}
// 如果不是只读对象,收集依赖
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 浅层响应立即返回,不递归调用 reactive()
if (shallow) {
return res
}
// 如果是 ref 对象,则返回真正的值,即 ref.value,数组除外。
if (isRef(res)) {
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
return shouldUnwrap ? res.value : res
}
// 由于 proxy 只能代理一层,所以 target[key] 的值如果是对象,就继续对其进行代理
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
// 取旧值
let oldValue = (target as any)[key]
// 深度代理情况下,我们需要手动处理属性值为ref的情况,将trigger交给ref来触发
if (!shallow) {
value = toRaw(value)
oldValue = toRaw(oldValue)
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
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)
if (target === toRaw(receiver)) {
if (!hadKey) {
// 如果 target 没有 key,就代表是新增操作,需要触发依赖
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 更新--如果新旧值不相等,才触发依赖
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
复制代码