vue3-core/packages/reactivity/src/ref.ts

144 lines
3.8 KiB
TypeScript
Raw Normal View History

2019-08-16 21:42:46 +08:00
import { track, trigger } from './effect'
import { TrackOpTypes, TriggerOpTypes } from './operations'
2019-08-16 21:42:46 +08:00
import { isObject } from '@vue/shared'
import { reactive, isReactive } from './reactive'
2019-10-14 23:21:09 +08:00
import { ComputedRef } from './computed'
2019-10-22 23:26:48 +08:00
import { CollectionTypes } from './collectionHandlers'
2019-08-16 21:42:46 +08:00
const isRefSymbol = Symbol()
2019-10-10 02:01:53 +08:00
export interface Ref<T = any> {
// This field is necessary to allow TS to differentiate a Ref from a plain
// object that happens to have a "value" field.
// However, checking a symbol on an arbitrary object is much slower than
// checking a plain property, so we use a _isRef plain property for isRef()
// check in the actual implementation.
// The reason for not just declaring _isRef in the interface is because we
// don't want this internal field to leak into userland autocompletion -
// a private symbol, on the other hand, achieves just that.
[isRefSymbol]: true
value: T
2019-08-16 21:42:46 +08:00
}
2019-10-22 23:26:48 +08:00
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val
2019-08-16 21:42:46 +08:00
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
export function isRef(r: any): r is Ref {
return r ? r._isRef === true : false
}
export function ref<T>(value: T): T extends Ref ? T : Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
2020-01-28 02:13:38 +08:00
export function ref(value?: unknown) {
return createRef(value)
}
export function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>
export function shallowRef<T = any>(): Ref<T | undefined>
export function shallowRef(value?: unknown) {
return createRef(value, true)
}
function createRef(value: unknown, shallow = false) {
2020-01-28 02:13:38 +08:00
if (isRef(value)) {
return value
}
if (!shallow) {
value = convert(value)
}
2019-10-22 23:26:48 +08:00
const r = {
_isRef: true,
2019-08-16 21:42:46 +08:00
get value() {
track(r, TrackOpTypes.GET, 'value')
2020-01-28 02:13:38 +08:00
return value
2019-08-16 21:42:46 +08:00
},
set value(newVal) {
value = shallow ? newVal : convert(newVal)
trigger(
r,
TriggerOpTypes.SET,
'value',
__DEV__ ? { newValue: newVal } : void 0
)
2019-08-16 21:42:46 +08:00
}
}
return r
2019-08-20 21:38:00 +08:00
}
export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
return isRef(ref) ? (ref.value as any) : ref
}
export type CustomRefFactory<T> = (
track: () => void,
trigger: () => void
) => {
get: () => T
set: (value: T) => void
}
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
const { get, set } = factory(
() => track(r, TrackOpTypes.GET, 'value'),
() => trigger(r, TriggerOpTypes.SET, 'value')
)
const r = {
_isRef: true,
get value() {
return get()
},
set value(v) {
set(v)
}
}
return r as any
}
2019-08-20 21:38:00 +08:00
export function toRefs<T extends object>(
object: T
): { [K in keyof T]: Ref<T[K]> } {
if (__DEV__ && !isReactive(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
2019-08-20 21:38:00 +08:00
const ret: any = {}
for (const key in object) {
ret[key] = toProxyRef(object, key)
}
return ret
}
function toProxyRef<T extends object, K extends keyof T>(
object: T,
key: K
): Ref<T[K]> {
return {
_isRef: true,
get value(): any {
2019-08-20 21:38:00 +08:00
return object[key]
},
set value(newVal) {
object[key] = newVal
}
} as any
2019-08-16 21:42:46 +08:00
}
// corner case when use narrows type
// Ex. type RelativePath = string & { __brand: unknown }
// RelativePath extends object -> true
type BaseTypes = string | number | boolean | Node | Window
2019-08-16 21:42:46 +08:00
// Recursively unwraps nested value bindings.
export type UnwrapRef<T> = {
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
ref: T extends Ref<infer V> ? UnwrapRef<V> : T
array: T
object: { [K in keyof T]: UnwrapRef<T[K]> }
}[T extends ComputedRef<any>
? 'cRef'
2020-01-21 02:22:18 +08:00
: T extends Array<any>
? 'array'
: T extends Ref | Function | CollectionTypes | BaseTypes
? 'ref' // bail out on types that shouldn't be unwrapped
: T extends object ? 'object' : 'ref']