useNuxtApp

ソース
Nuxtアプリケーションの共有ランタイムコンテキストにアクセスします。

useNuxtAppは、クライアント側とサーバー側の両方で利用可能な(ただしNitroルート内では利用できない)Nuxtの共有ランタイムコンテキスト、別名Nuxtコンテキストにアクセスする方法を提供する組み込みのコンポーザブルです。これにより、Vueアプリのインスタンス、ランタイムフック、ランタイム設定変数、およびssrContextpayloadなどの内部状態にアクセスできます。

app/app.vue
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>

スコープ内でランタイムコンテキストが利用できない場合、useNuxtAppは呼び出し時に例外をスローします。nuxtAppを必要としないコンポーザブルの場合、または例外なしでコンテキストが利用可能かどうかを単に確認する場合、代わりにtryUseNuxtAppを使用できます。

メソッド

provide (name, value)

nuxtAppは、Nuxtプラグインを使用して拡張できるランタイムコンテキストです。provide関数を使用してNuxtプラグインを作成し、すべてのコンポーザブルおよびコンポーネントで値とヘルパーメソッドをNuxtアプリケーションで利用できるようにします。

provide関数はnamevalueパラメータを受け入れます。

const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', name => `Hello ${name}!`)

// Prints "Hello name!"
console.log(nuxtApp.$hello('name'))

上記の例に示すように、$hellonuxtAppコンテキストの新しいカスタム部分となり、nuxtAppがアクセス可能なすべての場所で利用できます。

hook(name, cb)

nuxtAppで利用可能なフックを使用すると、Nuxtアプリケーションのランタイム側面をカスタマイズできます。Vue.jsのコンポーザブルとNuxtプラグインでランタイムフックを使用し、レンダリングライフサイクルにフックすることができます。

hook関数は、特定の時点でレンダリングライフサイクルにフックしてカスタムロジックを追加するのに役立ちます。hook関数は、主にNuxtプラグインを作成するときに使用されます。

Nuxtによって呼び出される利用可能なランタイムフックについては、ランタイムフックを参照してください。

app/plugins/test.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:start', () => {
    /* your code goes here */
  })
  nuxtApp.hook('vue:error', (..._args) => {
    console.log('vue:error')
    // if (import.meta.client) {
    //   console.log(..._args)
    // }
  })
})

callHook(name, ...args)

callHookは、既存のフックのいずれかで呼び出されたときにPromiseを返します。

await nuxtApp.callHook('my-plugin:init')

プロパティ

useNuxtApp()は、アプリを拡張およびカスタマイズし、状態、データ、変数を共有するために使用できる以下のプロパティを公開します。

vueApp

vueAppはグローバルなVue.jsアプリケーションインスタンスですこれはnuxtAppを通じてアクセスできます。

いくつかの便利なメソッド

  • component()- 名前文字列とコンポーネント定義の両方を渡すとグローバルコンポーネントを登録し、名前のみを渡すと既に登録されているコンポーネントを取得します。
  • directive()- 名前文字列とディレクティブ定義の両方を渡すとグローバルカスタムディレクティブを登録し、名前のみを渡すと既に登録されているディレクティブを取得します(例)
  • use()- Vue.jsプラグインをインストールします(例)
https://vuejs.org/api/application.html#application-apiで詳細をご覧ください。

ssrContext

ssrContextはサーバーサイドレンダリング中に生成され、サーバーサイドでのみ利用可能です。

NuxtはssrContextを通じて以下のプロパティを公開します。

  • url (string) - 現在のリクエストURL。
  • event (h3js/h3リクエストイベント) - 現在のルートのリクエストとレスポンスにアクセスします。
  • payload (object) - NuxtAppペイロードオブジェクト。

payload

