Nuxt Nation カンファレンス開催!11月12日~13日、ご参加ください。

useNuxtApp

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

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

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によって呼び出される利用可能なランタイムフックについては、ランタイムフックを参照してください。

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は、nuxtAppを通じてアクセスできるグローバルなVue.js アプリケーションインスタンスです。

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

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

ssrContext

ssrContextはサーバーサイドレンダリング中に生成され、サーバー側でのみ使用できます。

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

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

payload

payloadは、サーバー側からクライアント側にデータと状態変数を公開します。サーバー側から渡された後、クライアント側で次のキーが使用できます。

  • serverRendered (ブール値) - レスポンスがサーバーサイドレンダリングされたかどうかを示します。
  • data (オブジェクト) - 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 (オブジェクト) - NuxtでuseStateコンポーザブルを使用して共有状態を設定する場合、この状態データはpayload.state.[name-of-your-state]からアクセスされます。
    plugins/my-plugin.ts
    export const useColor = () => useState<string>('color', () => 'pink')
    
    export default defineNuxtPlugin((nuxtApp) => {
      if (import.meta.server) {
        const color = useColor()
      }
    })
    

    より高度な型、例えばrefreactiveshallowRefshallowReactive、そしてNuxtErrorなども使用できます。
    Nuxt v3.4以降、Nuxtでサポートされていない型の独自のリデューサー/リバイバーを定義できます。
    Alexander Lichterによる、特にクラスに関するペイロードのシリアライズについての動画をご覧ください。

    以下の例では、ペイロードプラグインを使用して、LuxonのDateTimeクラスのリデューサー(またはシリアライザー)とリバイバー(またはデシリアライザー)を定義します。
    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(ブール値)を使用して、Nuxtアプリがクライアント側でハイドレーションしているかどうかを確認します。

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 instance unavailable」というメッセージが表示されたため、ここにたどり着いた可能性があります。このメソッドは控えめに使用し、問題を引き起こしている例を報告してください。そうすることで、最終的にフレームワークレベルで解決できるようになります。

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

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アプリケーションのコンテキストを必要とする関数。このコンテキストは自動的に正しく適用されます。

runWithContextfunctionWithContextが返すものを返します。

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

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

これは何を意味するのでしょうか?Composition APIとNuxtコンポーザブルは、ライフサイクル中、そして非同期操作の前に同じティックでのみ使用できます。

// --- 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()のようなローカル変数に最初の呼び出し時に現在のインスタンスをキャッシュし、次のコンポーザブル呼び出しで使用することですが、問題は、ネストされたコンポーザブル呼び出しが、composition-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と同様のコンポーザブルをサポートしています。これにより、nuxtAppを直接渡すことなくnavigateTo()のようなコンポーザブルが機能するようになり、Composition APIのDXとパフォーマンスの利点をNuxtフレームワーク全体にもたらします。

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

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

残りの問題

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

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

Node.jsのAsyncLocalStorageと新しいunctxサポートを使用して、実験的な新機能を使用することで、変換やコンテキストの明示的な受け渡し/呼び出しを必要とせずに、ネイティブに**任意のネストされた非同期コンポーザブル**で非同期コンテキストを使用できます。

ネイティブ非同期コンテキストサポートは現在、BunとNodeで動作します。
ドキュメント > ガイド > 詳細 > 実験的機能#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
  }
}