跳转到内容

响应性 API:核心

另请参阅

为了更好地理解响应性 API,建议阅读指南中的以下章节

ref()

接收一个内部值并返回一个响应性和可变的 ref 对象,该对象具有一个指向内部值的单属性 .value

  • 类型

    ts
    function ref<T>(value: T): Ref<UnwrapRef<T>>
    
    interface Ref<T> {
      value: T
    }
  • 详情

    ref 对象是可变的 - 即您可以分配新的值给 .value。它也是响应性的 - 即对 .value 的任何读取操作都会被跟踪,写入操作将触发相关效果。

    如果将对象分配为 ref 的值,则对象会通过 reactive() 被转换为深度响应性。这也意味着如果对象包含嵌套的 ref,它们将被深度展开。

    为了避免深度转换,请使用 shallowRef()

  • 示例

    js
    const count = ref(0)
    console.log(count.value) // 0
    
    count.value = 1
    console.log(count.value) // 1
  • 另请参阅

computed()

接收一个 getter 函数 并返回一个只读响应性 ref 对象,该对象包含从 getter 返回的值。它还可以接收一个具有 getset 函数的对象来创建可写的 ref 对象。

  • 类型

    ts
    // read-only
    function computed<T>(
      getter: (oldValue: T | undefined) => T,
      // see "Computed Debugging" link below
      debuggerOptions?: DebuggerOptions
    ): Readonly<Ref<Readonly<T>>>
    
    // writable
    function computed<T>(
      options: {
        get: (oldValue: T | undefined) => T
        set: (value: T) => void
      },
      debuggerOptions?: DebuggerOptions
    ): Ref<T>
  • 示例

    创建只读计算 ref

    js
    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    
    console.log(plusOne.value) // 2
    
    plusOne.value++ // error

    创建可写计算 ref

    js
    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

    调试

    js
    const plusOne = computed(() => count.value + 1, {
      onTrack(e) {
        debugger
      },
      onTrigger(e) {
        debugger
      }
    })
  • 另请参阅

reactive()

返回对象的响应式代理。

  • 类型

    ts
    function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
  • 详情

    响应式转换是“深层”的:它影响所有嵌套属性。响应式对象在保持响应性的同时,也会深度展开任何引用属性。

    需要注意的是,当引用作为响应式数组或原生集合类型(如Map)的元素访问时,不会执行引用展开。

    为了避免深度转换并仅在根级别保留响应性,请使用shallowReactive()

    返回的对象及其嵌套对象被ES Proxy包装,并且与原始对象不等价。建议仅使用响应式代理并避免依赖于原始对象。

  • 示例

    创建响应式对象

    js
    const obj = reactive({ count: 0 })
    obj.count++

    引用展开

    ts
    const count = ref(1)
    const obj = reactive({ count })
    
    // ref will be unwrapped
    console.log(obj.count === count.value) // true
    
    // it will update `obj.count`
    count.value++
    console.log(count.value) // 2
    console.log(obj.count) // 2
    
    // it will also update `count` ref
    obj.count++
    console.log(obj.count) // 3
    console.log(count.value) // 3

    请注意,当引用作为数组或集合元素访问时,引用不会被展开

    js
    const books = reactive([ref('Vue 3 Guide')])
    // need .value here
    console.log(books[0].value)
    
    const map = reactive(new Map([['count', ref(0)]]))
    // need .value here
    console.log(map.get('count').value)

    当将引用赋值给reactive属性时,该引用也将自动展开

    ts
    const count = ref(1)
    const obj = reactive({})
    
    obj.count = count
    
    console.log(obj.count) // 1
    console.log(obj.count === count.value) // true
  • 另请参阅

readonly()

接受一个对象(响应式或纯对象)或一个引用,并返回对原始对象的只读代理。

  • 类型

    ts
    function readonly<T extends object>(
      target: T
    ): DeepReadonly<UnwrapNestedRefs<T>>
  • 详情

    只读代理是深层的:访问的任何嵌套属性也将是只读的。它还具有与reactive()相同的引用展开行为,除了展开的值也将变为只读。

    为了避免深度转换,请使用shallowReadonly()

  • 示例

    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!

watchEffect()

