| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- import type { ComputedRef } from './computed'
- import {
- activeEffect,
- shouldTrack,
- trackEffect,
- triggerEffects,
- } from './effect'
- import { DirtyLevels, TrackOpTypes, TriggerOpTypes } from './constants'
- import {
- type IfAny,
- hasChanged,
- isArray,
- isFunction,
- isObject,
- } from '@vue/shared'
- import {
- isProxy,
- isReactive,
- isReadonly,
- isShallow,
- toRaw,
- toReactive,
- } from './reactive'
- import type { Builtin, ShallowReactiveMarker } from './reactive'
- import { type Dep, createDep } from './dep'
- import { ComputedRefImpl } from './computed'
- import { getDepFromReactive } from './reactiveEffect'
- import { warn } from './warning'
- declare const RefSymbol: unique symbol
- export declare const RawSymbol: unique symbol
- export interface Ref<T = any> {
- value: T
- /**
- * 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.
- */
- [RefSymbol]: true
- }
- type RefBase<T> = {
- dep?: Dep
- value: T
- }
- export function trackRefValue(ref: RefBase<any>) {
- if (shouldTrack && activeEffect) {
- ref = toRaw(ref)
- trackEffect(
- activeEffect,
- (ref.dep ??= createDep(
- () => (ref.dep = undefined),
- ref instanceof ComputedRefImpl ? ref : undefined,
- )),
- __DEV__
- ? {
- target: ref,
- type: TrackOpTypes.GET,
- key: 'value',
- }
- : void 0,
- )
- }
- }
- export function triggerRefValue(
- ref: RefBase<any>,
- dirtyLevel: DirtyLevels = DirtyLevels.Dirty,
- newVal?: any,
- oldVal?: any,
- ) {
- ref = toRaw(ref)
- const dep = ref.dep
- if (dep) {
- triggerEffects(
- dep,
- dirtyLevel,
- __DEV__
- ? {
- target: ref,
- type: TriggerOpTypes.SET,
- key: 'value',
- newValue: newVal,
- oldValue: oldVal,
- }
- : void 0,
- )
- }
- }
- /**
- * Checks if a value is a ref object.
- *
- * @param r - The value to inspect.
- * @see {@link https://vuejs.org/api/reactivity-utilities.html#isref}
- */
- export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
- export function isRef(r: any): r is Ref {
- return !!(r && r.__v_isRef === true)
- }
- /**
- * Takes an inner value and returns a reactive and mutable ref object, which
- * has a single property `.value` that points to the inner value.
- *
- * @param value - The object to wrap in the ref.
- * @see {@link https://vuejs.org/api/reactivity-core.html#ref}
- */
- export function ref<T>(value: T): Ref<UnwrapRef<T>>
- export function ref<T = any>(): Ref<T | undefined>
- export function ref(value?: unknown) {
- return createRef(value, false)
- }
- declare const ShallowRefMarker: unique symbol
- export type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
- /**
- * Shallow version of {@link ref()}.
- *
- * @example
- * ```js
- * const state = shallowRef({ count: 1 })
- *
- * // does NOT trigger change
- * state.value.count = 2
- *
- * // does trigger change
- * state.value = { count: 2 }
- * ```
- *
- * @param value - The "inner value" for the shallow ref.
- * @see {@link https://vuejs.org/api/reactivity-advanced.html#shallowref}
- */
- export function shallowRef<T>(
- value: T,
- ): Ref extends T
- ? T extends Ref
- ? IfAny<T, ShallowRef<T>, T>
- : ShallowRef<T>
- : ShallowRef<T>
- export function shallowRef<T = any>(): ShallowRef<T | undefined>
- export function shallowRef(value?: unknown) {
- return createRef(value, true)
- }
- function createRef(rawValue: unknown, shallow: boolean) {
- if (isRef(rawValue)) {
- return rawValue
- }
- return new RefImpl(rawValue, shallow)
- }
- class RefImpl<T> {
- private _value: T
- private _rawValue: T
- public dep?: Dep = undefined
- public readonly __v_isRef = true
- constructor(
- value: T,
- public readonly __v_isShallow: boolean,
- ) {
- this._rawValue = __v_isShallow ? value : toRaw(value)
- this._value = __v_isShallow ? value : toReactive(value)
- }
- get value() {
- trackRefValue(this)
- return this._value
- }
- set value(newVal) {
- const useDirectValue =
- this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
- newVal = useDirectValue ? newVal : toRaw(newVal)
- if (hasChanged(newVal, this._rawValue)) {
- const oldVal = this._rawValue
- this._rawValue = newVal
- this._value = useDirectValue ? newVal : toReactive(newVal)
- triggerRefValue(this, DirtyLevels.Dirty, newVal, oldVal)
- }
- }
- }
- /**
- * Force trigger effects that depends on a shallow ref. This is typically used
- * after making deep mutations to the inner value of a shallow ref.
- *
- * @example
- * ```js
- * const shallow = shallowRef({
- * greet: 'Hello, world'
- * })
- *
- * // Logs "Hello, world" once for the first run-through
- * watchEffect(() => {
- * console.log(shallow.value.greet)
- * })
- *
- * // This won't trigger the effect because the ref is shallow
- * shallow.value.greet = 'Hello, universe'
- *
- * // Logs "Hello, universe"
- * triggerRef(shallow)
- * ```
- *
- * @param ref - The ref whose tied effects shall be executed.
- * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref}
- */
- export function triggerRef(ref: Ref) {
- triggerRefValue(ref, DirtyLevels.Dirty, __DEV__ ? ref.value : void 0)
- }
- export type MaybeRef<T = any> = T | Ref<T>
- export type MaybeRefOrGetter<T = any> = MaybeRef<T> | (() => T)
- /**
- * Returns the inner value if the argument is a ref, otherwise return the
- * argument itself. This is a sugar function for
- * `val = isRef(val) ? val.value : val`.
- *
- * @example
- * ```js
- * function useFoo(x: number | Ref<number>) {
- * const unwrapped = unref(x)
- * // unwrapped is guaranteed to be number now
- * }
- * ```
- *
- * @param ref - Ref or plain value to be converted into the plain value.
- * @see {@link https://vuejs.org/api/reactivity-utilities.html#unref}
- */
- export function unref<T>(ref: MaybeRef<T> | ComputedRef<T> | ShallowRef<T>): T {
- return isRef(ref) ? ref.value : ref
- }
- /**
- * Normalizes values / refs / getters to values.
- * This is similar to {@link unref()}, except that it also normalizes getters.
- * If the argument is a getter, it will be invoked and its return value will
- * be returned.
- *
- * @example
- * ```js
- * toValue(1) // 1
- * toValue(ref(1)) // 1
- * toValue(() => 1) // 1
- * ```
- *
- * @param source - A getter, an existing ref, or a non-function value.
- * @see {@link https://vuejs.org/api/reactivity-utilities.html#tovalue}
- */
- export function toValue<T>(
- source: MaybeRefOrGetter<T> | ComputedRef<T> | ShallowRef<T>,
- ): T {
- return isFunction(source) ? source() : unref(source)
- }
- 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)
- }
- },
- }
- /**
- * Returns a reactive proxy for the given object.
- *
- * If the object already is reactive, it's returned as-is. If not, a new
- * reactive proxy is created. Direct child properties that are refs are properly
- * handled, as well.
- *
- * @param objectWithRefs - Either an already-reactive object or a simple object
- * that contains refs.
- */
- export function proxyRefs<T extends object>(
- objectWithRefs: T,
- ): ShallowUnwrapRef<T> {
- return isReactive(objectWithRefs)
- ? objectWithRefs
- : new Proxy(objectWithRefs, shallowUnwrapHandlers)
- }
- export type CustomRefFactory<T> = (
- track: () => void,
- trigger: () => void,
- ) => {
- get: () => T
- set: (value: T) => void
- }
- class CustomRefImpl<T> {
- public dep?: Dep = undefined
- 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(
- () => trackRefValue(this),
- () => triggerRefValue(this),
- )
- this._get = get
- this._set = set
- }
- get value() {
- return this._get()
- }
- set value(newVal) {
- this._set(newVal)
- }
- }
- /**
- * Creates a customized ref with explicit control over its dependency tracking
- * and updates triggering.
- *
- * @param factory - The function that receives the `track` and `trigger` callbacks.
- * @see {@link https://vuejs.org/api/reactivity-advanced.html#customref}
- */
- export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
- return new CustomRefImpl(factory) as any
- }
- export type ToRefs<T = any> = {
- [K in keyof T]: ToRef<T[K]>
- }
- /**
- * Converts a reactive object to a plain object where each property of the
- * resulting object is a ref pointing to the corresponding property of the
- * original object. Each individual ref is created using {@link toRef()}.
- *
- * @param object - Reactive object to be made into an object of linked refs.
- * @see {@link https://vuejs.org/api/reactivity-utilities.html#torefs}
- */
- export function toRefs<T extends object>(object: T): ToRefs<T> {
- if (__DEV__ && !isProxy(object)) {
- warn(`toRefs() expects a reactive object but received a plain one.`)
- }
- const ret: any = isArray(object) ? new Array(object.length) : {}
- for (const key in object) {
- ret[key] = propertyToRef(object, key)
- }
- return ret
- }
- class ObjectRefImpl<T extends object, K extends keyof T> {
- public readonly __v_isRef = true
- constructor(
- private readonly _object: T,
- private readonly _key: K,
- private readonly _defaultValue?: T[K],
- ) {}
- get value() {
- const val = this._object[this._key]
- return val === undefined ? this._defaultValue! : val
- }
- set value(newVal) {
- this._object[this._key] = newVal
- }
- get dep(): Dep | undefined {
- return getDepFromReactive(toRaw(this._object), this._key)
- }
- }
- class GetterRefImpl<T> {
- public readonly __v_isRef = true
- public readonly __v_isReadonly = true
- constructor(private readonly _getter: () => T) {}
- get value() {
- return this._getter()
- }
- }
- export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
- /**
- * Used to normalize values / refs / getters into refs.
- *
- * @example
- * ```js
- * // returns existing refs as-is
- * toRef(existingRef)
- *
- * // creates a ref that calls the getter on .value access
- * toRef(() => props.foo)
- *
- * // creates normal refs from non-function values
- * // equivalent to ref(1)
- * toRef(1)
- * ```
- *
- * Can also be used to create a ref for a property on a source reactive object.
- * The created ref is synced with its source property: mutating the source
- * property will update the ref, and vice-versa.
- *
- * @example
- * ```js
- * const state = reactive({
- * foo: 1,
- * bar: 2
- * })
- *
- * const fooRef = toRef(state, 'foo')
- *
- * // mutating the ref updates the original
- * fooRef.value++
- * console.log(state.foo) // 2
- *
- * // mutating the original also updates the ref
- * state.foo++
- * console.log(fooRef.value) // 3
- * ```
- *
- * @param source - A getter, an existing ref, a non-function value, or a
- * reactive object to create a property ref from.
- * @param [key] - (optional) Name of the property in the reactive object.
- * @see {@link https://vuejs.org/api/reactivity-utilities.html#toref}
- */
- export function toRef<T>(
- value: T,
- ): T extends () => infer R
- ? Readonly<Ref<R>>
- : T extends Ref
- ? T
- : Ref<UnwrapRef<T>>
- export function toRef<T extends object, K extends keyof T>(
- object: T,
- key: K,
- ): 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(
- source: Record<string, any> | MaybeRef,
- key?: string,
- defaultValue?: unknown,
- ): Ref {
- if (isRef(source)) {
- return source
- } else if (isFunction(source)) {
- return new GetterRefImpl(source) as any
- } else if (isObject(source) && arguments.length > 1) {
- return propertyToRef(source, key!, defaultValue)
- } else {
- return ref(source)
- }
- }
- function propertyToRef(
- source: Record<string, any>,
- key: string,
- defaultValue?: unknown,
- ) {
- const val = source[key]
- return isRef(val)
- ? val
- : (new ObjectRefImpl(source, key, defaultValue) as any)
- }
- /**
- * 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
- * }
- * }
- * ```
- */
- export interface RefUnwrapBailTypes {}
- export type ShallowUnwrapRef<T> = {
- [K in keyof T]: DistributeRef<T[K]>
- }
- type DistributeRef<T> = T extends Ref<infer V> ? V : T
- export type UnwrapRef<T> =
- T extends ShallowRef<infer V>
- ? V
- : T extends Ref<infer V>
- ? UnwrapRefSimple<V>
- : UnwrapRefSimple<T>
- export type UnwrapRefSimple<T> = T extends
- | Builtin
- | Ref
- | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
- | { [RawSymbol]?: true }
- ? T
- : T extends Map<infer K, infer V>
- ? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
- : T extends WeakMap<infer K, infer V>
- ? WeakMap<K, UnwrapRefSimple<V>> &
- UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
- : T extends Set<infer V>
- ? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
- : T extends WeakSet<infer V>
- ? WeakSet<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof WeakSet<any>>>
- : T extends ReadonlyArray<any>
- ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
- : T extends object & { [ShallowReactiveMarker]?: never }
- ? {
- [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
- }
- : T
|