middleware

Nuxt は、特定のルートに移動する前にコードを実行するためのミドルウェアを提供します。

Nuxt は、アプリケーション全体で使用できるカスタマイズ可能なルートミドルウェアフレームワークを提供します。これは、特定のルートに移動する前に実行したいコードを抽出するのに最適です。

ルートミドルウェアには3種類あります

  1. 匿名(またはインライン)ルートミドルウェアは、ページ内で直接定義されます。
  2. app/middleware/ に配置された名前付きルートミドルウェアは、ページで使用されると非同期インポートによって自動的にロードされます。
  3. app/middleware/.global サフィックスを付けて配置されたグローバルルートミドルウェアは、すべてのルート変更で実行されます。

最初の2種類のルートミドルウェアは、definePageMeta で定義できます。

ミドルウェアの名前はケバブケースに正規化されます。myMiddlewaremy-middleware になります。
ルートミドルウェアは、Nuxt アプリの Vue 部分で実行されます。名前は似ていますが、アプリの Nitro サーバー部分で実行される サーバーミドルウェアとは全く異なります。

使用方法

ルートミドルウェアは、現在のルートと次のルートを引数として受け取るナビゲーションガードです。

middleware/my-middleware.ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.id === '1') {
    return abortNavigation()
  }
  // In a real app you would probably not redirect every route to `/`
  // however it is important to check `to.path` before redirecting or you
  // might get an infinite redirect loop
  if (to.path !== '/') {
    return navigateTo('/')
  }
})

Nuxt は、ミドルウェアから直接返せる2つのグローバルなヘルパーを提供します。

  1. navigateTo - 指定されたルートにリダイレクトします
  2. abortNavigation - オプションのエラーメッセージを付けてナビゲーションを中止します。

とは異なりナビゲーションガードvue-router からは3番目の next() 引数は渡されず、リダイレクトまたはルートキャンセルはミドルウェアから値を返すことで処理されます

可能な戻り値は次のとおりです。

  • 何も返さない(単に return するか、全く返さない) - ナビゲーションをブロックせず、存在すれば次のミドルウェア関数に進むか、ルートナビゲーションを完了します。
  • return navigateTo('/') - 指定されたパスにリダイレクトし、リダイレクトコードを302 Foundリダイレクトがサーバー側で発生した場合
  • return navigateTo('/', { redirectCode: 301 }) - 指定されたパスにリダイレクトし、リダイレクトコードを301 Moved Permanentlyリダイレクトがサーバー側で発生した場合
  • return abortNavigation() - 現在のナビゲーションを停止します
  • return abortNavigation(error) - エラーで現在のナビゲーションを拒否します
詳しくは Docs > 4 X > API > Utils > Navigate To をご覧ください。
詳しくは Docs > 4 X > API > Utils > Abort Navigation をご覧ください。
リダイレクトまたはナビゲーションの停止には、上記のヘルパー関数を使用することをお勧めします。他に考えられる戻り値についてはvue-router ドキュメントに記載されていますが、将来的に破壊的な変更がある可能性があります。

ミドルウェアの順序

ミドルウェアは次の順序で実行されます

  1. グローバルミドルウェア
  2. ページで定義されたミドルウェアの順序(配列構文で複数のミドルウェアが宣言されている場合)

たとえば、次のミドルウェアとコンポーネントがあると仮定します。

app/middleware/ ディレクトリ
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
pages/profile.vue
<script setup lang="ts">
definePageMeta({
  middleware: [
    function (to, from) {
      // Custom inline middleware
    },
    'auth',
  ],
})
</script>

ミドルウェアは次の順序で実行されると予想できます。

  1. analytics.global.ts
  2. setup.global.ts
  3. カスタムインラインミドルウェア
  4. auth.ts

グローバルミドルウェアの順序付け

デフォルトでは、グローバルミドルウェアはファイル名に基づいてアルファベット順に実行されます。

ただし、特定の順序を定義したい場合があります。たとえば、最後のシナリオでは、setup.global.tsanalytics.global.ts の前に実行する必要があるかもしれません。その場合は、グローバルミドルウェアに「アルファベット順」の番号をプレフィックスとして付けることをお勧めします。