立即运行一个函数,同时响应式地跟踪其依赖项,并在依赖项更改时重新运行它。

  • 类型

    ts
    function watchEffect(
      effect: (onCleanup: OnCleanup) => void,
      options?: WatchEffectOptions
    ): WatchHandle
    
    type OnCleanup = (cleanupFn: () => void) => void
    
    interface WatchEffectOptions {
      flush?: 'pre' | 'post' | 'sync' // default: 'pre'
      onTrack?: (event: DebuggerEvent) => void
      onTrigger?: (event: DebuggerEvent) => void
    }
    
    interface WatchHandle {
      (): void // callable, same as `stop`
      pause: () => void
      resume: () => void
      stop: () => void
    }
  • 详情

    第一个参数是要运行的效应函数。效应函数接收一个函数,可用于注册清理回调。清理回调将在下一次效应重新运行之前被调用,并可用于清理无效的副作用,例如挂起的异步请求(见以下示例)。

    第二个参数是一个可选的选项对象,可用于调整效应的刷新时间或调试效应的依赖项。

    默认情况下,观察者将在组件渲染之前运行。将flush: 'post'设置为将观察者推迟到组件渲染之后。有关更多信息,请参阅回调刷新时间。在罕见的情况下,可能需要在响应式依赖项更改时立即触发观察者,例如,以使缓存无效。这可以通过使用flush: 'sync'来实现。然而,此设置应谨慎使用,因为它可能导致在同时更新多个属性时出现性能和数据一致性方面的问题。

    返回值是一个处理函数,可以调用它以停止效应再次运行。

  • 示例

    js
    const count = ref(0)
    
    watchEffect(() => console.log(count.value))
    // -> logs 0
    
    count.value++
    // -> logs 1

    停止观察者

    js
    const stop = watchEffect(() => {})
    
    // when the watcher is no longer needed:
    stop()

    暂停/恢复观察者:

    js
    const { stop, pause, resume } = watchEffect(() => {})
    
    // temporarily pause the watcher
    pause()
    
    // resume later
    resume()
    
    // stop
    stop()

    副作用清理

    js
    watchEffect(async (onCleanup) => {
      const { response, cancel } = doAsyncWork(newId)
      // `cancel` will be called if `id` changes, cancelling
      // the previous request if it hasn't completed yet
      onCleanup(cancel)
      data.value = await response
    })

    3.5+中的副作用清理

    js
    import { onWatcherCleanup } from 'vue'
    
    watchEffect(async () => {
      const { response, cancel } = doAsyncWork(newId)
      // `cancel` will be called if `id` changes, cancelling
      // the previous request if it hasn't completed yet
      onWatcherCleanup(cancel)
      data.value = await response
    })

    选项

    js
    watchEffect(() => {}, {
      flush: 'post',
      onTrack(e) {
        debugger
      },
      onTrigger(e) {
        debugger
      }
    })
  • 另请参阅

watchPostEffect()

watchEffect()的别名,带有flush: 'post'选项。

watchSyncEffect()

watchEffect()的别名,带有flush: 'sync'选项。

watch()

