リアクティビティー API: 上級編
shallowRef()
ref()
の shallow(浅い)バージョン。
型
tsfunction shallowRef<T>(value: T): ShallowRef<T> interface ShallowRef<T> { value: T }
詳細
ref()
とは異なり、浅い ref の内部の値はそのまま格納・公開され、深いリアクティブにはなりません。.value
へのアクセスだけがリアクティブです。shallowRef()
は通常、大きなデータ構造のパフォーマンスの最適化や外部の状態管理システムとの統合に使用されます。例
jsconst state = shallowRef({ count: 1 }) // 変更をトリガーしない state.value.count = 2 // 変更をトリガーする state.value = { count: 2 }
参照
triggerRef()
浅い ref に依存するエフェクトを強制的にトリガーします。これは通常、浅い ref の内部値に対して深い変更を加えた後に使用されます。
型
tsfunction triggerRef(ref: ShallowRef): void
例
jsconst shallow = shallowRef({ greet: 'Hello, world' }) // 初回実行時に一度だけ "Hello, world" をログ出力する watchEffect(() => { console.log(shallow.value.greet) }) // 浅い ref なので、これはエフェクトが発動しない shallow.value.greet = 'Hello, universe' // "Hello, universe" がログ出力される triggerRef(shallow)
customRef()
依存関係の追跡と更新のトリガーを明示的に制御して、カスタマイズされた ref を作成します。
型
tsfunction customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }
詳細
customRef()
はファクトリー関数を想定しており、引数としてtrack
とtrigger
関数を受け取り、get
とset
メソッドを持つオブジェクトを返す必要があります。一般的に、
track()
はget()
の内部で、trigger()
はset()
の内部で呼び出されるべきものです。しかし、これらをいつ呼び出すか、あるいは全く呼び出さないかについては、あなたが完全にコントロールできます。例
最新の set 呼び出しから一定のタイムアウト後にのみ値を更新する debounced ref を作成する:
jsimport { 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>
注意して使用してください
customRef を使用する場合、特にゲッターを実行するたびに新しいオブジェクトのデータ型を生成する場合にはそのゲッターの戻り値に注意が必要です。これは、このような customRef が props として渡されている親コンポーネントと子コンポーネントの関係に影響します。
親コンポーネントのレンダー関数は、別のリアクティブな状態の変化によってトリガーされる可能性があります。再レンダリング中に customRef の値は再評価され、子コンポーネントへの props として新しいオブジェクトデータ型を返します。この props は子コンポーネントで前回の値と比較され、異なるので、子コンポーネント内で customRef のリアクティブな依存関係がトリガーされます。一方で customRef のセッターが呼び出されず、その結果としてその依存関係がトリガーされなかったため、親コンポーネントのリアクティブな依存関係は実行されません。
shallowReactive()
reactive()
の shallow(浅い)バージョン。
型
tsfunction shallowReactive<T extends object>(target: T): T
詳細
reactive()
とは異なり、深い変換は行われません。浅いリアクティブオブジェクトでは、ルートレベルのプロパティのみがリアクティブになります。プロパティ値はそのまま保存され、公開されます。これは、ref 値を持つプロパティが自動的にアンラップされないことも意味します。注意して使用してください
浅いデータ構造は、コンポーネントのルートレベルの状態にのみ使用してください。深いリアクティブオブジェクトの中にネストするのは避けましょう。一貫性のないリアクティビティーの振る舞いを持ったツリーが作成され、理解やデバッグが困難になります。
例
jsconst state = shallowReactive({ foo: 1, nested: { bar: 2 } }) // ステート自身のプロパティを変更するのはリアクティブ state.foo++ // ...しかしネストされたオブジェクトは変換しない isReactive(state.nested) // false // リアクティブではない state.nested.bar++
shallowReadonly()
readonly()
の shallow(浅い)バージョン。
型
tsfunction shallowReadonly<T extends object>(target: T): Readonly<T>
詳細
readonly()
とは異なり、深い変換は行われません。ルートレベルのプロパティのみが読み取り専用になります。プロパティ値はそのまま保存され、公開されます。これは、ref 値を持つプロパティが自動的にアンラップされないことも意味します。注意して使用してください
浅いデータ構造は、コンポーネントのルートレベルの状態にのみ使用すべきです。深いリアクティブオブジェクトの中にネストするのは避けましょう。一貫性のないリアクティブな振る舞いをするツリーが作成され、理解やデバッグが困難になります。
例
jsconst state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // ステート自身のプロパティを変更すると失敗する state.foo++ // ...しかしネストされたオブジェクトでは動作する isReadonly(state.nested) // false // 動作する state.nested.bar++
toRaw()
Vue で作成されたプロキシの、未加工の元のオブジェクトを返します。
型
tsfunction toRaw<T>(proxy: T): T
詳細
toRaw()
はreactive()
,readonly()
,shallowReactive()
,shallowReadonly()
で生成したプロキシから元のオブジェクトを返せるようにします。これは、プロキシのアクセスやトラッキングのオーバーヘッドを発生させずに一時的に読み込んだり、変更をトリガーせずに書き込んだりするために使用できる緊急避難口です。元のオブジェクトへの永続的な参照を保持することは推奨されません。注意して使用してください。
例
jsconst foo = {} const reactiveFoo = reactive(foo) console.log(toRaw(reactiveFoo) === foo) // true
markRaw()
プロキシに変換されないようにオブジェクトをマークします。オブジェクト自体を返します。
型
tsfunction markRaw<T extends object>(value: T): T
例
jsconst foo = markRaw({}) console.log(isReactive(reactive(foo))) // false // 他のリアクティブオブジェクトの中にネストされても動作します const bar = reactive({ foo }) console.log(isReactive(bar.foo)) // false
注意して使用してください
markRaw()
やshallowReactive()
などの浅い API を使うと、デフォルトの深いリアクティブ/読み取り専用の変換を選択的にオプトアウトして、未加工の非プロキシオブジェクトを状態グラフに埋め込むことができるようになります。これらは様々な理由で利用できます:複雑なサードパーティのクラスインスタンスや Vue のコンポーネントオブジェクトのように、単純にリアクティブにすべきではない値もあります。
プロキシ変換をスキップすることで、イミュータブルなデータソースで大きなリストをレンダリングするときのパフォーマンスが向上する可能性があります。
未加工のオプトアウトはルートレベルのみであるため、ネストされた、マークされていない未加工のオブジェクトをリアクティブオブジェクトにセットし、再度アクセスすると、プロキシされたバージョンが戻ってくるので、高度とみなされます。これは、アイデンティティハザード(オブジェクトの同一性に依存する操作を、同じオブジェクトの未加工バージョンとプロキシされたバージョンの両方を使用して操作すること)につながる可能性があります。
jsconst foo = markRaw({ nested: {} }) const bar = reactive({ // `foo` は未加工としてマークされるが foo.nested はそうでない nested: foo.nested }) console.log(foo.nested === bar.nested) // false
アイデンティティハザードが発生することは一般的に稀です。しかし、安全にアイデンティティハザードを回避しながらこれらの API を適切に利用するには、リアクティビティーの仕組みについてしっかりと理解することが必要です。
effectScope()
エフェクトスコープオブジェクトを作成し、その中に作成されたリアクティブエフェクト(すなわち、computed とウォッチャー)をキャプチャーして、これらのエフェクトを一緒に廃棄できるようにします。この API の詳細な使用例については、対応する RFC を参照してください。
型
tsfunction effectScope(detached?: boolean): EffectScope interface EffectScope { run<T>(fn: () => T): T | undefined // スコープがアクティブでなければ undefined stop(): void }
例
jsconst scope = effectScope() scope.run(() => { const doubled = computed(() => counter.value * 2) watch(doubled, () => console.log(doubled.value)) watchEffect(() => console.log('Count: ', doubled.value)) }) // スコープ内のすべてのエフェクトを破棄 scope.stop()
getCurrentScope()
現在アクティブなエフェクトスコープがある場合、それを返します。
型
tsfunction getCurrentScope(): EffectScope | undefined
onScopeDispose()
現在アクティブなエフェクトスコープに破棄のコールバックを登録します。このコールバックは、関連するエフェクトスコープが停止されたときに呼び出されます。
各 Vue コンポーネントの setup()
関数はエフェクトスコープでも呼び出されるので、このメソッドは再利用可能なコンポジション関数において、コンポーネントに結合しない onUnmounted
の代替として使用できます。
アクティブなエフェクトスコープがない状態でこの関数を呼び出すと、警告が表示されます。バージョン 3.5 以上では、第二引数に true
を渡すことで、警告を抑制することができます。
型
tsfunction onScopeDispose(fn: () => void, failSilently?: boolean): void