ディレクトリ構造
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
「アルファベット順」の番号付けに慣れていない場合は、ファイル名が数値ではなく文字列としてソートされることを覚えておいてください。たとえば、10.new.global.ts2.new.global.ts の前に来ます。これが、例で一桁の数字に 0 をプレフィックスとして付けている理由です。

ミドルウェアの実行時期

サイトがサーバーサイドレンダリングまたは生成されている場合、最初のページのミドルウェアは、ページのレンダリング時と、その後クライアントでも再度実行されます。これは、ミドルウェアがブラウザ環境を必要とする場合、たとえば生成されたサイトがあり、応答を積極的にキャッシュする場合、またはローカルストレージから値を読み取りたい場合に必要になることがあります。

ただし、この動作を避けたい場合は、次のようにできます。

middleware/example.ts
export default defineNuxtRouteMiddleware((to) => {
  // skip middleware on server
  if (import.meta.server) {
    return
  }
  // skip middleware on client side entirely
  if (import.meta.client) {
    return
  }
  // or only skip middleware on initial client load
  const nuxtApp = useNuxtApp()
  if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) {
    return
  }
})

これは、サーバー側でミドルウェアでエラーをスローし、エラーページがレンダリングされた場合でも当てはまります。ミドルウェアはブラウザで再度実行されます。

エラーページのレンダリングは完全に別のページロードを意味し、登録されているすべてのミドルウェアが再度実行されます。ミドルウェアでエラーが処理されているかどうかを確認するには、useError を使用できます。

ミドルウェアでのルートへのアクセス

ミドルウェアで次のルートと前のルートにアクセスするには、必ず to および from パラメータを使用してください。このコンテキストでは、useRoute() コンポーザブルの使用は完全に避けてください。ミドルウェアではナビゲーションを中止したり、別のルートにリダイレクトしたりできるため、ミドルウェアには「現在のルート」の概念がありませんuseRoute() コンポーザブルは、このコンテキストでは常に不正確になります。

ミドルウェアで直接呼び出しがなくても、内部で useRoute() を使用するコンポーザブルを呼び出すと、この警告がトリガーされることがあります。これは上記と同じ問題につながるため、ミドルウェアで使用する場合は、ルートを引数として受け入れるように関数を構造化する必要があります。
export default defineNuxtRouteMiddleware((to) => {
  // passing the route to the function to avoid calling `useRoute()` in middleware
  doSomethingWithRoute(to)

  // ❌ this will output a warning and is NOT recommended
  callsRouteInternally()
})

ミドルウェアの動的な追加

グローバルまたは名前付きルートミドルウェアは、addRouteMiddleware() ヘルパー関数を使用して、たとえばプラグイン内から手動で追加できます。

export default defineNuxtPlugin(() => {
  addRouteMiddleware('global-test', () => {
    console.log('this global middleware was added in a plugin and will be run on every route change')
  }, { global: true })

  addRouteMiddleware('named-test', () => {
    console.log('this named middleware was added in a plugin and would override any existing middleware of the same name')
  })
})

ディレクトリ構造
-| middleware/
---| auth.ts

ページファイルで、このルートミドルウェアを参照できます。

<script setup lang="ts">
definePageMeta({
  middleware: ['auth'],
  // or middleware: 'auth'
})
</script>

これで、そのページへのナビゲーションが完了する前に、auth ルートミドルウェアが実行されます。

Docs > 4 X > Examples > Routing > Middleware で実際の例を読み、編集してください。

ビルド時におけるミドルウェアの設定

各ページで definePageMeta を使用する代わりに、pages:extend フック内で名前付きルートミドルウェアを追加できます。

nuxt.config.ts
import type { NuxtPage } from 'nuxt/schema'

export default defineNuxtConfig({
  hooks: {
    'pages:extend' (pages) {
      function setMiddleware (pages: NuxtPage[]) {
        for (const page of pages) {
          if (/* some condition */ Math.random() > 0.5) {
            page.meta ||= {}
            // Note that this will override any middleware set in `definePageMeta` in the page
            page.meta.middleware = ['named']
          }
          if (page.children) {
            setMiddleware(page.children)
          }
        }
      }
      setMiddleware(pages)
    },
  },
})