diff --git a/packages/compiler-sfc/src/templateUtils.ts b/packages/compiler-sfc/src/templateUtils.ts index 3f4cb8f6c..0966945a5 100644 --- a/packages/compiler-sfc/src/templateUtils.ts +++ b/packages/compiler-sfc/src/templateUtils.ts @@ -30,7 +30,7 @@ export function parseUrl(url: string): UrlWithStringQuery { /** * vuejs/component-compiler-utils#22 Support uri fragment in transformed require - * @param urlString an url as a string + * @param urlString - an url as a string */ function parseUriParts(urlString: string): UrlWithStringQuery { // A TypeError is thrown if urlString is not a string diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index e9f36ecd2..b24484c9e 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -68,6 +68,39 @@ export class ComputedRefImpl { } } +/** + * Takes a getter function and returns a readonly reactive ref object for the + * returned value from the getter. It can also take an object with get and set + * functions to create a writable ref object. + * + * @example + * ```js + * // Creating a readonly computed ref: + * const count = ref(1) + * const plusOne = computed(() => count.value + 1) + * + * console.log(plusOne.value) // 2 + * plusOne.value++ // error + * ``` + * + * ```js + * // Creating a writable computed ref: + * const count = ref(1) + * const plusOne = computed({ + * get: () => count.value + 1, + * set: (val) => { + * count.value = val - 1 + * } + * }) + * + * plusOne.value = 1 + * console.log(count.value) // 0 + * ``` + * + * @param getter - Function that produces the next value. + * @param debugOptions - For debugging. See {@link https://vuejs.org/guide/extras/reactivity-in-depth.html#computed-debugging}. + * @see {@link https://vuejs.org/api/reactivity-core.html#computed} + */ export function computed( getter: ComputedGetter, debugOptions?: DebuggerOptions diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 130fc863f..d4a34edfe 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -167,6 +167,16 @@ export interface ReactiveEffectRunner { effect: ReactiveEffect } +/** + * Registers the given function to track reactive updates. + * + * The given function will be run once immediately. Every time any reactive + * property that's accessed within it gets updated, the function will run again. + * + * @param fn - The function that will track reactive updates. + * @param options - Allows to control the effect's behaviour. + * @returns A runner that can be used to control the effect after creation. + */ export function effect( fn: () => T, options?: ReactiveEffectOptions @@ -188,6 +198,11 @@ export function effect( return runner } +/** + * Stops the effect associated with the given runner. + * + * @param runner - Association with the effect to stop tracking. + */ export function stop(runner: ReactiveEffectRunner) { runner.effect.stop() } @@ -195,21 +210,40 @@ export function stop(runner: ReactiveEffectRunner) { export let shouldTrack = true const trackStack: boolean[] = [] +/** + * Temporarily pauses tracking. + */ export function pauseTracking() { trackStack.push(shouldTrack) shouldTrack = false } +/** + * Re-enables effect tracking (if it was paused). + */ export function enableTracking() { trackStack.push(shouldTrack) shouldTrack = true } +/** + * Resets the previous global effect tracking state. + */ export function resetTracking() { const last = trackStack.pop() shouldTrack = last === undefined ? true : last } +/** + * Tracks access to a reactive property. + * + * This will check which effect is running at the moment and record it as dep + * which records all effects that depend on the reactive property. + * + * @param target - Object holding the reactive property. + * @param type - Defines the type of access to the reactive property. + * @param key - Identifier of the reactive property to track. + */ export function track(target: object, type: TrackOpTypes, key: unknown) { if (shouldTrack && activeEffect) { let depsMap = targetMap.get(target) @@ -260,6 +294,14 @@ export function trackEffects( } } +/** + * Finds all deps associated with the target (or a specific property) and + * triggers the effects stored within. + * + * @param target - The reactive object. + * @param type - Defines the type of the operation that needs to trigger effects. + * @param key - Can be used to target a specific reactive property in the target object. + */ export function trigger( target: object, type: TriggerOpTypes, diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index c644d03fa..a65c48d03 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -107,6 +107,15 @@ export class EffectScope { } } +/** + * Creates an effect scope object which can capture the reactive effects (i.e. + * computed and watchers) created within it so that these effects can be + * disposed together. For detailed use cases of this API, please consult its + * corresponding {@link https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md | RFC}. + * + * @param detached - Can be used to create a "detached" effect scope. + * @see {@link https://vuejs.org/api/reactivity-advanced.html#effectscope} + */ export function effectScope(detached?: boolean) { return new EffectScope(detached) } @@ -120,10 +129,22 @@ export function recordEffectScope( } } +/** + * Returns the current active effect scope if there is one. + * + * @see {@link https://vuejs.org/api/reactivity-advanced.html#getcurrentscope} + */ export function getCurrentScope() { return activeEffectScope } +/** + * Registers a dispose callback on the current active effect scope. The + * callback will be invoked when the associated effect scope is stopped. + * + * @param fn - The callback function to attach to the scope's cleanup. + * @see {@link https://vuejs.org/api/reactivity-advanced.html#onscopedispose} + */ export function onScopeDispose(fn: () => void) { if (activeEffectScope) { activeEffectScope.cleanups.push(fn) diff --git a/packages/reactivity/src/reactive.ts b/packages/reactivity/src/reactive.ts index 83e1c7abe..b51a50279 100644 --- a/packages/reactivity/src/reactive.ts +++ b/packages/reactivity/src/reactive.ts @@ -65,26 +65,19 @@ function getTargetType(value: Target) { export type UnwrapNestedRefs = T extends Ref ? T : UnwrapRefSimple /** - * Creates a reactive copy of the original object. + * Returns a reactive proxy of the object. * - * The reactive conversion is "deep"—it affects all nested properties. In the - * ES2015 Proxy based implementation, the returned proxy is **not** equal to the - * original object. It is recommended to work exclusively with the reactive - * proxy and avoid relying on the original object. - * - * A reactive object also automatically unwraps refs contained in it, so you - * don't need to use `.value` when accessing and mutating their value: + * The reactive conversion is "deep": it affects all nested properties. A + * reactive object also deeply unwraps any properties that are refs while + * maintaining reactivity. * + * @example * ```js - * const count = ref(0) - * const obj = reactive({ - * count - * }) - * - * obj.count++ - * obj.count // -> 1 - * count.value // -> 1 + * const obj = reactive({ count: 0 }) * ``` + * + * @param target - The source object. + * @see {@link https://vuejs.org/api/reactivity-core.html#reactive} */ export function reactive(target: T): UnwrapNestedRefs export function reactive(target: object) { @@ -106,9 +99,34 @@ export declare const ShallowReactiveMarker: unique symbol export type ShallowReactive = T & { [ShallowReactiveMarker]?: true } /** - * Return a shallowly-reactive copy of the original object, where only the root - * level properties are reactive. It also does not auto-unwrap refs (even at the - * root level). + * Shallow version of {@link reactive()}. + * + * Unlike {@link reactive()}, there is no deep conversion: only root-level + * properties are reactive for a shallow reactive object. Property values are + * stored and exposed as-is - this also means properties with ref values will + * not be automatically unwrapped. + * + * @example + * ```js + * const state = shallowReactive({ + * foo: 1, + * nested: { + * bar: 2 + * } + * }) + * + * // mutating state's own properties is reactive + * state.foo++ + * + * // ...but does not convert nested objects + * isReactive(state.nested) // false + * + * // NOT reactive + * state.nested.bar++ + * ``` + * + * @param target - The source object. + * @see {@link https://vuejs.org/api/reactivity-advanced.html#shallowreactive} */ export function shallowReactive( target: T @@ -147,8 +165,33 @@ export type DeepReadonly = T extends Builtin : Readonly /** - * Creates a readonly copy of the original object. Note the returned copy is not - * made reactive, but `readonly` can be called on an already reactive object. + * Takes an object (reactive or plain) or a ref and returns a readonly proxy to + * the original. + * + * A readonly proxy is deep: any nested property accessed will be readonly as + * well. It also has the same ref-unwrapping behavior as {@link reactive()}, + * except the unwrapped values will also be made readonly. + * + * @example + * ```js + * const original = reactive({ count: 0 }) + * + * const copy = readonly(original) + * + * watchEffect(() => { + * // works for reactivity tracking + * console.log(copy.count) + * }) + * + * // mutating original will trigger watchers relying on the copy + * original.count++ + * + * // mutating the copy will fail and result in a warning + * copy.count++ // warning! + * ``` + * + * @param target - The source object. + * @see {@link https://vuejs.org/api/reactivity-core.html#readonly} */ export function readonly( target: T @@ -163,10 +206,34 @@ export function readonly( } /** - * Returns a reactive-copy of the original object, where only the root level - * properties are readonly, and does NOT unwrap refs nor recursively convert - * returned properties. - * This is used for creating the props proxy object for stateful components. + * Shallow version of {@link readonly()}. + * + * Unlike {@link readonly()}, there is no deep conversion: only root-level + * properties are made readonly. Property values are stored and exposed as-is - + * this also means properties with ref values will not be automatically + * unwrapped. + * + * @example + * ```js + * const state = shallowReadonly({ + * foo: 1, + * nested: { + * bar: 2 + * } + * }) + * + * // mutating state's own properties will fail + * state.foo++ + * + * // ...but works on nested objects + * isReadonly(state.nested) // false + * + * // works + * state.nested.bar++ + * ``` + * + * @param target - The source object. + * @see {@link https://vuejs.org/api/reactivity-advanced.html#shallowreadonly} */ export function shallowReadonly(target: T): Readonly { return createReactiveObject( @@ -217,6 +284,24 @@ function createReactiveObject( return proxy } +/** + * Checks if an object is a proxy created by {@link reactive()} or + * {@link shallowReactive()} (or {@link ref()} in some cases). + * + * @example + * ```js + * isReactive(reactive({})) // => true + * isReactive(readonly(reactive({}))) // => true + * isReactive(ref({}).value) // => true + * isReactive(readonly(ref({})).value) // => true + * isReactive(ref(true)) // => false + * isReactive(shallowRef({}).value) // => false + * isReactive(shallowReactive({})) // => true + * ``` + * + * @param value - The value to check. + * @see {@link https://vuejs.org/api/reactivity-utilities.html#isreactive} + */ export function isReactive(value: unknown): boolean { if (isReadonly(value)) { return isReactive((value as Target)[ReactiveFlags.RAW]) @@ -224,6 +309,17 @@ export function isReactive(value: unknown): boolean { return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE]) } +/** + * Checks whether the passed value is a readonly object. The properties of a + * readonly object can change, but they can't be assigned directly via the + * passed object. + * + * The proxies created by {@link readonly()} and {@link shallowReadonly()} are + * both considered readonly, as is a computed ref without a set function. + * + * @param value - The value to check. + * @see {@link https://vuejs.org/api/reactivity-utilities.html#isreadonly} + */ export function isReadonly(value: unknown): boolean { return !!(value && (value as Target)[ReactiveFlags.IS_READONLY]) } @@ -232,10 +328,40 @@ export function isShallow(value: unknown): boolean { return !!(value && (value as Target)[ReactiveFlags.IS_SHALLOW]) } +/** + * Checks if an object is a proxy created by {@link reactive}, + * {@link readonly}, {@link shallowReactive} or {@link shallowReadonly()}. + * + * @param value - The value to check. + * @see {@link https://vuejs.org/api/reactivity-utilities.html#isproxy} + */ export function isProxy(value: unknown): boolean { return isReactive(value) || isReadonly(value) } +/** + * Returns the raw, original object of a Vue-created proxy. + * + * `toRaw()` can return the original object from proxies created by + * {@link reactive()}, {@link readonly()}, {@link shallowReactive()} or + * {@link shallowReadonly()}. + * + * This is an escape hatch that can be used to temporarily read without + * incurring proxy access / tracking overhead or write without triggering + * changes. It is **not** recommended to hold a persistent reference to the + * original object. Use with caution. + * + * @example + * ```js + * const foo = {} + * const reactiveFoo = reactive(foo) + * + * console.log(toRaw(reactiveFoo) === foo) // true + * ``` + * + * @param observed - The object for which the "raw" value is requested. + * @see {@link https://vuejs.org/api/reactivity-advanced.html#toraw} + */ export function toRaw(observed: T): T { const raw = observed && (observed as Target)[ReactiveFlags.RAW] return raw ? toRaw(raw) : observed @@ -243,13 +369,49 @@ export function toRaw(observed: T): T { export type Raw = T & { [RawSymbol]?: true } +/** + * Marks an object so that it will never be converted to a proxy. Returns the + * object itself. + * + * @example + * ```js + * const foo = markRaw({}) + * console.log(isReactive(reactive(foo))) // false + * + * // also works when nested inside other reactive objects + * const bar = reactive({ foo }) + * console.log(isReactive(bar.foo)) // false + * ``` + * + * **Warning:** `markRaw()` together with the shallow APIs such as + * {@link shallowReactive()} allow you to selectively opt-out of the default + * deep reactive/readonly conversion and embed raw, non-proxied objects in your + * state graph. + * + * @param value - The object to be marked as "raw". + * @see {@link https://vuejs.org/api/reactivity-advanced.html#markraw} + */ export function markRaw(value: T): Raw { def(value, ReactiveFlags.SKIP, true) return value } +/** + * Returns a reactive proxy of the given value (if possible). + * + * If the given value is not an object, the original value itself is returned. + * + * @param value - The value for which a reactive proxy shall be created. + */ export const toReactive = (value: T): T => isObject(value) ? reactive(value) : value +/** + * Returns a readonly proxy of the given value (if possible). + * + * If the given value is not an object, the original value itself is returned. + * + * @param value - The value for which a readonly proxy shall be created. + */ export const toReadonly = (value: T): T => isObject(value) ? readonly(value as Record) : value diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 9bb4ce29b..85a19802d 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -69,11 +69,24 @@ export function triggerRefValue(ref: RefBase, newVal?: any) { } } +/** + * 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(r: Ref | unknown): r is Ref 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( value: T ): [T] extends [Ref] ? T : Ref> @@ -87,6 +100,23 @@ declare const ShallowRefMarker: unique symbol export type ShallowRef = Ref & { [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( value: T ): T extends Ref ? T : ShallowRef @@ -132,10 +162,51 @@ class RefImpl { } } +/** + * 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, __DEV__ ? ref.value : void 0) } +/** + * 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) { + * 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(ref: T | Ref): T { return isRef(ref) ? (ref.value as any) : ref } @@ -153,6 +224,16 @@ const shallowUnwrapHandlers: ProxyHandler = { } } +/** + * 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( objectWithRefs: T ): ShallowUnwrapRef { @@ -195,6 +276,13 @@ class CustomRefImpl { } } +/** + * 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(factory: CustomRefFactory): Ref { return new CustomRefImpl(factory) as any } @@ -202,6 +290,15 @@ export function customRef(factory: CustomRefFactory): Ref { export type ToRefs = { [K in keyof T]: ToRef } + +/** + * 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(object: T): ToRefs { if (__DEV__ && !isProxy(object)) { console.warn(`toRefs() expects a reactive object but received a plain one.`) @@ -238,17 +335,42 @@ class ObjectRefImpl { export type ToRef = IfAny, [T] extends [Ref] ? T : Ref> +/** + * Can 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 object - The reactive object containing the desired property. + * @param key - Name of the property in the reactive object. + * @see {@link https://vuejs.org/api/reactivity-utilities.html#toref} + */ export function toRef( object: T, key: K ): ToRef - export function toRef( object: T, key: K, defaultValue: T[K] ): ToRef> - export function toRef( object: T, key: K,