payloadは、サーバーサイドからクライアントサイドにデータと状態変数を公開します。以下のキーは、サーバーサイドから渡された後、クライアントで利用可能になります。

  • serverRendered (boolean) - レスポンスがサーバーサイドレンダリングされたかどうかを示します。
  • data (object) - useFetchまたはuseAsyncDataのいずれかを使用してAPIエンドポイントからデータをフェッチすると、結果のペイロードはpayload.dataからアクセスできます。このデータはキャッシュされ、同一のリクエストが複数回行われた場合に同じデータをフェッチするのを防ぐのに役立ちます。
    <script setup lang="ts">
    const { data } = await useAsyncData('count', () => $fetch('/api/count'))
    </script>
    

    上記の例でuseAsyncDataを使ってcountの値を取得した後、payload.dataにアクセスすると、{ count: 1 }が記録されているのがわかります。
    ssrcontextから同じpayload.dataにアクセスする場合も、サーバー側で同じ値にアクセスできます。
  • state (object) - NuxtでuseStateコンポーザブルを使用して共有状態を設定すると、この状態データはpayload.state.[name-of-your-state]を介してアクセスされます。
    app/plugins/my-plugin.ts
    export const useColor = () => useState<string>('color', () => 'pink')
    
    export default defineNuxtPlugin((nuxtApp) => {
      if (import.meta.server) {
        const color = useColor()
      }
    })
    

    refreactiveshallowRefshallowReactiveNuxtErrorのようなより高度な型も使用できます。
    Nuxt v3.4以降、Nuxtでサポートされていない型に対して独自のレデューサー/リバイバーを定義することが可能です。Nuxt v3.4以降、Nuxtでサポートされていない型に対して独自のレデューサー/リバイバーを定義することが可能です。

    以下の例では、ペイロードプラグインを使用して、Luxon DateTimeクラスのレデューサー(またはシリアライザー)とリバイバー(またはデシリアライザー)を定義しています。LuxonDateTimeクラスのレデューサー(またはシリアライザー)とリバイバー(またはデシリアライザー)をペイロードプラグインを使用して定義します。
    app/plugins/date-time-payload.ts
    /**
     * This kind of plugin runs very early in the Nuxt lifecycle, before we revive the payload.
     * You will not have access to the router or other Nuxt-injected properties.
     *
     * Note that the "DateTime" string is the type identifier and must
     * be the same on both the reducer and the reviver.
     */
    export default definePayloadPlugin((nuxtApp) => {
      definePayloadReducer('DateTime', (value) => {
        return value instanceof DateTime && value.toJSON()
      })
      definePayloadReviver('DateTime', (value) => {
        return DateTime.fromISO(value)
      })
    })
    

isHydrating

nuxtApp.isHydrating (boolean) を使用して、Nuxtアプリがクライアント側でハイドレーション中かどうかを確認します。

app/components/nuxt-error-boundary.ts
export default defineComponent({
  setup (_props, { slots, emit }) {
    const nuxtApp = useNuxtApp()
    onErrorCaptured((err) => {
      if (import.meta.client && !nuxtApp.isHydrating) {
        // ...
      }
    })
  },
})

runWithContext

「Nuxtインスタンスが利用できません」というメッセージが表示されたため、ここにいる可能性が高いでしょう。このメソッドは控えめに使用し、問題を引き起こしている例を報告してください。最終的にはフレームワークレベルで解決される可能性があります。

runWithContextメソッドは、関数を呼び出して明示的なNuxtコンテキストを与えることを目的としています。通常、Nuxtコンテキストは暗黙的に渡され、これについて心配する必要はありません。しかし、ミドルウェア/プラグインで複雑なasync/awaitシナリオを扱う場合、非同期呼び出し後に現在のインスタンスが設定解除されているケースに遭遇することがあります。

app/middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  let user
  try {
    user = await fetchUser()
    // the Vue/Nuxt compiler loses context here because of the try/catch block.
  } catch (e) {
    user = null
  }
  if (!user) {
    // apply the correct Nuxt context to our `navigateTo` call.
    return nuxtApp.runWithContext(() => navigateTo('/auth'))
  }
})

使用方法

const result = nuxtApp.runWithContext(() => functionWithContext())
  • functionWithContext: 現在のNuxtアプリケーションのコンテキストを必要とするすべての関数。このコンテキストは自動的に正しく適用されます。

runWithContextは、functionWithContextが返すものを返します。

コンテキストの詳細な説明

Vue.jsのComposition API(およびNuxtのコンポーザブルも同様に)は、暗黙的なコンテキストに依存して動作します。ライフサイクル中に、Vueは現在のコンポーネントの一時的なインスタンス(およびNuxtのnuxtAppの一時的なインスタンス)をグローバル変数に設定し、同じティックで設定を解除します。サーバーサイドでレンダリングする場合、異なるユーザーからの複数のリクエストとnuxtAppが同じグローバルコンテキストで実行されます。このため、NuxtとVueは、2人のユーザーまたはコンポーネント間で共有された参照が漏れるのを防ぐために、このグローバルインスタンスを直ちに設定解除します。

