]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
docs: comments on reactivity functions (fixes #4832) (#4836)
authorRoland Hummel <defaude@gmail.com>
Fri, 31 Mar 2023 09:06:10 +0000 (11:06 +0200)
committerGitHub <noreply@github.com>
Fri, 31 Mar 2023 09:06:10 +0000 (17:06 +0800)
close #4832

packages/compiler-sfc/src/templateUtils.ts
packages/reactivity/src/computed.ts
packages/reactivity/src/effect.ts
packages/reactivity/src/effectScope.ts
packages/reactivity/src/reactive.ts
packages/reactivity/src/ref.ts

index 3f4cb8f6caa450140d2fa29a7ded18c8997d66ad..0966945a5ee55cedf8fe1f07757277f1e62cc99b 100644 (file)
@@ -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
index e9f36ecd22fdfd64a1b92d7771031fddbad7078c..b24484c9e62cb8ba8029f4514c1725130acbda4e 100644 (file)
@@ -68,6 +68,39 @@ export class ComputedRefImpl<T> {
   }
 }
 
+/**
+ * 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<T>(
   getter: ComputedGetter<T>,
   debugOptions?: DebuggerOptions
index 130fc863f4fa865c313ce44d26ff2737483ea5db..d4a34edfef41ede0268253adf4c8460bbe4c7481 100644 (file)
@@ -167,6 +167,16 @@ export interface ReactiveEffectRunner<T = any> {
   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<T = any>(
   fn: () => T,
   options?: ReactiveEffectOptions
@@ -188,6 +198,11 @@ export function effect<T = any>(
   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,
index c644d03fae6ca90a33d062b1ed9223d9a3c13f33..a65c48d031bd6e4798c417a85af82421ce5e5080 100644 (file)
@@ -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)
index 83e1c7abee1b0affe792e64b5f1a66622fe0000d..b51a5027972eb8dd08d277848bc45a6a99a7db8a 100644 (file)
@@ -65,26 +65,19 @@ function getTargetType(value: Target) {
 export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>
 
 /**
- * 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<T extends object>(target: T): UnwrapNestedRefs<T>
 export function reactive(target: object) {
@@ -106,9 +99,34 @@ export declare const ShallowReactiveMarker: unique symbol
 export type ShallowReactive<T> = 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<T extends object>(
   target: T
@@ -147,8 +165,33 @@ export type DeepReadonly<T> = T extends Builtin
   : Readonly<T>
 
 /**
- * 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<T extends object>(
   target: T
@@ -163,10 +206,34 @@ export function readonly<T extends object>(
 }
 
 /**
- * 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<T extends object>(target: T): Readonly<T> {
   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<T>(observed: T): T {
   const raw = observed && (observed as Target)[ReactiveFlags.RAW]
   return raw ? toRaw(raw) : observed
@@ -243,13 +369,49 @@ export function toRaw<T>(observed: T): T {
 
 export type Raw<T> = 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<T extends object>(value: T): Raw<T> {
   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 = <T extends unknown>(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 = <T extends unknown>(value: T): T =>
   isObject(value) ? readonly(value as Record<any, any>) : value
index 9bb4ce29be59196cf34afec674dbe78f85416acb..85a19802d4fe8f2a9b7c6bcbc535eaf579a0e068 100644 (file)
@@ -69,11 +69,24 @@ export function triggerRefValue(ref: RefBase<any>, 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<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 extends object>(
   value: T
 ): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
@@ -87,6 +100,23 @@ 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 extends object>(
   value: T
 ): T extends Ref ? T : ShallowRef<T>
@@ -132,10 +162,51 @@ class RefImpl<T> {
   }
 }
 
+/**
+ * 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<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: T | Ref<T>): T {
   return isRef(ref) ? (ref.value as any) : ref
 }
@@ -153,6 +224,16 @@ const shallowUnwrapHandlers: ProxyHandler<any> = {
   }
 }
 
+/**
+ * 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> {
@@ -195,6 +276,13 @@ class CustomRefImpl<T> {
   }
 }
 
+/**
+ * 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
 }
@@ -202,6 +290,15 @@ export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
 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)) {
     console.warn(`toRefs() expects a reactive object but received a plain one.`)
@@ -238,17 +335,42 @@ class ObjectRefImpl<T extends object, K extends keyof T> {
 
 export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
 
+/**
+ * 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<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<T extends object, K extends keyof T>(
   object: T,
   key: K,