# vue3 源码 响应式
# p:reactivity
TIP
这个包是内嵌到vue的渲染器中(@vue/runtime-dom),但是它也可以单独发布或者被第三方引用,需要注意的是如果你是提供给第三方渲染器使用,其内部可能已经实现了响应机制,可能出现兼容问题
├── __tests__ // 单元测试目录
├── api-extractor.json
├── index.js
├── package.json
└── src
├── baseHandlers.ts // 基本类型的处理器
├── collectionHandlers.ts // Set Map WeakSet WeckMap的处理器
├── computed.ts // 计算属性,同Vue2
├── effect.ts // reactive 核心,处理依赖收集,依赖更新
├── index.ts
├── operations.ts // 定义依赖收集,依赖更新的类型
├── reactive.ts // reactive 入口,内部主要以Proxy实现
└── ref.ts // reactive 的变种方法,Proxy处理不了值类型的响应,Ref来处理
# f:reactive.ts
packages\reactivity\src\reactive.ts
- 核心,
reactive
中的实现是由proxy
加effect
组合
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// yiki: read-only?return itself : return others that had create observe
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
// #1
return createReactiveObject(
target, // 要变成响应式的对象
false, // isReadonly
mutableHandlers,// #3 // Handler
mutableCollectionHandlers, // Handler
reactiveMap // WeakMap
)
}
# createReactiveObject(...) #1
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>, // view f:baseHandlers.ts
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
...
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxys
}
# f:ref.ts
packages\reactivity\src\ref.ts
ref
跟reactive
都是响应系统的核心方法,作为整个系统的入口
- 可以将
ref
看成reactive
的一个变形版本,这是由于reactive
内部采用Proxy
来实现,而Proxy
只接受对象作为入参 - 于是有了
ref
来解决值类型的数据响应,如果传入ref
的是一个对象
,内部也会调用reactive
方法进行深层响应转换
export function ref<T extends object>(
value: T
): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
ref
接收一个可选的unknown
做为入参,接着直接调用createRef
export function ref(value?: unknown) {
// #2
return createRef(value, false)
}
# createRef(...) #2
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
} // #3
return new RefImpl(rawValue, shallow)
}
# class RefImpl #3
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
constructor(value: T, public readonly _shallow: boolean) {
this._rawValue = _shallow ? value : toRaw(value)
this._value = _shallow ? value : toReactive(value)
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
newVal = this._shallow ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = this._shallow ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
# f:baseHandlers.ts
packages\reactivity\src\baseHandlers.ts
- 包含了四种
handler
mutableHandlers
可变处理readonlyHandlers
只读处理shallowReactiveHandlers
浅观察处理(只观察目标对象的第一层属性)shallowReadonlyHandlers
浅观察 && 只读处理
- 其中
readonlyHandlers
shallowReactiveHandlers
shallowReadonlyHandlers
都是mutableHandlers
的变形版本
# mutableHandlers #3
export const mutableHandlers: ProxyHandler<object> = {
get, // #7
set, // #8
deleteProperty, // #4
has, // #5
ownKeys // #6
}
# deleteProperty(...) #4
function deleteProperty(target: object, key: string | symbol): boolean {
const hadKey = hasOwn(target, key)
const oldValue = (target as any)[key]
const result = Reflect.deleteProperty(target, key)
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
# has(...) #5
function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key)
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, TrackOpTypes.HAS, key)
}
return result
}
# ownKeys(...) #6
function ownKeys(target: object): (string | symbol)[] {
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}
# get(...) #7
const get = /*#__PURE__*/ createGetter()
/**
* @description: 用于拦截对象的读取属性操作
* @param {isReadonly} 是否只读
* @param {shallow} 是否浅观察
*/
function createGetter(isReadonly = false, shallow = false) {
/**
* @description:
* @param {target} 目标对象
* @param {key} 需要获取的值的键值
* @param {receiver} 如果遇到 setter,receiver则为setter调用时的this值
*/
return function get(target: object, key: string | symbol, receiver: object) {
// ReactiveFlags 是在reactive中声明的枚举值,如果key是枚举值则直接返回对应的布尔值
if (key === ReactiveFlags.isReactive) {
return !isReadonly
} else if (key === ReactiveFlags.isReadonly) {
return isReadonly
} else if (key === ReactiveFlags.raw) { // 如果key是raw 则直接返回目标对象
return target
}
const targetIsArray = isArray(target)
// 如果目标对象是数组并且 key 属于三个方法之一 ['includes', 'indexOf', 'lastIndexOf'],即触发了这三个操作之一
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
// 如果 key 是 symbol 内置方法,或者访问的是原型对象,直接返回结果,不收集依赖
if (isSymbol(key) && builtInSymbols.has(key) || key === '__proto__') {
return res
}
// 如果是浅观察并且不为只读则调用 track Get, 并返回结果
if (shallow) {
!isReadonly && track(target, TrackOpTypes.GET, key)
return res
}
// 如果get的结果是ref
if (isRef(res)) {
// 目标对象为数组并且不为只读调用 track Get, 并返回结果
if (targetIsArray) {
!isReadonly && track(target, TrackOpTypes.GET, key)
return res
} else {
// ref unwrapping, only for Objects, not for Arrays.
return res.value
}
}
// 目标对象不为只读则调用 track Get
!isReadonly && track(target, TrackOpTypes.GET, key)
// 由于 proxy 只能代理一层,所以 target[key] 的值如果是对象,就继续对其进行代理
return isObject(res)
? isReadonly
? // need to lazy access readonly and reactive here to avoid
// circular dependency
readonly(res)
: reactive(res)
: res
}
}
const arrayInstrumentations: Record<string, Function> = {};
['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
arrayInstrumentations[key] = function(...args: any[]): any {
const arr = toRaw(this) as any
for (let i = 0, l = (this as any).length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
// we run the method using the original args first (which may be reactive)
const res = arr[key](...args)
if (res === -1 || res === false) {
// if that didn't work, run it again using raw values.
return arr[key](...args.map(toRaw))
} else {
return res
}
}
})
# set(...) #8
const set = /*#__PURE__*/ createSetter()
/**
* @description: 拦截对象的设置属性操作
* @param {shallow} 是否是浅观察
*/
function createSetter(shallow = false) {
/**
* @description:
* @param {target} 目标对象
* @param {key} 设置的属性的名称
* @param {value} 要改变的属性值
* @param {receiver} 如果遇到 setter,receiver则为setter调用时的this值
*/
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
const oldValue = (target as any)[key]
// 如果模式不是浅观察
if (!shallow) {
value = toRaw(value)
// 并且目标对象不是数组,旧值是ref,新值不是ref,则直接赋值,注意这里提到ref,这里不展开讲,后面详细讲
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 = hasOwn(target, key)
// 赋值
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
if (target === toRaw(receiver)) {
if (!hadKey) {
// 如是不存在则trigger ADD
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 存在则trigger SET
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
# f:computed.ts
packages\reactivity\src\computed.ts
计算属性
,可能会依赖
其他reactive
的值,同时会延迟和缓存计算值
export function computed<T>(
getter: ComputedGetter<T>,
debugOptions?: DebuggerOptions
): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
const onlyGetter = isFunction(getterOrOptions)
if (onlyGetter) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter)
if (__DEV__ && debugOptions) {
cRef.effect.onTrack = debugOptions.onTrack
cRef.effect.onTrigger = debugOptions.onTrigger
}
return cRef as any
}