それはどういう意味ですか?Composition APIとNuxt Composablesは、ライフサイクル中、および非同期操作の前の同じティックでのみ利用可能です。

// --- Vue internal ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---

// Vue / Nuxt sets a global variable referencing to current component in _vueInstance when calling setup()
async function setup () {
  getCurrentInstance() // Works
  await someAsyncOperation() // Vue unsets the context in same tick before async operation!
  getCurrentInstance() // null
}

これに対する古典的な解決策は、const instance = getCurrentInstance()のように最初の呼び出しで現在のインスタンスをローカル変数にキャッシュし、次のコンポーザブル呼び出しで使用することですが、問題は、ネストされたコンポーザブル呼び出しが、コンポジションAPIの暗黙的なコンテキストに依存せず、インスタンスを引数として明示的に受け入れる必要があることです。これはコンポーザブルの設計上の制限であり、それ自体は問題ではありません。

この制限を克服するために、Vueはアプリケーションコードをコンパイルする際に舞台裏で作業を行い、<script setup>の各呼び出し後にコンテキストを復元します。

const __instance = getCurrentInstance() // Generated by Vue compiler
getCurrentInstance() // Works!
await someAsyncOperation() // Vue unsets the context
__restoreInstance(__instance) // Generated by Vue compiler
getCurrentInstance() // Still works!

Vueが実際に何をしているかの詳細な説明については、unjs/unctx#2 (comment).

解決策

ここでrunWithContextを使用して、<script setup>と同様にコンテキストを復元できます。

Nuxtは内部的にunjs/unctxを使用して、プラグインやミドルウェアのVueに類似したコンポーザブルをサポートしています。これにより、navigateTo()のようなコンポーザブルがnuxtAppを直接渡すことなく機能し、Composition APIのDXとパフォーマンスの利点をNuxtフレームワーク全体にもたらします。

NuxtのコンポーザブルはVueのComposition APIと同じ設計であり、そのためこの変換を魔法のように行う同様の解決策が必要です。unjs/unctx#2(提案)、unjs/unctx#4(変換の実装)、およびnuxt/framework#3884(Nuxtへの統合)をご覧ください。

Vueは現在、<script setup>のasync/await使用に対する非同期コンテキストの復元のみをサポートしています。Nuxtでは、defineNuxtPlugin()defineNuxtRouteMiddleware()に対する変換サポートが追加され、これらを使用するとNuxtはコンテキスト復元を伴って自動的に変換します。

残された問題

unjs/unctxのコンテキスト自動復元変換は、awaitを含むtry/catchステートメントでバグがあるようです。これは、上記の回避策の必要性を排除するために最終的に解決される必要があります。

ネイティブ非同期コンテキスト

新しい実験的な機能を使用して、Node.js AsyncLocalStorageと新しいunctxサポートを使用することで、ネイティブ非同期コンテキストサポートを有効にし、変換や手動でのコンテキストの受け渡し/呼び出しなしで、任意のネストされた非同期コンポーザブルに非同期コンテキストをネイティブに利用可能にすることが可能です。Node.js AsyncLocalStorageと、変換や手動でのコンテキストの受け渡し/呼び出しなしで、任意のネストされた非同期コンポーザブルに非同期コンテキストをネイティブに利用可能にするための新しいunctxサポートを使用します。

ネイティブ非同期コンテキストサポートは現在、BunとNodeで動作します。
Docs > 4 X > Guide > Going Further > Experimental Features#asynccontextで詳細をご覧ください。

tryUseNuxtApp

この関数はuseNuxtAppと全く同じように動作しますが、コンテキストが利用できない場合に例外をスローする代わりにnullを返します。

nuxtAppを必要としないコンポーザブルの場合、または例外なしでコンテキストが利用可能かどうかを単に確認する場合に使用できます。

使用例

composable.ts
export function useStandType () {
  // Always works on the client
  if (tryUseNuxtApp()) {
    return useRuntimeConfig().public.STAND_TYPE
  } else {
    return process.env.STAND_TYPE
  }
}