2021-07-08 02:13:23 +08:00
|
|
|
import { isTracking, trackEffects, triggerEffects } from './effect'
|
2019-12-04 00:30:24 +08:00
|
|
|
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
2022-01-16 15:46:49 +08:00
|
|
|
import { isArray, hasChanged, IfAny } from '@vue/shared'
|
2021-10-10 06:06:12 +08:00
|
|
|
import { isProxy, toRaw, isReactive, toReactive } from './reactive'
|
|
|
|
import type { ShallowReactiveMarker } from './reactive'
|
2019-10-22 23:26:48 +08:00
|
|
|
import { CollectionTypes } from './collectionHandlers'
|
2021-07-08 02:37:28 +08:00
|
|
|
import { createDep, Dep } from './dep'
|
2019-08-16 21:42:46 +08:00
|
|
|
|
2021-09-02 04:49:12 +08:00
|
|
|
declare const RefSymbol: unique symbol
|
2020-05-04 20:51:01 +08:00
|
|
|
|
2019-10-10 02:01:53 +08:00
|
|
|
export interface Ref<T = any> {
|
2020-10-07 06:16:20 +08:00
|
|
|
value: T
|
2020-05-03 04:16:51 +08:00
|
|
|
/**
|
2020-05-04 20:51:01 +08:00
|
|
|
* Type differentiator only.
|
|
|
|
* We need this to be in public d.ts but don't want it to show up in IDE
|
|
|
|
* autocomplete, so we use a private Symbol instead.
|
2020-05-03 04:16:51 +08:00
|
|
|
*/
|
2020-05-04 20:51:01 +08:00
|
|
|
[RefSymbol]: true
|
2021-06-24 05:22:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type RefBase<T> = {
|
2021-07-08 02:13:23 +08:00
|
|
|
dep?: Dep
|
2021-06-24 05:22:21 +08:00
|
|
|
value: T
|
|
|
|
}
|
|
|
|
|
|
|
|
export function trackRefValue(ref: RefBase<any>) {
|
|
|
|
if (isTracking()) {
|
|
|
|
ref = toRaw(ref)
|
|
|
|
if (!ref.dep) {
|
2021-07-08 02:13:23 +08:00
|
|
|
ref.dep = createDep()
|
2021-06-24 05:22:21 +08:00
|
|
|
}
|
2021-06-24 05:20:38 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
trackEffects(ref.dep, {
|
|
|
|
target: ref,
|
|
|
|
type: TrackOpTypes.GET,
|
|
|
|
key: 'value'
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
trackEffects(ref.dep)
|
|
|
|
}
|
2021-06-24 05:22:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
|
|
|
|
ref = toRaw(ref)
|
|
|
|
if (ref.dep) {
|
2021-06-24 05:20:38 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
triggerEffects(ref.dep, {
|
|
|
|
target: ref,
|
|
|
|
type: TriggerOpTypes.SET,
|
|
|
|
key: 'value',
|
|
|
|
newValue: newVal
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
triggerEffects(ref.dep)
|
|
|
|
}
|
2021-06-24 05:22:21 +08:00
|
|
|
}
|
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 {
|
2022-01-28 18:35:09 +08:00
|
|
|
return !!(r && r.__v_isRef === true)
|
2019-11-09 02:29:43 +08:00
|
|
|
}
|
|
|
|
|
2021-10-08 23:57:49 +08:00
|
|
|
export function ref<T extends object>(
|
|
|
|
value: T
|
|
|
|
): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
|
2020-04-22 23:54:54 +08:00
|
|
|
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) {
|
2021-08-24 06:32:58 +08:00
|
|
|
return createRef(value, false)
|
2020-02-22 11:39:32 +08:00
|
|
|
}
|
|
|
|
|
2021-10-10 05:51:09 +08:00
|
|
|
declare const ShallowRefMarker: unique symbol
|
|
|
|
|
2021-12-06 12:18:21 +08:00
|
|
|
export type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
|
2021-10-10 05:51:09 +08:00
|
|
|
|
2020-08-19 11:34:29 +08:00
|
|
|
export function shallowRef<T extends object>(
|
|
|
|
value: T
|
2021-10-10 05:51:09 +08:00
|
|
|
): T extends Ref ? T : ShallowRef<T>
|
|
|
|
export function shallowRef<T>(value: T): ShallowRef<T>
|
|
|
|
export function shallowRef<T = any>(): ShallowRef<T | undefined>
|
2020-02-22 11:39:32 +08:00
|
|
|
export function shallowRef(value?: unknown) {
|
|
|
|
return createRef(value, true)
|
|
|
|
}
|
|
|
|
|
2021-09-22 22:11:54 +08:00
|
|
|
function createRef(rawValue: unknown, shallow: boolean) {
|
|
|
|
if (isRef(rawValue)) {
|
|
|
|
return rawValue
|
|
|
|
}
|
|
|
|
return new RefImpl(rawValue, shallow)
|
|
|
|
}
|
|
|
|
|
2020-08-22 01:47:41 +08:00
|
|
|
class RefImpl<T> {
|
|
|
|
private _value: T
|
2021-06-24 05:22:21 +08:00
|
|
|
private _rawValue: T
|
2020-08-22 01:47:41 +08:00
|
|
|
|
2021-07-08 02:13:23 +08:00
|
|
|
public dep?: Dep = undefined
|
2020-08-22 01:47:41 +08:00
|
|
|
public readonly __v_isRef = true
|
|
|
|
|
2022-01-18 09:17:22 +08:00
|
|
|
constructor(value: T, public readonly __v_isShallow: boolean) {
|
|
|
|
this._rawValue = __v_isShallow ? value : toRaw(value)
|
|
|
|
this._value = __v_isShallow ? value : toReactive(value)
|
2020-08-22 01:47:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
get value() {
|
2021-06-24 05:22:21 +08:00
|
|
|
trackRefValue(this)
|
2020-08-22 01:47:41 +08:00
|
|
|
return this._value
|
|
|
|
}
|
|
|
|
|
|
|
|
set value(newVal) {
|
2022-01-18 09:17:22 +08:00
|
|
|
newVal = this.__v_isShallow ? newVal : toRaw(newVal)
|
2021-07-16 04:28:01 +08:00
|
|
|
if (hasChanged(newVal, this._rawValue)) {
|
2020-08-22 01:47:41 +08:00
|
|
|
this._rawValue = newVal
|
2022-01-18 09:17:22 +08:00
|
|
|
this._value = this.__v_isShallow ? newVal : toReactive(newVal)
|
2021-06-24 05:22:21 +08:00
|
|
|
triggerRefValue(this, newVal)
|
2020-08-22 01:47:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-23 06:00:10 +08:00
|
|
|
export function triggerRef(ref: Ref) {
|
2021-06-24 05:22:21 +08:00
|
|
|
triggerRefValue(ref, __DEV__ ? ref.value : void 0)
|
2020-04-23 06:00:10 +08:00
|
|
|
}
|
|
|
|
|
2021-07-02 03:20:49 +08:00
|
|
|
export function unref<T>(ref: T | Ref<T>): T {
|
2020-02-22 11:39:32 +08:00
|
|
|
return isRef(ref) ? (ref.value as any) : ref
|
|
|
|
}
|
|
|
|
|
2020-07-29 04:30:56 +08:00
|
|
|
const shallowUnwrapHandlers: ProxyHandler<any> = {
|
|
|
|
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
|
|
|
|
set: (target, key, value, receiver) => {
|
|
|
|
const oldValue = target[key]
|
|
|
|
if (isRef(oldValue) && !isRef(value)) {
|
|
|
|
oldValue.value = value
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return Reflect.set(target, key, value, receiver)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function proxyRefs<T extends object>(
|
|
|
|
objectWithRefs: T
|
|
|
|
): ShallowUnwrapRef<T> {
|
|
|
|
return isReactive(objectWithRefs)
|
|
|
|
? objectWithRefs
|
|
|
|
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
|
|
|
|
}
|
|
|
|
|
2021-12-11 18:15:44 +08:00
|
|
|
export type CustomRefFactory<T> = (
|
2020-04-15 08:45:41 +08:00
|
|
|
track: () => void,
|
|
|
|
trigger: () => void
|
|
|
|
) => {
|
|
|
|
get: () => T
|
|
|
|
set: (value: T) => void
|
|
|
|
}
|
|
|
|
|
2020-08-22 01:47:41 +08:00
|
|
|
class CustomRefImpl<T> {
|
2021-07-08 02:13:23 +08:00
|
|
|
public dep?: Dep = undefined
|
2021-06-24 05:22:21 +08:00
|
|
|
|
2020-08-22 01:47:41 +08:00
|
|
|
private readonly _get: ReturnType<CustomRefFactory<T>>['get']
|
|
|
|
private readonly _set: ReturnType<CustomRefFactory<T>>['set']
|
|
|
|
|
|
|
|
public readonly __v_isRef = true
|
|
|
|
|
|
|
|
constructor(factory: CustomRefFactory<T>) {
|
|
|
|
const { get, set } = factory(
|
2021-06-24 05:22:21 +08:00
|
|
|
() => trackRefValue(this),
|
|
|
|
() => triggerRefValue(this)
|
2020-08-22 01:47:41 +08:00
|
|
|
)
|
|
|
|
this._get = get
|
|
|
|
this._set = set
|
|
|
|
}
|
|
|
|
|
|
|
|
get value() {
|
|
|
|
return this._get()
|
|
|
|
}
|
|
|
|
|
|
|
|
set value(newVal) {
|
|
|
|
this._set(newVal)
|
2020-04-15 08:45:41 +08:00
|
|
|
}
|
2020-08-22 01:47:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
|
|
|
|
return new CustomRefImpl(factory) as any
|
2020-04-15 08:45:41 +08:00
|
|
|
}
|
|
|
|
|
2021-09-02 04:49:12 +08:00
|
|
|
export type ToRefs<T = any> = {
|
2021-11-25 17:39:09 +08:00
|
|
|
[K in keyof T]: ToRef<T[K]>
|
2021-09-02 04:49:12 +08:00
|
|
|
}
|
2020-04-25 01:10:16 +08:00
|
|
|
export function toRefs<T extends object>(object: T): ToRefs<T> {
|
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.`)
|
|
|
|
}
|
2020-08-19 00:11:13 +08:00
|
|
|
const ret: any = isArray(object) ? new Array(object.length) : {}
|
2019-08-20 21:38:00 +08:00
|
|
|
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-08-22 01:47:41 +08:00
|
|
|
class ObjectRefImpl<T extends object, K extends keyof T> {
|
|
|
|
public readonly __v_isRef = true
|
|
|
|
|
2021-12-11 16:41:58 +08:00
|
|
|
constructor(
|
|
|
|
private readonly _object: T,
|
|
|
|
private readonly _key: K,
|
|
|
|
private readonly _defaultValue?: T[K]
|
|
|
|
) {}
|
2020-08-22 01:47:41 +08:00
|
|
|
|
|
|
|
get value() {
|
2021-12-11 16:41:58 +08:00
|
|
|
const val = this._object[this._key]
|
|
|
|
return val === undefined ? (this._defaultValue as T[K]) : val
|
2020-08-22 01:47:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
set value(newVal) {
|
|
|
|
this._object[this._key] = newVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-16 15:46:49 +08:00
|
|
|
export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
|
2021-10-10 05:51:09 +08:00
|
|
|
|
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
|
2021-12-11 16:41:58 +08:00
|
|
|
): ToRef<T[K]>
|
|
|
|
|
|
|
|
export function toRef<T extends object, K extends keyof T>(
|
|
|
|
object: T,
|
|
|
|
key: K,
|
|
|
|
defaultValue: T[K]
|
|
|
|
): ToRef<Exclude<T[K], undefined>>
|
|
|
|
|
|
|
|
export function toRef<T extends object, K extends keyof T>(
|
|
|
|
object: T,
|
|
|
|
key: K,
|
|
|
|
defaultValue?: T[K]
|
2020-10-28 06:50:34 +08:00
|
|
|
): ToRef<T[K]> {
|
2021-08-18 00:11:52 +08:00
|
|
|
const val = object[key]
|
2021-12-11 16:41:58 +08:00
|
|
|
return isRef(val)
|
|
|
|
? val
|
|
|
|
: (new ObjectRefImpl(object, key, defaultValue) 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-05-02 04:14:30 +08:00
|
|
|
type BaseTypes = string | number | boolean
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a special exported interface for other packages to declare
|
|
|
|
* additional types that should bail out for ref unwrapping. For example
|
|
|
|
* \@vue/runtime-dom can declare it like so in its d.ts:
|
|
|
|
*
|
|
|
|
* ``` ts
|
|
|
|
* declare module '@vue/reactivity' {
|
|
|
|
* export interface RefUnwrapBailTypes {
|
|
|
|
* runtimeDOMBailTypes: Node | Window
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
2020-07-08 18:32:42 +08:00
|
|
|
* Note that api-extractor somehow refuses to include `declare module`
|
2020-05-02 04:14:30 +08:00
|
|
|
* augmentations in its generated d.ts, so we have to manually append them
|
|
|
|
* to the final generated d.ts in our build process.
|
|
|
|
*/
|
|
|
|
export interface RefUnwrapBailTypes {}
|
2020-01-17 06:47:47 +08:00
|
|
|
|
2020-07-29 04:30:56 +08:00
|
|
|
export type ShallowUnwrapRef<T> = {
|
2021-07-16 03:58:20 +08:00
|
|
|
[K in keyof T]: T[K] extends Ref<infer V>
|
|
|
|
? V
|
2021-09-05 04:42:39 +08:00
|
|
|
: // if `V` is `unknown` that means it does not extend `Ref` and is undefined
|
|
|
|
T[K] extends Ref<infer V> | undefined
|
2021-07-20 06:24:18 +08:00
|
|
|
? unknown extends V
|
|
|
|
? undefined
|
|
|
|
: V | undefined
|
|
|
|
: T[K]
|
2020-07-29 04:30:56 +08:00
|
|
|
}
|
|
|
|
|
2021-10-10 05:51:09 +08:00
|
|
|
export type UnwrapRef<T> = T extends ShallowRef<infer V>
|
|
|
|
? V
|
|
|
|
: T extends Ref<infer V>
|
2020-04-09 04:33:06 +08:00
|
|
|
? UnwrapRefSimple<V>
|
2020-06-18 04:06:55 +08:00
|
|
|
: UnwrapRefSimple<T>
|
2020-04-09 04:33:06 +08:00
|
|
|
|
2021-07-28 23:15:08 +08:00
|
|
|
export type UnwrapRefSimple<T> = T extends
|
2020-05-02 04:14:30 +08:00
|
|
|
| Function
|
|
|
|
| CollectionTypes
|
|
|
|
| BaseTypes
|
|
|
|
| Ref
|
|
|
|
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
|
2020-04-09 04:33:06 +08:00
|
|
|
? T
|
2020-06-30 00:26:28 +08:00
|
|
|
: T extends Array<any>
|
2021-11-26 10:54:12 +08:00
|
|
|
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
|
2021-10-10 05:51:09 +08:00
|
|
|
: T extends object & { [ShallowReactiveMarker]?: never }
|
2021-07-28 23:12:46 +08:00
|
|
|
? {
|
|
|
|
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
|
|
|
|
}
|
2021-07-20 06:24:18 +08:00
|
|
|
: T
|