アップグレードガイド

最新の Nuxt バージョンにアップグレードする方法を学びます。

Nuxt のアップグレード

最新リリース

Nuxt を最新リリースにアップグレードするには最新リリースnuxt upgrade コマンドを使用します。

npx nuxt upgrade

Nightly リリースチャンネル

最新の Nuxt ビルドを使用し、リリース前の機能をテストするには、ナイトリーリリースチャンネル ガイドをお読みください。

Nuxt 4 への移行

Nuxt 4 には、大幅な改善と変更が含まれています。このガイドでは、既存の Nuxt 3 アプリケーションを Nuxt 4 に移行する方法を説明します。

まず、Nuxt 4 にアップグレードする

npm install nuxt@^4.0.0

アップグレード後、ほとんどの Nuxt 4 の動作がデフォルトになります。ただし、移行中に下位互換性を維持する必要がある場合は、一部の機能を構成できます。

次のセクションでは、Nuxt 4 へのアップグレードに必要な主な変更点と移行について詳しく説明します。

破壊的変更または重要な変更は、移行手順と利用可能な構成オプションとともに以下に記載されています。

Codemods を使用した移行

アップグレードプロセスを容易にするため、私たちはCodemodチームと協力して、いくつかのオープンソースの codemods で多くの移行手順を自動化しました。

問題が発生した場合は、npx codemod feedback 🙏 で Codemod チームに報告してください。

Nuxt 4 codemods の完全なリスト、各 codemod の詳細情報、ソース、および実行方法については、Codemod Registry.

にアクセスしてください。このガイドで言及されているすべての codemods は、次の codemod レシピを使用して実行できます。

npx codemod@latest nuxt/4/migration-recipe

このコマンドは、実行したくないものを選択解除するオプションとともに、すべての codemods を順番に実行します。各 codemod は、それぞれの変更とともに以下にリストされ、個別に実行することもできます。

新しいディレクトリ構造

🚦 影響レベル: 大

Nuxt はデフォルトで新しいディレクトリ構造を使用するようになりました。下位互換性もあります(Nuxt が古い構造を使用していることを検出した場合、たとえばトップレベルの app/pages/ ディレクトリを使用している場合、この新しい構造は適用されません)。

👉完全な RFC を参照

変更点

  • 新しい Nuxt のデフォルト srcDir はデフォルトで app/ になり、ほとんどのものがそこから解決されます。
  • serverDir は、<srcDir>/server ではなく、デフォルトで <rootDir>/server になります。
  • layers/modules/public/ は、デフォルトで <rootDir> から解決されます。
  • 使用している場合Nuxt Content v2.13+content/<rootDir> から解決されます。
  • 新しい dir.app が追加されました。これは、router.options.ts および spa-loading-template.html を検索するディレクトリです。これはデフォルトで <srcDir>/ になります。
v4 のフォルダ構造の例。
.output/
.nuxt/
app/
  assets/
  components/
  composables/
  layouts/
  middleware/
  pages/
  plugins/
  utils/
  app.config.ts
  app.vue
  router.options.ts
content/
layers/
modules/
node_modules/
public/
shared/
server/
  api/
  middleware/
  plugins/
  routes/
  utils/
nuxt.config.ts
この新しい構造では、~ エイリアスはデフォルトで app/ ディレクトリ(srcDir)を指すようになります。これは、~/componentsapp/components/ に、~/pagesapp/pages/ に解決されることを意味します。

👉 詳細については、この変更を実装した PR.

変更理由

  1. パフォーマンス - すべてのコードをリポジトリのルートに配置すると、.git/ および node_modules/ フォルダが FS ウォッチャーによってスキャン/含まれる問題が発生し、非 Mac OS での起動が大幅に遅れる可能性があります。
  2. IDE の型安全性 - server/ とアプリの残りの部分は、利用可能なグローバルインポートが異なる 2 つのまったく異なるコンテキストで実行されており、server/ がアプリの残りの部分と同じフォルダ内にないことを確認することが、IDE での適切な自動補完を保証するための大きな第一歩となります。

移行手順

  1. app/ という名前の新しいディレクトリを作成します。
  2. あなたの assets/components/composables/app/layouts/app/middleware/app/pages/app/plugins/utils/ フォルダをその下に移動し、app.vueerror.vueapp.config.ts も移動します。app/router-options.ts または app/spa-loading-template.html がある場合、これらのパスは変更されません。
  3. あなたの nuxt.config.tscontent/layers/modules/public/server/ フォルダは app/ フォルダの外、プロジェクトのルートに配置してください。
  4. 新しいディレクトリ構造に合わせて、tailwindcsseslint の構成ファイルなど、サードパーティの構成ファイルを更新してください(必要であれば - @nuxtjs/tailwindcsstailwindcss を正しく自動構成するはずです)。
