useAsyncData と reactivity
Published at: 2022/12/4
useAsyncData('key', asyncHandlerFunc)
前提
- useAsyncData とそれを実現する Nuxt 3 の非同期処理の機構にて共有したこと
- nuxt3 では useAsyncData は key に対する SPA 側のキャッシュデータとして実装されている。
Value に対する Reactivity
前提の記事で共有した通り、 useAsyncData は SPA としてシングルトン的なキャッシュ置き場を持っていて、そこにすべての useAsyncData の key とそれに対応する結果 (useAsyncData を await して帰ってくるオブジェクト) を保持する。
type AsyncData<DataT, ErrorT> = {
data: Ref<DataT | null>
pending: Ref<boolean>
execute: () => Promise<void>
refresh: () => Promise<void>
error: Ref<ErrorT | null>
}
useAsyncData の結果は、 data
や pending
のプロパティが Ref
であることから分かるように、 クライアント側でその呼び出している async 関数(handler) の Promise によって、非同期に更新されている構成となっている。
実は、この AsyncData
のオブジェクトは、 useAsyncData
関数の await する前の直の戻り値としても利用可能になっている。
というのも、
ここでの処理によって、 AsyncData オブジェクトの各プロパティは、 AsyncData を返す Promise オブジェクトへとマージされている。
これを利用して、ページ遷移の際などクライアント側で useAsyncData の結果を待たずにページを表示することが可能となる。 (SSR の場合は、おそらくすべての asyncData Map の promise が resolve するのを待ってから SSR するので、これをやったからといってページの 初期の html の結果がローディング状態になる、ということはない模様。 )
<script setup lang="ts">
const { data, pending } = useAsyncData('foo', async () => {
// データの取得
})
</script>
<template>
<div>
<!-- data や pending を使って条件分岐してテンプレートを組み立て -->
</div>
</template>
reactivity まわりのオプション解説
lazy
:true
の場合、 useAsyncData の戻り値の Promise は、 await したとしても handler の発火やその結果のawait
とは関係なしに、ただただその時の時点のAsyncData
が resolve される。onBeforeMount
で実際に handler の実行は開始される。
immediate
:false
を指定すると、手動でrefresh
/execute
(両者の実態は同じもの) を呼ばないかぎり handler が呼ばれない。watch
: Vue の watch 機構の callback にrefresh
の呼び出しを bind するような処理を行う。
Key に対する reactivity は存在しない
useAsyncData は、その設計思想として key に対して非同期データ取得の promise をデータキャッシュ的に紐付けを行うことで実現されている。
なので、 key
が reactive に変化していくような使い方は想定されておらず、さらにはそれを無理矢理やろうとしてもおそらく上手くいかない。
ただ、一方で例えば routing のパラメーターをキーの一部に埋め込んだりしたい場合には、どういう実装をするのが良いのか、という話になる。
Nuxt 3 においては、 NuxtLayout
と NuxtPage
はそれぞれインスタンス化される際に、 key
属性が指定される。
NuxtLayout
はレイアウト自身の名前が指定され、 NuxtPage
は url のパスが指定される。
これにより、ページが変更される場合には、漏れなく PageComponent はこれまでのものが破棄され、新しい route パラメータを持った page コンポーネントが生成されなおすことになる。
これにより、少なくとも url に対して一意になるように useAsyncData
のキーを指定しておけば、 reactivity 問題を意識する必要はあまりなくなる。
<template>
<div>
<!-- data を利用した何かしら -->
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug
const { data } = await useAsyncData(`some-path/${slug}`, () => {
// slug を利用した処理な何かしら
})
</script>
url に紐付かないような場合であっても、同様に、動的な useAsyncData のキーを指定する場合には、インスタンス化された component が生きている間に reactivity によって途中で key のあるべき値が変わらないように実装することが、おそらく useAsyncData
的には想定された利用ユースケースだと考えられる。
参考
framework/asyncData.ts at main · nuxt/framework · GitHub
The Intuitive Web Framework, based on Vue 3. Contribute to nuxt/framework development by creating an account on GitHub.
github.com
間違い・補足などあれば
twitter にて連絡いただけると幸いです。