跳轉到內容

響應式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:高階已載入