この移行は、npx codemod@latest nuxt/4/file-structure を実行することで自動化できます。

ただし、移行は必須ではありません。現在のフォルダ構造を維持したい場合は、Nuxt が自動的に検出するはずです。(検出されない場合は、問題を報告してください。)唯一の例外は、srcDir を既にカスタム設定している場合です。この場合、modules/public/server/ フォルダがカスタム srcDir ではなく rootDir から解決されることを認識しておく必要があります。必要に応じて、dir.modulesdir.publicserverDir を構成することでこれをオーバーライドできます。

v3 のフォルダ構造を強制することも、次の構成で可能です。

nuxt.config.ts
export default defineNuxtConfig({
  // This reverts the new srcDir default from `app` back to your root directory
  srcDir: '.',
  // This specifies the directory prefix for `router.options.ts` and `spa-loading-template.html`
  dir: {
    app: 'app',
  },
})

シングルトンデータフェッチレイヤー

🚦 影響レベル: 中

変更点

Nuxt のデータフェッチシステム(useAsyncData および useFetch)は、パフォーマンスと一貫性を向上させるために大幅に再編成されました。

  1. 同じキーに対する共有 ref: 同じキーを持つ useAsyncData または useFetch へのすべての呼び出しは、同じ dataerrorstatus ref を共有するようになりました。これは、明示的なキーを持つすべての呼び出しで、deeptransformpickgetCachedData、または default オプションが競合しないことが重要であることを意味します。
  2. getCachedData の制御強化: getCachedData 関数は、ウォッチャーによる呼び出しや refreshNuxtData の呼び出しによって発生する場合でも、データがフェッチされるたびに呼び出されるようになりました。(以前は、新しいデータは常にフェッチされ、これらのケースではこの関数は呼び出されませんでした。)キャッシュされたデータを使用するタイミングと再フェッチするタイミングをより細かく制御できるように、この関数は現在、リクエストの原因を含むコンテキストオブジェクトを受け取ります。
  3. リアクティブキーのサポート: 計算された ref、プレーンな ref、またはゲッター関数をキーとして使用できるようになりました。これにより、自動的なデータ再フェッチが可能になり(データを個別に保存します)。
  4. データクリーンアップ: useAsyncData でフェッチされたデータを使用する最後のコンポーネントがアンマウントされると、Nuxt はメモリ使用量の増大を防ぐためにそのデータを削除します。

変更理由

これらの変更は、メモリ使用量を改善し、useAsyncData の呼び出し全体でのローディング状態の一貫性を高めるために行われました。

移行手順

  1. 競合するオプションのチェック: 同じキーを異なるオプションまたはフェッチ関数で使用しているコンポーネントを確認してください。
    // This will now trigger a warning
    const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false })
    const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
    

    明示的なキーを共有する(カスタムオプションを持つ)useAsyncData への呼び出しを独自の composable に抽出することが有益な場合があります。
    app/composables/useUserData.ts
    export function useUserData (userId: string) {
      return useAsyncData(
        `user-${userId}`,
        () => fetchUser(userId),
        {
          deep: true,
          transform: user => ({ ...user, lastAccessed: new Date() }),
        },
      )
    }
    
  2. getCachedData の実装を更新する:
    useAsyncData('key', fetchFunction, {
    -  getCachedData: (key, nuxtApp) => {
    -    return cachedData[key]
    -  }
    +  getCachedData: (key, nuxtApp, ctx) => {
    +    // ctx.cause - can be 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch'
    +    
    +    // Example: Don't use cache on manual refresh
    +    if (ctx.cause === 'refresh:manual') return undefined
    +    
    +    return cachedData[key]
    +  }
    })
    

または、現時点では、次の方法でこの動作を無効にできます。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    granularCachedData: false,
    purgeCachedData: false,
  },
})

レイヤーでのモジュール読み込み順序の修正

🚦 影響レベル: 最小

変更点

Nuxt レイヤー を使用する際のモジュールの読み込み順序が修正されました。以前は、プロジェクトルートのモジュールが、拡張レイヤーのモジュールの前に読み込まれていましたが、これは期待される動作とは逆でした。

モジュールは正しい順序で読み込まれるようになりました

  1. レイヤーモジュールが先(拡張順 - 深いレイヤーが先)
  2. プロジェクトモジュールが最後(優先度最高)

これは両方に影響します

  • nuxt.config.tsmodules 配列で定義されたモジュール
  • modules/ ディレクトリから自動検出されるモジュール

変更理由

この変更により、次のことが保証されます。

  • 拡張レイヤーは、コンシューミングプロジェクトよりも優先度が低くなります。
  • モジュール実行順序は、直感的なレイヤー継承パターンと一致します。
  • モジュール構成とフックは、マルチレイヤーセットアップで期待どおりに機能します。

移行手順

ほとんどのプロジェクトでは変更は不要です。これは、期待される動作に合わせて読み込み順序を修正するためです。

