2019-08-16 21:42:46 +08:00
|
|
|
import { track, trigger } from './effect'
|
2019-12-04 00:30:24 +08:00
|
|
|
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
2020-04-23 03:11:01 +08:00
|
|
|
import { isObject, hasChanged } from '@vue/shared'
|
|
|
|
import { reactive, isProxy, toRaw } 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
|
|
|
|
2019-11-09 02:29:43 +08:00
|
|
|
const isRefSymbol = Symbol()
|
|
|
|
|
2019-10-10 02:01:53 +08:00
|
|
|
export interface Ref<T = any> {
|
2019-11-09 02:29:43 +08:00
|
|
|
// 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
|
2020-02-26 08:44:06 +08:00
|
|
|
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
|
|
|
|
2020-02-22 00:45:42 +08:00
|
|
|
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
|
2019-11-09 02:29:43 +08:00
|
|
|
export function isRef(r: any): r is Ref {
|
|
|
|
return r ? r._isRef === true : false
|
|
|
|
}
|
|
|
|
|
2020-04-22 23:54:54 +08:00
|
|
|
export function ref<T extends object>(
|
|
|
|
value: T
|
|
|
|
): T extends Ref ? T : Ref<UnwrapRef<T>>
|
|
|
|
export function ref<T>(value: T): Ref<UnwrapRef<T>>
|
2020-03-07 00:32:39 +08:00
|
|
|
export function ref<T = any>(): Ref<T | undefined>
|
2020-01-28 02:13:38 +08:00
|
|
|
export function ref(value?: unknown) {
|
2020-02-22 11:39:32 +08:00
|
|
|
return createRef(value)
|
|
|
|
}
|
|
|
|
|
2020-02-26 09:43:01 +08:00
|
|
|
export function shallowRef<T>(value: T): T extends Ref ? T : Ref<T>
|
2020-03-07 00:32:39 +08:00
|
|
|
export function shallowRef<T = any>(): Ref<T | undefined>
|
2020-02-22 11:39:32 +08:00
|
|
|
export function shallowRef(value?: unknown) {
|
|
|
|
return createRef(value, true)
|
|
|
|
}
|
|
|
|
|
2020-04-23 03:11:01 +08:00
|
|
|
function createRef(rawValue: unknown, shallow = false) {
|
|
|
|
if (isRef(rawValue)) {
|
|
|
|
return rawValue
|
2020-02-22 11:39:32 +08:00
|
|
|
}
|
2020-04-23 03:11:01 +08:00
|
|
|
let value = shallow ? rawValue : convert(rawValue)
|
2019-10-22 23:26:48 +08:00
|
|
|
const r = {
|
2019-10-17 09:36:17 +08:00
|
|
|
_isRef: true,
|
2019-08-16 21:42:46 +08:00
|
|
|
get value() {
|
2019-12-04 00:30:24 +08:00
|
|
|
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) {
|
2020-04-23 06:00:10 +08:00
|
|
|
if (hasChanged(toRaw(newVal), rawValue)) {
|
2020-04-23 03:11:01 +08:00
|
|
|
rawValue = newVal
|
|
|
|
value = shallow ? newVal : convert(newVal)
|
|
|
|
trigger(
|
|
|
|
r,
|
|
|
|
TriggerOpTypes.SET,
|
|
|
|
'value',
|
|
|
|
__DEV__ ? { newValue: newVal } : void 0
|
|
|
|
)
|
|
|
|
}
|
2019-08-16 21:42:46 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-09 02:29:43 +08:00
|
|
|
return r
|
2019-08-20 21:38:00 +08:00
|
|
|
}
|
|
|
|
|
2020-04-23 06:00:10 +08:00
|
|
|
export function triggerRef(ref: Ref) {
|
|
|
|
trigger(
|
|
|
|
ref,
|
|
|
|
TriggerOpTypes.SET,
|
|
|
|
'value',
|
|
|
|
__DEV__ ? { newValue: ref.value } : void 0
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-02-22 11:39:32 +08:00
|
|
|
export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
|
|
|
|
return isRef(ref) ? (ref.value as any) : ref
|
|
|
|
}
|
|
|
|
|
2020-04-15 08:45:41 +08:00
|
|
|
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]> } {
|
2020-04-16 04:45:20 +08:00
|
|
|
if (__DEV__ && !isProxy(object)) {
|
2019-11-05 23:44:28 +08:00
|
|
|
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) {
|
2020-04-15 08:49:18 +08:00
|
|
|
ret[key] = toRef(object, key)
|
2019-08-20 21:38:00 +08:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2020-04-15 08:49:18 +08:00
|
|
|
export function toRef<T extends object, K extends keyof T>(
|
2019-08-20 21:38:00 +08:00
|
|
|
object: T,
|
|
|
|
key: K
|
|
|
|
): Ref<T[K]> {
|
2019-10-10 23:34:42 +08:00
|
|
|
return {
|
2019-10-17 09:36:17 +08:00
|
|
|
_isRef: true,
|
2019-10-10 23:34:42 +08:00
|
|
|
get value(): any {
|
2019-08-20 21:38:00 +08:00
|
|
|
return object[key]
|
|
|
|
},
|
|
|
|
set value(newVal) {
|
|
|
|
object[key] = newVal
|
|
|
|
}
|
2019-11-09 02:29:43 +08:00
|
|
|
} as any
|
2019-08-16 21:42:46 +08:00
|
|
|
}
|
|
|
|
|
2020-01-17 06:47:47 +08:00
|
|
|
// corner case when use narrows type
|
|
|
|
// Ex. type RelativePath = string & { __brand: unknown }
|
|
|
|
// RelativePath extends object -> true
|
2020-04-13 23:51:32 +08:00
|
|
|
type BaseTypes = string | number | boolean | Node | Window
|
2020-01-17 06:47:47 +08:00
|
|
|
|
2020-04-09 04:33:06 +08:00
|
|
|
export type UnwrapRef<T> = T extends ComputedRef<infer V>
|
|
|
|
? UnwrapRefSimple<V>
|
|
|
|
: T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>
|
|
|
|
|
2020-04-15 23:22:44 +08:00
|
|
|
type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
|
2020-04-09 04:33:06 +08:00
|
|
|
? T
|
2020-04-15 23:22:44 +08:00
|
|
|
: T extends Array<any> ? T : T extends object ? UnwrappedObject<T> : T
|
2020-04-09 04:33:06 +08:00
|
|
|
|
2020-04-12 18:25:46 +08:00
|
|
|
// Extract all known symbols from an object
|
|
|
|
// when unwrapping Object the symbols are not `in keyof`, this should cover all the
|
|
|
|
// known symbols
|
|
|
|
type SymbolExtract<T> = (T extends { [Symbol.asyncIterator]: infer V }
|
|
|
|
? { [Symbol.asyncIterator]: V }
|
|
|
|
: {}) &
|
|
|
|
(T extends { [Symbol.hasInstance]: infer V }
|
|
|
|
? { [Symbol.hasInstance]: V }
|
|
|
|
: {}) &
|
|
|
|
(T extends { [Symbol.isConcatSpreadable]: infer V }
|
|
|
|
? { [Symbol.isConcatSpreadable]: V }
|
|
|
|
: {}) &
|
|
|
|
(T extends { [Symbol.iterator]: infer V } ? { [Symbol.iterator]: V } : {}) &
|
|
|
|
(T extends { [Symbol.match]: infer V } ? { [Symbol.match]: V } : {}) &
|
|
|
|
(T extends { [Symbol.matchAll]: infer V } ? { [Symbol.matchAll]: V } : {}) &
|
|
|
|
(T extends { [Symbol.replace]: infer V } ? { [Symbol.replace]: V } : {}) &
|
|
|
|
(T extends { [Symbol.search]: infer V } ? { [Symbol.search]: V } : {}) &
|
|
|
|
(T extends { [Symbol.species]: infer V } ? { [Symbol.species]: V } : {}) &
|
|
|
|
(T extends { [Symbol.split]: infer V } ? { [Symbol.split]: V } : {}) &
|
|
|
|
(T extends { [Symbol.toPrimitive]: infer V }
|
|
|
|
? { [Symbol.toPrimitive]: V }
|
|
|
|
: {}) &
|
|
|
|
(T extends { [Symbol.toStringTag]: infer V }
|
|
|
|
? { [Symbol.toStringTag]: V }
|
|
|
|
: {}) &
|
|
|
|
(T extends { [Symbol.unscopables]: infer V }
|
|
|
|
? { [Symbol.unscopables]: V }
|
|
|
|
: {})
|
|
|
|
|
|
|
|
type UnwrappedObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T>
|