2019-08-16 13:42:46 +00:00
|
|
|
import { track, trigger } from './effect'
|
2019-12-03 16:30:24 +00:00
|
|
|
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
2019-08-16 13:42:46 +00:00
|
|
|
import { isObject } from '@vue/shared'
|
2019-11-05 15:44:28 +00:00
|
|
|
import { reactive, isReactive } from './reactive'
|
2019-10-14 15:21:09 +00:00
|
|
|
import { ComputedRef } from './computed'
|
2019-10-22 15:26:48 +00:00
|
|
|
import { CollectionTypes } from './collectionHandlers'
|
2019-08-16 13:42:46 +00:00
|
|
|
|
2019-11-08 18:29:43 +00:00
|
|
|
const isRefSymbol = Symbol()
|
|
|
|
|
2019-10-09 18:01:53 +00:00
|
|
|
export interface Ref<T = any> {
|
2019-11-08 18:29:43 +00: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
|
2019-10-10 15:34:42 +00:00
|
|
|
value: UnwrapRef<T>
|
2019-08-16 13:42:46 +00:00
|
|
|
}
|
|
|
|
|
2019-10-22 15:26:48 +00:00
|
|
|
const convert = <T extends unknown>(val: T): T =>
|
|
|
|
isObject(val) ? reactive(val) : val
|
2019-08-16 13:42:46 +00:00
|
|
|
|
2020-01-06 21:15:49 +00:00
|
|
|
export function isRef<T>(r: Ref<T> | T): r is Ref<T>
|
2019-11-08 18:29:43 +00:00
|
|
|
export function isRef(r: any): r is Ref {
|
|
|
|
return r ? r._isRef === true : false
|
|
|
|
}
|
|
|
|
|
2020-01-27 18:13:38 +00:00
|
|
|
export function ref<T extends Ref>(value: T): T
|
|
|
|
export function ref<T>(value: T): Ref<T>
|
2019-11-02 02:54:01 +00:00
|
|
|
export function ref<T = any>(): Ref<T>
|
2020-01-27 18:13:38 +00:00
|
|
|
export function ref(value?: unknown) {
|
|
|
|
if (isRef(value)) {
|
|
|
|
return value
|
2019-10-10 15:34:42 +00:00
|
|
|
}
|
2020-01-27 18:13:38 +00:00
|
|
|
value = convert(value)
|
2020-01-24 16:39:52 +00:00
|
|
|
|
|
|
|
if (__SSR__) {
|
|
|
|
return {
|
|
|
|
_isRef: true,
|
2020-01-27 18:13:38 +00:00
|
|
|
value
|
2020-01-24 16:39:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-22 15:26:48 +00:00
|
|
|
const r = {
|
2019-10-17 01:36:17 +00:00
|
|
|
_isRef: true,
|
2019-08-16 13:42:46 +00:00
|
|
|
get value() {
|
2019-12-03 16:30:24 +00:00
|
|
|
track(r, TrackOpTypes.GET, 'value')
|
2020-01-27 18:13:38 +00:00
|
|
|
return value
|
2019-08-16 13:42:46 +00:00
|
|
|
},
|
|
|
|
set value(newVal) {
|
2020-01-27 18:13:38 +00:00
|
|
|
value = convert(newVal)
|
2019-11-03 03:20:49 +00:00
|
|
|
trigger(
|
|
|
|
r,
|
2019-12-03 16:30:24 +00:00
|
|
|
TriggerOpTypes.SET,
|
2019-11-03 03:20:49 +00:00
|
|
|
'value',
|
|
|
|
__DEV__ ? { newValue: newVal } : void 0
|
|
|
|
)
|
2019-08-16 13:42:46 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-08 18:29:43 +00:00
|
|
|
return r
|
2019-08-20 13:38:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function toRefs<T extends object>(
|
|
|
|
object: T
|
|
|
|
): { [K in keyof T]: Ref<T[K]> } {
|
2020-01-24 16:39:52 +00:00
|
|
|
if (__DEV__ && !__SSR__ && !isReactive(object)) {
|
2019-11-05 15:44:28 +00:00
|
|
|
console.warn(`toRefs() expects a reactive object but received a plain one.`)
|
|
|
|
}
|
2019-08-20 13:38:00 +00: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]> {
|
2019-10-10 15:34:42 +00:00
|
|
|
return {
|
2019-10-17 01:36:17 +00:00
|
|
|
_isRef: true,
|
2019-10-10 15:34:42 +00:00
|
|
|
get value(): any {
|
2019-08-20 13:38:00 +00:00
|
|
|
return object[key]
|
|
|
|
},
|
|
|
|
set value(newVal) {
|
|
|
|
object[key] = newVal
|
|
|
|
}
|
2019-11-08 18:29:43 +00:00
|
|
|
} as any
|
2019-08-16 13:42:46 +00:00
|
|
|
}
|
|
|
|
|
2019-11-08 17:52:24 +00:00
|
|
|
type UnwrapArray<T> = { [P in keyof T]: UnwrapRef<T[P]> }
|
|
|
|
|
2020-01-16 22:47:47 +00:00
|
|
|
// corner case when use narrows type
|
|
|
|
// Ex. type RelativePath = string & { __brand: unknown }
|
|
|
|
// RelativePath extends object -> true
|
|
|
|
type BaseTypes = string | number | boolean
|
|
|
|
|
2019-08-16 13:42:46 +00:00
|
|
|
// Recursively unwraps nested value bindings.
|
2019-10-07 00:11:52 +00:00
|
|
|
export type UnwrapRef<T> = {
|
2019-10-14 15:02:49 +00:00
|
|
|
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
|
2019-10-07 00:11:52 +00:00
|
|
|
ref: T extends Ref<infer V> ? UnwrapRef<V> : T
|
2019-11-08 17:52:24 +00:00
|
|
|
array: T extends Array<infer V> ? Array<UnwrapRef<V>> & UnwrapArray<T> : T
|
2019-10-07 00:11:52 +00:00
|
|
|
object: { [K in keyof T]: UnwrapRef<T[K]> }
|
2019-10-14 15:02:49 +00:00
|
|
|
}[T extends ComputedRef<any>
|
|
|
|
? 'cRef'
|
2020-01-20 18:22:18 +00: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']
|