ただし、以前の誤った順序に依存していたプロジェクトでは、次の手順が必要になる場合があります。

  1. モジュールの依存関係を確認する: 特定の読み込み順序に依存するモジュールがあるか確認します。
  2. モジュール構成を調整する: モジュールが誤った順序を回避するように構成されていた場合。
  3. 徹底的にテストする: すべての機能が修正された順序で期待どおりに動作することを確認します。

新しい正しい順序の例

// Layer: my-layer/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['layer-module-1', 'layer-module-2'],
})

// Project: nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./my-layer'],
  modules: ['project-module-1', 'project-module-2'],
})

// Loading order (corrected):
// 1. layer-module-1
// 2. layer-module-2
// 3. project-module-1 (can override layer modules)
// 4. project-module-2 (can override layer modules)

モジュールの順序依存関係で問題が発生し、フックを登録する必要がある場合は、すべての他のモジュールが読み込まれた後に実行される modules:done フックの使用を検討してください。これは安全に使用できます。

👉 参照PR #31507GlobalComponentsissue #25719詳細については。

ルートメタデータの重複排除

🚦 影響レベル: 最小

変更点

definePageMeta を使用して、namepath などのルートメタデータを設定できます。以前は、これらはルートとルートメタデータの両方で利用可能でした(たとえば、route.nameroute.meta.name)。

現在、これらはルートオブジェクトからのみアクセスできます。

変更理由

experimental.scanPageMeta をデフォルトで有効にした結果であり、パフォーマンスの最適化です。

移行手順

移行は簡単であるはずです。

  const route = useRoute()
  
- console.log(route.meta.name)
+ console.log(route.name)

コンポーネント名の正規化

🚦 影響レベル: 中

Vue は、Nuxt のコンポーネント命名パターンに一致するコンポーネント名を生成するようになりました。

変更点

デフォルトでは、手動で設定していない場合、Vue はコンポーネントのファイル名に一致するコンポーネント名を割り当てます。

ディレクトリ構造
├─ components/
├─── SomeFolder/
├───── MyComponent.vue

この場合、Vue にとってはコンポーネント名は MyComponent になります。<KeepAlive> と一緒に使用したり、Vue DevTools で特定したりするには、この名前を使用する必要があります。

しかし、それを自動インポートするには、SomeFolderMyComponent を使用する必要があります。

この変更により、これら 2 つの値は一致し、Vue は Nuxt のコンポーネント命名パターンに一致するコンポーネント名を生成します。

移行手順

@vue/test-utilsfindComponent を使用するテストや、コンポーネント名に依存する <KeepAlive> で更新された名前を使用するようにしてください。

または、現時点では、次の方法でこの動作を無効にできます。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    normalizeComponentNames: false,
  },
})

Unhead v2

🚦 影響レベル: 最小

変更点

Unhead<head> タグを生成するために使用されるものがバージョン 2 に更新されました。ほとんど互換性がありますが、低レベル API にはいくつかの破壊的変更が含まれています。

  • 削除されたプロップ: vmid, hid, children, body
  • Promise 入力はサポートされなくなりました。
  • タグはデフォルトで Capo.js を使用してソートされるようになりました。

移行手順

上記の変更は、アプリへの影響は最小限であるはずです。

問題が発生した場合は、次のことを確認してください。

  • 削除されたプロップを使用していない。
useHead({
  meta: [{ 
    name: 'description', 
    // meta tags don't need a vmid, or a key    
-   vmid: 'description' 
-   hid: 'description'
  }]
})
import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup () {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})

必須ではありませんが、@unhead/vue からのインポートを #imports または nuxt/app に更新することが推奨されます。

-import { useHead } from '@unhead/vue'
+import { useHead } from '#imports'

それでも問題が発生する場合は、head.legacy 設定を有効にすることで v1 の動作に戻すことができます。

export default defineNuxtConfig({
  unhead: {
    legacy: true,
  },
})

SPA ローディング画面の新しい DOM 位置

🚦 影響レベル: 最小

変更点

クライアントオンリーページ(ssr: false)をレンダリングする場合、Nuxt アプリのルート内でローディング画面(~/app/spa-loading-template.html から - Nuxt 4 では ~/spa-loading-template.html に変更されたことにも注意してください)をオプションでレンダリングします。

<div id="__nuxt">
  <!-- spa loading template -->
</div>

現在、テンプレートを Nuxt アプリのルートと並べてレンダリングすることをデフォルトとしています。

<div id="__nuxt"></div>
<!-- spa loading template -->

変更理由

これにより、SPA ローディングテンプレートが Vue アプリのサスペンスが解決されるまで DOM に残り、白い画面のちらつきを防ぐことができます。

移行手順

