跳转到内容

响应式API:高级

shallowRef()

ref()的浅层版本。

  • 类型

    ts
    function shallowRef<T>(value: T): ShallowRef<T>
    
    interface ShallowRef<T> {
      value: T
    }
  • 详情

    ref()不同,浅层引用的内部值直接存储和暴露,不会变为深层响应式。只有.value访问是响应式的。

    shallowRef()通常用于大型数据结构的性能优化或与外部状态管理系统的集成。

  • 示例

    js
    const state = shallowRef({ count: 1 })
    
    // does NOT trigger change
    state.value.count = 2
    
    // does trigger change
    state.value = { count: 2 }
  • 另请参阅

triggerRef()

根据浅引用(shallow ref)触发相关效果。这通常在修改浅引用内部值之后使用。

  • 类型

    ts
    function triggerRef(ref: ShallowRef): void
  • 示例

    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)

customRef()

创建一个具有显式控制其依赖跟踪和更新触发的自定义引用。

  • 类型

    ts
    function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
    
    type CustomRefFactory<T> = (
      track: () => void,
      trigger: () => void
    ) => {
      get: () => T
      set: (value: T) => void
    }
  • 详情

    customRef()期望一个工厂函数,该函数接收tracktrigger函数作为参数,并应返回一个具有getset方法的对象。

    通常,track()应在get()内部调用,而trigger()应在set()内部调用。然而,您可以完全控制它们何时调用,或者是否调用它们。

  • 示例

    创建一个防抖引用,仅在最新设置调用后的特定超时后更新值

    js
    import { customRef } from 'vue'
    
    export function useDebouncedRef(value, delay = 200) {
      let timeout
      return customRef((track, trigger) => {
        return {
          get() {
            track()
            return value
          },
          set(newValue) {
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()
            }, delay)
          }
        }
      })
    }

    组件中使用

    vue
    <script setup>
    import { useDebouncedRef } from './debouncedRef'
    const text = useDebouncedRef('hello')
    </script>
    
    <template>
      <input v-model="text" />
    </template>

    在Playground中尝试

    谨慎使用

    使用自定义引用时,我们应该小心其getter的返回值,尤其是在每次运行getter时生成新的对象数据类型时。这影响了父组件和子组件之间的关系,其中这种自定义引用被作为prop传递。

    父组件的渲染函数可能由不同响应式状态的更改触发。在重新渲染期间,我们自定义引用的值会被重新评估,返回一个新的对象数据类型作为prop传递给子组件。子组件将此prop与其最后一个值进行比较,由于它们不同,子组件中自定义引用的响应式依赖项被触发。同时,父组件中的响应式依赖项没有运行,因为自定义引用的setter没有被调用,其依赖项也没有被触发。

    在Playground中查看

shallowReactive()

浅版本的reactive()

  • 类型

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

    reactive()不同,没有深度转换:浅响应式对象的根级属性是响应式的。属性值按原样存储和公开 - 这也意味着具有ref值的属性将不会自动解包。

    谨慎使用

    浅数据结构应仅用于组件的根级状态。避免将其嵌套在深度响应式对象中,因为它创建了一个具有不一致响应性行为的树,这可能会很难理解和调试。

  • 示例

    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++

shallowReadonly()

浅版本的readonly()

  • 类型

    ts
    function shallowReadonly<T extends object>(target: T): Readonly<T>
  • 详情

    readonly()不同,没有深度转换:只有根级属性被设置为只读。属性值按原样存储和公开 - 这也意味着具有ref值的属性将不会自动解包。

    谨慎使用

    浅数据结构应仅用于组件的根级状态。避免将其嵌套在深度响应式对象中,因为它创建了一个具有不一致响应性行为的树,这可能会很难理解和调试。

  • 示例

    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++

toRaw()

返回Vue创建的代理的原始对象。

  • 类型

    ts
    function toRaw<T>(proxy: T): T
  • 详情

    toRaw()可以从由reactive()readonly()shallowReactive()shallowReadonly()创建的代理中返回原始对象。

    这是一个逃生舱,可用于在不产生代理访问/跟踪开销的情况下暂时读取,或在不触发更改的情况下写入。不建议持久引用原始对象。请谨慎使用。

  • 示例

    js
    const foo = {}
    const reactiveFoo = reactive(foo)
    
    console.log(toRaw(reactiveFoo) === foo) // true

markRaw()

标记一个对象,使其永远不会转换为代理。返回对象本身。

  • 类型

    ts
    function markRaw<T extends object>(value: T): T
  • 示例

    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

    谨慎使用

    markRaw() 和浅层 API,如 shallowReactive(),允许您选择性地退出默认的深度响应式/只读转换,并将原始、非代理对象嵌入到您的状态图中。它们可以用作各种原因

    • 某些值根本不应该变成响应式的,例如复杂的第三方类实例或 Vue 组件对象。

    • 当渲染包含不可变数据源的大型列表时,跳过代理转换可以提高性能。

    它们被认为是高级的,因为原始的退出仅在根级别,所以如果您将一个嵌套的、未标记的原始对象设置到响应式对象中,然后再次访问它,您会得到代理版本。这可能导致 身份风险 - 即执行依赖于对象身份的操作,但使用同一对象的原始和代理版本

    js
    const foo = markRaw({
      nested: {}
    })
    
    const bar = reactive({
      // although `foo` is marked as raw, foo.nested is not.
      nested: foo.nested
    })
    
    console.log(foo.nested === bar.nested) // false

    身份风险通常很少见。然而,为了安全地避免身份风险并正确使用这些 API,需要对反应性系统的工作方式有一个深刻的理解。

effectScope()

创建一个效果作用域对象,可以捕获其中创建的响应式效果(即计算属性和监视器),以便可以将这些效果一起丢弃。有关此 API 的详细用法,请参阅其相应的 RFC

  • 类型

    ts
    function effectScope(detached?: boolean): EffectScope
    
    interface EffectScope {
      run<T>(fn: () => T): T | undefined // undefined if scope is inactive
      stop(): void
    }
  • 示例

    js
    const scope = effectScope()
    
    scope.run(() => {
      const doubled = computed(() => counter.value * 2)
    
      watch(doubled, () => console.log(doubled.value))
    
      watchEffect(() => console.log('Count: ', doubled.value))
    })
    
    // to dispose all effects in the scope
    scope.stop()

getCurrentScope()

如果有,则返回当前活动的 效果作用域

  • 类型

    ts
    function getCurrentScope(): EffectScope | undefined

onScopeDispose()

在当前活动的 效果作用域 上注册一个销毁回调。当关联的效果作用域停止时,将调用此回调。

此方法可以用作可复用组合函数中 onUnmounted 的非组件耦合替代品,因为每个 Vue 组件的 setup() 函数也是在效果作用域内调用的。

如果没有活动的作用域就调用此函数,将抛出一个警告。在 3.5+ 中,可以通过将 true 作为第二个参数传递来抑制此警告。

  • 类型

    ts
    function onScopeDispose(fn: () => void, failSilently?: boolean): void
响应式 API:高级已加载