观察一个或多个响应式数据源,并在数据源更改时调用回调函数。

  • 类型

    ts
    // watching single source
    function watch<T>(
      source: WatchSource<T>,
      callback: WatchCallback<T>,
      options?: WatchOptions
    ): WatchHandle
    
    // watching multiple sources
    function watch<T>(
      sources: WatchSource<T>[],
      callback: WatchCallback<T[]>,
      options?: WatchOptions
    ): WatchHandle
    
    type WatchCallback<T> = (
      value: T,
      oldValue: T,
      onCleanup: (cleanupFn: () => void) => void
    ) => void
    
    type WatchSource<T> =
      | Ref<T> // ref
      | (() => T) // getter
      | (T extends object ? T : never) // reactive object
    
    interface WatchOptions extends WatchEffectOptions {
      immediate?: boolean // default: false
      deep?: boolean | number // default: false
      flush?: 'pre' | 'post' | 'sync' // default: 'pre'
      onTrack?: (event: DebuggerEvent) => void
      onTrigger?: (event: DebuggerEvent) => void
      once?: boolean // default: false (3.4+)
    }
    
    interface WatchHandle {
      (): void // callable, same as `stop`
      pause: () => void
      resume: () => void
      stop: () => void
    }

    为了可读性,类型已简化。

  • 详情

    watch() 默认是懒加载的 - 即只有当被观察的源发生变化时,回调才会被调用。

    第一个参数是观察者的 。源可以是以下之一

    • 一个返回值的 getter 函数
    • 一个 ref
    • 一个响应式对象
    • ...或者以上任意类型的数组。

    第二个参数是在源发生变化时被调用的回调函数。回调函数接收三个参数:新值、旧值以及一个用于注册副作用清理回调函数的函数。清理回调函数将在下一次副作用重新运行之前被调用,可以用来清理无效的副作用,例如挂起的异步请求。

    当观察多个源时,回调函数接收包含新/旧值的两个数组,这些值对应于源数组。

    第三个可选参数是一个支持以下选项的选项对象

    • immediate:在观察者创建时立即触发回调。第一次调用时,旧值将是 undefined
    • deep:如果源是对象,强制进行深度遍历,以便在深度突变时触发回调。在 3.5+ 中,这也可以是一个表示最大遍历深度的数字。参见 深度观察者
    • flush:调整回调的刷新时间。参见 回调刷新时间watchEffect()
    • onTrack / onTrigger:调试观察者的依赖关系。参见 观察者调试
    • once:(3.4+)只运行一次回调。第一次回调运行后,观察者将自动停止。

    watchEffect() 相比,watch() 允许我们

    • 懒加载副作用;
    • 更具体地指定哪些状态应该触发观察者重新运行;
    • 访问被观察状态的当前值和旧值。
  • 示例

    观察 getter

    js
    const state = reactive({ count: 0 })
    watch(
      () => state.count,
      (count, prevCount) => {
        /* ... */
      }
    )

    观察 ref

    js
    const count = ref(0)
    watch(count, (count, prevCount) => {
      /* ... */
    })

    当观察多个源时,回调函数接收包含新/旧值的数组,这些值对应于源数组

    js
    watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
      /* ... */
    })

    当使用 getter 源时,只有当 getter 的返回值发生变化时,观察者才会触发。如果您希望在深度突变时触发回调,需要显式地将观察者强制进入深度模式,使用 { deep: true }。注意,在深度模式下,如果回调是由深度突变触发的,新值和旧值将是相同的对象

    js
    const state = reactive({ count: 0 })
    watch(
      () => state,
      (newValue, oldValue) => {
        // newValue === oldValue
      },
      { deep: true }
    )

    当直接观察响应式对象时,观察者自动处于深度模式

    js
    const state = reactive({ count: 0 })
    watch(state, () => {
      /* triggers on deep mutation to state */
    })

    watch()watchEffect() 具有相同的刷新时间和调试选项

    js
    watch(source, callback, {
      flush: 'post',
      onTrack(e) {
        debugger
      },
      onTrigger(e) {
        debugger
      }
    })

    停止观察者

    js
    const stop = watch(source, callback)
    
    // when the watcher is no longer needed:
    stop()

    暂停/恢复观察者:

    js
    const { stop, pause, resume } = watch(() => {})
    
    // temporarily pause the watcher
    pause()
    
    // resume later
    resume()
    
    // stop
    stop()

    副作用清理

    js
    watch(id, async (newId, oldId, onCleanup) => {
      const { response, cancel } = doAsyncWork(newId)
      // `cancel` will be called if `id` changes, cancelling
      // the previous request if it hasn't completed yet
      onCleanup(cancel)
      data.value = await response
    })

    3.5+中的副作用清理

    js
    import { onWatcherCleanup } from 'vue'
    
    watch(id, async (newId) => {
      const { response, cancel } = doAsyncWork(newId)
      onWatcherCleanup(cancel)
      data.value = await response
    })
  • 另请参阅

onWatcherCleanup()

注册一个清理函数,在当前观察者即将重新运行时执行。只能在同步执行 watchEffect 副作用函数或 watch 回调函数时调用(即不能在异步函数中的 await 语句之后调用。)

  • 类型

    ts
    function onWatcherCleanup(
      cleanupFn: () => void,
      failSilently?: boolean
    ): void
  • 示例

    ts
    import { watch, onWatcherCleanup } from 'vue'
    
    watch(id, (newId) => {
      const { response, cancel } = doAsyncWork(newId)
      // `cancel` will be called if `id` changes, cancelling
      // the previous request if it hasn't completed yet
      onWatcherCleanup(cancel)
    })
响应性 API:核心已加载