CSS や document.queryElement で SPA ローディングテンプレートをターゲットにしていた場合、セレクタを更新する必要があります。この目的のために、新しい app.spaLoaderTag および app.spaLoaderAttrs 構成オプションを使用できます。

または、次の方法で以前の動作に戻すことができます。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    spaLoadingTemplateLocation: 'within',
  },
})

解析された error.data

🚦 影響レベル: 最小

data プロパティを持つエラーをスローすることは可能でしたが、解析されませんでした。現在、それは解析され、error オブジェクトで利用可能になっています。これは修正ですが、以前の動作に依存して手動で解析していた場合は、技術的に破壊的変更です。

移行手順

カスタム error.vue を更新して、error.data の追加解析を削除してください。

  <script setup lang="ts">
  import type { NuxtError } from '#app'

  const props = defineProps({
    error: Object as () => NuxtError
  })

- const data = JSON.parse(error.data)
+ const data = error.data
  </script>

より詳細なインラインスタイル

🚦 影響レベル: 中

Nuxt は、グローバル CSS ではなく、Vue コンポーネントのスタイルのみをインライン化するようになりました。

変更点

以前は、Nuxt はグローバルスタイルを含むすべての CSS をインライン化し、個別の CSS ファイルへの <link> 要素を削除していました。現在、Nuxt は Vue コンポーネント(以前は CSS の個別のチャンクを生成していました)に対してのみこれを行います。これは、ネットワークリクエストを削減する(以前と同様に、初期ロード時にページごとまたはコンポーネントごとの個別の .css ファイルに対するリクエストはありません)ことと、単一のグローバル CSS ファイルのキャッシングを可能にし、初期リクエストのドキュメントダウンロードサイズを削減することのバランスが取れていると考えています。

移行手順

この機能は完全に構成可能であり、inlineStyles: true を設定することで、グローバル CSS とコンポーネントごとの CSS の両方をインライン化する以前の動作に戻すことができます。

nuxt.config.ts
export default defineNuxtConfig({
  features: {
    inlineStyles: true,
  },
})

ページメタのスキャンを解決後に実行

🚦 影響レベル: 最小

変更点

pages:extend フックを呼び出す前に、definePageMeta で定義されたページメタデータ(definePageMeta で定義されたページメタデータ)をスキャンするようになりました。

変更理由

これは、pages:extend で追加したいページのメタデータをスキャンできるようにするためでした。新しい pages:resolved フックで、ページメタデータを変更またはオーバーライドする機会は引き続き提供されます。

移行手順

ページメタデータをオーバーライドしたい場合は、pages:extend ではなく、pages:resolved で行ってください。

  export default defineNuxtConfig({
    hooks: {
-     'pages:extend'(pages) {
+     'pages:resolved'(pages) {
        const myPage = pages.find(page => page.path === '/')
        myPage.meta ||= {}
        myPage.meta.layout = 'overridden-layout'
      }
    }
  })

または、次の方法で以前の動作に戻すことができます。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    scanPageMeta: true,
  },
})

共有プリレンダリングデータ

🚦 影響レベル: 中

変更点

以前は実験的だった、useAsyncData および useFetch の呼び出しからのデータを、異なるページ間で共有する機能を有効にしました。参照元の PR.

変更理由

この機能は、プリレンダリングされたページ間でペイロードデータを自動的に共有します。これは、useAsyncData または useFetch を使用し、異なるページで同じデータをフェッチするサイトをプリレンダリングする際に、大幅なパフォーマンス向上が期待できます。

たとえば、サイトが各ページで useFetch を呼び出す必要がある場合(たとえば、メニューのナビゲーションデータや CMS からのサイト設定を取得するため)、このデータは、それを使用する最初のページをプリレンダリングする際に一度だけフェッチされ、他のページをプリレンダリングする際にキャッシュされて使用されます。

移行手順

データのユニークなキーが常に同じデータに解決できることを確認してください。たとえば、特定のページに関連するデータをフェッチするために useAsyncData を使用している場合、そのデータに一意に一致するキーを指定する必要があります。(useFetch はこれを自動的に行うはずです。)

app/pages/test/[slug].vue
// This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference
// to the data fetched, but Nuxt can't know that because it's not reflected in the key.
const route = useRoute()
const { data } = await useAsyncData(async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})
// Instead, you should use a key that uniquely identifies the data fetched.
const { data } = await useAsyncData(route.params.slug, async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})

または、次の方法でこの機能を無効にできます。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    sharedPrerenderData: false,
  },
})

useAsyncData および useFetch でのデフォルト data および error

🚦 影響レベル: 最小

変更点

useAsyncData から返される data および error オブジェクトは、デフォルトで undefined になります。

変更理由

以前は datanull に初期化されていましたが、clearNuxtDataundefined にリセットされていました。errornull に初期化されていました。この変更は、一貫性を高めるためのものです。

移行手順

data.value または error.valuenull かどうかをチェックしていた場合、これらのチェックを undefined をチェックするように更新できます。

この手順は、npx codemod@latest nuxt/4/default-data-error-value を実行することで自動化できます。

useAsyncData および useFetchrefresh 呼び出し時に dedupe オプションの boolean 値の非推奨化

🚦 影響レベル: 最小

変更点

以前は、refreshdedupe: boolean を渡すことが可能でした。これらは canceltrue)および deferfalse)のエイリアスでした。

app/app.vue
const { refresh } = await useAsyncData(() => Promise.resolve({ message: 'Hello, Nuxt!' }))

async function refreshData () {
  await refresh({ dedupe: true })
}

変更理由

これらのエイリアスは、明確にするために削除されました。

useAsyncData のオプションに dedupe をオプションとして追加した際に問題が発生し、ブール値はであったため削除しました。

refresh({ dedupe: false }) は、この新しいリクエストを優先して既存のリクエストをキャンセルしないことを意味しました。しかし、useAsyncData のオプション内に dedupe: true を渡すと、既存の保留中のリクエストがある場合、新しいリクエストを行わないことを意味します。(参照PR.)

移行手順

移行は簡単であるはずです。

  const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))
  
  async function refreshData () {
-   await refresh({ dedupe: true })
+   await refresh({ dedupe: 'cancel' })

-   await refresh({ dedupe: false })
+   await refresh({ dedupe: 'defer' })
  }
この手順は、npx codemod@latest nuxt/4/deprecated-dedupe-value を実行することで自動化できます。

useAsyncData および useFetchdata をクリアする際にデフォルトを尊重する

🚦 影響レベル: 最小

変更点

useAsyncDatauseAsyncData にカスタム default 値を提供した場合、この値は clear または clearNuxtData を呼び出す際に使用され、単に unset されるのではなくデフォルト値にリセットされます。

変更理由

多くの場合、ユーザーは null/undefined をチェックする必要をなくすために、空の配列のような適切な空の値を設定します。これは、データをリセット/クリアする際に尊重されるべきです。

useAsyncData および useFetch での pending 値の整合性

🚦 影響レベル: 中

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch から返される pending オブジェクトは、status も pending の場合にのみ true となる計算プロパティになりました。

変更点

現在、immediate: false が渡された場合、pending は最初の要求が行われるまで false になります。これは、pending が最初の要求が行われるまで常に true であった以前の動作からの変更です。

変更理由

これにより、pending の意味が、リクエストが進行中の場合に pending となる status プロパティと一致します。

移行手順

pending プロパティに依存している場合、pending が true になるのは status も pending の場合のみという新しい動作を考慮するようにロジックを調整してください。

  <template>
-   <div v-if="!pending">
+   <div v-if="status === 'success'">
      <p>Data: {{ data }}</p>
    </div>
    <div v-else>
      <p>Loading...</p>
    </div>
  </template>
  <script setup lang="ts">
  const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
    immediate: false
  })
  onMounted(() => execute())
  </script>

または、次の方法で一時的に以前の動作に戻すことができます。

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    pendingWhenIdle: true,
  },
})

useAsyncData および useFetch でのキー変更の挙動

🚦 影響レベル: 中

変更点

useAsyncData または useFetch でリアクティブキーを使用する場合、Nuxt はキーが変更されると自動的にデータを再フェッチします。immediate: false が設定されている場合、useAsyncData は、データが一度フェッチされていれば、キーが変更された場合にのみデータをフェッチします。

以前は、useFetch はわずかに異なる動作をしていました。常にキーが変更されたときにデータをフェッチしていました。

現在、useFetchuseAsyncData は一貫して動作し、データが一度フェッチされていれば、キーが変更された場合にのみデータをフェッチします。

変更理由

これにより、useAsyncDatauseFetch の間で一貫した動作が保証され、予期しないフェッチが防止されます。immediate: false を設定している場合、useFetch または useAsyncData でデータがフェッチされないため、refresh または execute を呼び出す必要があります。

移行手順

この変更により、期待される動作は一般的に改善されるはずですが、非即時 useFetch のキーまたはオプションの変更を期待していた場合、最初の呼び出しは手動でトリガーする必要があります。

  const id = ref('123')
  const { data, execute } = await useFetch('/api/test', {
    query: { id },
    immediate: false
  )
+ watch(id, () => execute(), { once: true })

この動作をオプトアウトするには

// Or globally in your Nuxt config
export default defineNuxtConfig({
  experimental: {
    alwaysRunFetchOnKeyChange: true,
  },
})

useAsyncData および useFetch でのシャローデータリアクティビティ

🚦 影響レベル: 最小

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch から返される data オブジェクトは、ref ではなく shallowRef になりました。

変更点

新しいデータがフェッチされると、data に依存するものはすべて、オブジェクト全体が置き換えられるため、引き続きリアクティブになります。しかし、コードがそのデータ構造内のプロパティを変更した場合、アプリでリアクティビティをトリガーしません。

変更理由

これにより、Vue はすべてのプロパティ/配列の変更を監視する必要がないため、深くネストされたオブジェクトと配列に対して大幅なパフォーマンス向上がもたらされます。ほとんどの場合、data は不変であるべきです。

移行手順

ほとんどの場合、移行手順は必要ありませんが、データオブジェクトのリアクティビティに依存している場合は、次の 2 つのオプションがあります。

  1. composable ごとにディープリアクティビティを段階的にオプトインできます。
    - const { data } = useFetch('/api/test')
    + const { data } = useFetch('/api/test', { deep: true })
    
  2. プロジェクト全体でデフォルトの動作を変更できます(推奨されません)。
    nuxt.config.ts
    export default defineNuxtConfig({
      experimental: {
        defaults: {
          useAsyncData: {
            deep: true,
          },
        },
      },
    })
    
必要に応じて、npx codemod@latest nuxt/4/shallow-function-reactivity を実行してこの手順を自動化できます。

builder:watch での絶対パスの監視

🚦 影響レベル: 最小

変更点

Nuxt の builder:watch フックは、プロジェクトの srcDir からの相対パスではなく、絶対パスを発行するようになりました。

変更理由

これにより、srcDir の外にあるパスの監視をサポートでき、レイヤーやその他のより複雑なパターンでのサポートが向上します。

移行手順

既知の Nuxt モジュールでこのフックを使用しているものは、すでにプロアクティブに移行しました。参照issue #25339.

ただし、builder:watch フックを使用するモジュール作成者で、下位/上位互換性を維持したい場合は、次のコードを使用して、Nuxt v3 と Nuxt v4 の両方でコードが同じように機能するようにできます。

+ import { relative, resolve } from 'node:fs'
  // ...
  nuxt.hook('builder:watch', async (event, path) => {
+   path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
    // ...
  })
この手順は、npx codemod@latest nuxt/4/absolute-watch-path を実行することで自動化できます。

window.__NUXT__ オブジェクトの削除

変更点

アプリのハイドレーションが完了した後、グローバルな window.__NUXT__ オブジェクトを削除します。

変更理由

これにより、マルチアプリパターン(#21635)への道が開かれ、Nuxt アプリデータにアクセスするための単一の方法である useNuxtApp() に集中できるようになります。

移行手順

データは引き続き利用可能ですが、useNuxtApp().payload でアクセスできます。

- console.log(window.__NUXT__)
+ console.log(useNuxtApp().payload)

ディレクトリインデックススキャン

🚦 影響レベル: 中

変更点

app/middleware/ フォルダ内の子フォルダも index ファイルをスキャンし、これらはプロジェクトのミドルウェアとしても登録されるようになりました。

変更理由

Nuxt は、app/middleware/ および app/plugins/ を含む多くのフォルダを自動的にスキャンします。

app/plugins/ フォルダ内の子フォルダも index ファイルをスキャンしており、スキャンされたディレクトリ間でこの動作を一貫させたいと考えています。

移行手順

おそらく移行は不要ですが、以前の動作に戻したい場合は、ミドルウェアを除外するフックを追加できます。

export default defineNuxtConfig({
  hooks: {
    'app:resolve' (app) {
      app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
    },
  },
})

テンプレートコンパイルの変更

🚦 影響レベル: 最小

変更点

以前は、Nuxt は lodash/template を使用して、ファイルシステム上のテンプレートを .ejs ファイル形式/構文でコンパイルしていました。

さらに、これらのテンプレート内でのコード生成に使用できるいくつかのテンプレートユーティリティ(serializeimportNameimportSources)を提供していましたが、これらは削除されました。

変更理由

Nuxt v3 では、「仮想」構文に移行し、より柔軟でパフォーマンスの高い getContents() 関数を使用しています。

さらに、lodash/template は一連のセキュリティ問題が発生しています。これらは、実行時ではなくビルド時に信頼されたコードによって使用されるため、Nuxt プロジェクトには実際には適用されませんが、セキュリティ監査には依然として表示されます。また、lodash は大きな依存関係であり、ほとんどのプロジェクトでは使用されていません。

最後に、コードシリアライゼーション関数を Nuxt 内で直接提供するのは理想的ではありません。代わりに、次のようなプロジェクトを維持しています。unjs/knitworkこれらはプロジェクトの依存関係となり、Nuxt 自体のアップグレードを必要とせずに、セキュリティ問題を直接報告/解決できます。

移行手順

EJS 構文を使用するモジュールを更新するための PR を発行しましたが、ご自身で行う必要がある場合は、下位/上位互換性のある 3 つの代替手段があります。

  • getContents() に文字列補間ロジックを直接移動する。
  • 次のようなカスタム関数を使用して置換を処理する。https://github.com/nuxt-modules/color-mode/pull/240.
  • Nuxt ではなく、プロジェクトの依存関係として es-toolkit/compat(lodash template のドロップイン代替)を使用する。
+ import { readFileSync } from 'node:fs'
+ import { template } from 'es-toolkit/compat'
  // ...
  addTemplate({
    fileName: 'appinsights-vue.js'
    options: { /* some options */ },
-   src: resolver.resolve('./runtime/plugin.ejs'),
+   getContents({ options }) {
+     const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
+     return template(contents)({ options })
+   },
  })

最後に、テンプレートユーティリティ(serialize, importName, importSources)を使用している場合、knitwork のユーティリティで次のように置き換えることができます。

import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'

const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"\{(.+)\}"(?=,?$)/gm, r => JSON.parse(r).replace(/^\{(.*)\}$/, '$1'))

const importSources = (sources: string | string[], { lazy = false } = {}) => {
  return toArray(sources).map((src) => {
    if (lazy) {
      return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
    }
    return genImport(src, genSafeVariableName(src))
  }).join('\n')
}

const importName = genSafeVariableName
この手順は、npx codemod@latest nuxt/4/template-compilation-changes を実行することで自動化できます。

デフォルトの TypeScript 構成の変更

🚦 影響レベル: 最小

変更点

compilerOptions.noUncheckedIndexedAccessfalse ではなく true になりました。

変更理由

この変更は、以前の3.12 config updateでデフォルトを改善したことに続くもので、主にTotalTypeScript の推奨事項.

移行手順

に従っています。2 つのアプローチがあります。

  1. アプリで型チェックを実行し、新しいエラーを修正する(推奨)。
  2. nuxt.config.ts で新しいデフォルトをオーバーライドする。
    export default defineNuxtConfig({
      typescript: {
        tsConfig: {
          compilerOptions: {
            noUncheckedIndexedAccess: false,
          },
        },
      },
    })
    

TypeScript 構成の分割

🚦 影響レベル: 最小

変更点

Nuxt は、より良い型チェックエクスペリエンスを提供するために、異なるコンテキストのために別々の TypeScript 構成を生成するようになりました。

  1. 新しい TypeScript 構成ファイル: Nuxt は、追加の TypeScript 構成を生成します。
    • .nuxt/tsconfig.app.json - アプリコード(Vue コンポーネント、composables など)用
    • .nuxt/tsconfig.server.json - サーバーサイドコード(Nitro/server ディレクトリ)用
    • .nuxt/tsconfig.node.json - ビルド時コード(モジュール、nuxt.config.ts など)用
    • .nuxt/tsconfig.shared.json - アプリとサーバーのコンテキスト間で共有されるコード(型や環境に依存しないユーティリティなど)用
    • .nuxt/tsconfig.json - 下位互換性のためのレガシー構成
  2. 下位互換性: .nuxt/tsconfig.json を拡張する既存のプロジェクトは、以前と同様に機能します。
  3. プロジェクト参照のオプトイン: 新しいプロジェクト、またはより良い型チェックを希望するプロジェクトは、TypeScript のプロジェクト参照機能を採用できます。
  4. コンテキスト固有の型チェック: 各コンテキストには、適切なコンパイラオプションと、その特定の環境の include/exclude があります。
  5. 新しい typescript.nodeTsConfig オプション: Node.js ビルド時コードの TypeScript 構成をカスタマイズできるようになりました。

変更理由

この変更は、いくつかの利点をもたらします。

  1. より良い型安全性: 各コンテキスト(アプリ、サーバー、ビルド時)は、コンテキスト固有のグローバルおよび API を使用して適切な型チェックを取得します。
  2. IDE 体験の向上: コードベースのさまざまな部分に対する IntelliSense とエラーレポートが改善されます。
  3. クリーンな分離: サーバーコードはクライアントサイド API を誤って提案せず、その逆も同様です。
  4. パフォーマンス: TypeScript は、適切にスコープされた構成でコードをより効率的にチェックできます。

たとえば、nuxt.config.ts では auto-imports は利用できません(以前は TypeScript によってフラグが立てられませんでした)。そして、IDE は server/ ディレクトリの tsconfig.json によってヒントされた別個のコンテキストを認識していましたが、これは型チェックに反映されませんでした(別途ステップが必要でした)。

移行手順

移行は不要です。既存のプロジェクトは以前と同様に機能します。

ただし、改善された型チェックを活用するには、新しいプロジェクト参照アプローチをオプトインできます。

  1. ルート tsconfig.json を更新して プロジェクト参照を使用する
    {
      "files": [],
      "references": [
        { "path": "./.nuxt/tsconfig.app.json" },
        { "path": "./.nuxt/tsconfig.server.json" },
        { "path": "./.nuxt/tsconfig.shared.json" },
        { "path": "./.nuxt/tsconfig.node.json" }
      ]
    }
    
  2. 手動のサーバー tsconfig.json ファイル(例: server/tsconfig.json)を削除し.nuxt/tsconfig.server.json を拡張していたものを削除します。
  3. 型チェックスクリプトを更新して プロジェクト参照用のビルドフラグを使用する
    - "typecheck": "nuxt prepare && vue-tsc --noEmit"
    + "typecheck": "nuxt prepare && vue-tsc -b --noEmit"
    
  4. すべての型拡張を適切なコンテキストに移動する:
    • アプリコンテキストの型を拡張している場合は、ファイルを app/ ディレクトリに移動します。
    • サーバーコンテキストの型を拡張している場合は、ファイルを server/ ディレクトリに移動します。
    • アプリとサーバー間で共有される 型を拡張している場合は、ファイルを shared/ ディレクトリに移動します。
    app/server/、または shared/ ディレクトリ以外の場所から型を拡張しても、新しいプロジェクト参照設定では機能しません。
  5. Node.js TypeScript オプションを構成する(必要に応じて)
    export default defineNuxtConfig({
      typescript: {
        // Customize app/server TypeScript config
        tsConfig: {
          compilerOptions: {
            strict: true,
          },
        },
        // Customize build-time TypeScript config
        nodeTsConfig: {
          compilerOptions: {
            strict: true,
          },
        },
      },
    })
    
  6. CI/ビルドスクリプトを更新する TypeScript チェックを実行するスクリプトを、新しいプロジェクト参照アプローチを使用するように更新します。

新しい構成は、既存のセットアップの完全な下位互換性を維持しながら、オプトインしたプロジェクトに、より優れた型安全性と IntelliSense を提供します。

実験的機能の削除

🚦 影響レベル: 最小

変更点

Nuxt 4 では、4 つの実験的機能は構成できなくなりました。

  • experimental.treeshakeClientOnlytrue になります(v3.0 以降のデフォルト)。
  • experimental.configSchematrue になります(v3.3 以降のデフォルト)。
  • experimental.polyfillVueUseHeadfalse になります(v3.4 以降のデフォルト)。
  • experimental.respectNoSSRHeaderfalse になります(v3.4 以降のデフォルト)。
  • vite.devBundler は構成できなくなりました。デフォルトで vite-node を使用します。

変更理由

これらのオプションは、しばらくの間現在の値に設定されており、構成可能である必要があるとは考えていません。

移行手順

トップレベルの generate 構成の削除

🚦 影響レベル: 最小

変更点

トップレベルの generate 構成オプションは Nuxt 4 では利用できなくなりました。これにはすべてのプロパティが含まれます。

  • generate.exclude - ルートをプリレンダリングから除外するため
  • generate.routes - プリレンダリングするルートを指定するため

変更理由

トップレベルの generate 構成は、Nuxt 2 からの持ち越しでした。長らく nitro.prerender をサポートしており、Nuxt 3+ でプリレンダリングを構成するための推奨方法です。

移行手順

generate 構成を対応する nitro.prerender オプションに置き換える

export default defineNuxtConfig({
- generate: {
-   exclude: ['/admin', '/private'],
-   routes: ['/sitemap.xml', '/robots.txt']
- }
+ nitro: {
+   prerender: {
+     ignore: ['/admin', '/private'],
+     routes: ['/sitemap.xml', '/robots.txt']
+   }
+ }
})
Nitro のプリレンダリング構成オプションについてさらに詳しく読む。

Nuxt 2 vs. Nuxt 3+

以下の表では、Nuxtの3つのバージョンを簡単に比較しています。

機能 / バージョンNuxt 2Nuxt BridgeNuxt 3+
Vue223
安定性😊 安定😊 安定😊 安定
パフォーマンス🏎 高速✈️ より高速🚀 最速
Nitro Engine
ESM サポート🌙 部分的👍 より良い
TypeScript☑️ オプトイン🚧 部分的
コンポジションAPI🚧 部分的
Options API
コンポーネントの自動インポート
<script setup> 構文🚧 部分的
自動インポート
webpack445
Vite⚠️ 部分的🚧 部分的
Nuxt CLI❌ 旧式✅ nuxt✅ nuxt
静的サイト

Nuxt 2 から Nuxt 3+

移行ガイドでは、Nuxt 2 の機能と Nuxt 3+ の機能をステップバイステップで比較し、現在のアプリケーションを適応させるためのガイダンスを提供しています。

Nuxt 2 から Nuxt Bridge

Nuxt 2 アプリケーションを段階的に Nuxt 3 に移行したい場合は、Nuxt Bridge を使用できます。Nuxt Bridge は、オプトインメカニズムを使用して Nuxt 2 で Nuxt 3+ の機能を使用できる互換性レイヤーです。

Nuxt 2 から Nuxt Bridge への移行