はじめに
Nuxt3 の EOL が直近に迫ってきているので、Nuxt4.0.0 以降のリリースノートの中で発表された内容のうち、押さえておくべき個人的な備忘録をまとめたいと思います。
もちろん、今回記載した内容が全てではなく、公式ドキュメントや移行ガイドも非常に充実していますので、そちらも合わせて参照してください。
変更点(v4.0.0 のリリースノート)
まず、v4.0.0 のリリースノートから抜粋をして、主要な変更点を確認します。
- Cleaner project organization with the new app/ directory structure
- Smarter data fetching - we've taken the opportunity to address some inconsistencies and improve performance with the data layer
- Better TypeScript support with project-based separation between the different contexts in your project - app code, server code, shared/ folder, and configuration
- Faster CLI and development with adoption of internal sockets and a faster CLI
(翻訳)- 新しいapp/ディレクトリ構造によるプロジェクト構成の整理
- データ取得の最適化 - データレイヤーの不整合解消とパフォーマンス向上を実現
- プロジェクト内の異なるコンテキスト(アプリコード、サーバーコード、shared/フォルダ、設定)をプロジェクト単位で分離したTypeScriptサポートの強化
- 内部ソケットの採用と高速化されたCLIによるCLIと開発の高速化
前提: Nuxt3 からの安全な移行(Opt-in)
Nuxt 4 への移行における最大の特徴は、「Nuxt3 のまま Nuxt4 の機能を Opt-in(有効化)できる」点です。
nuxt.config.ts に compatibilityVersion: 4 を設定することで、Nuxt 3 プロジェクト内で Nuxt 4 の挙動を有効化できます。
下記のように、特定の機能を段階的に有効化・無効化しながら、Nuxt 4 への移行を進めることが可能です。
export default defineNuxtConfig({
future: {
compatibilityVersion: 4,
},
experimental: {
sharedPrerenderData: false,
}
})
ディレクトリ構造の変更
Nuxt3
v3.20.2 までのデフォルトのディレクトリ構造は以下の通りです。
root/
├── .nuxt/
├── .output/
├── assets/
├── components/
├── composables/
├── content/
├── layouts/
├── middleware/
├── modules/
├── node_modules/
├── pages/
├── plugins/
├── public/
├── server/
├── shared/
├── utils/
├── .env/
├── .gitignore
├── .nuxtignore
├── .nuxtrc
├── app.vue
├── app.config.ts
├── error.vue
├── nuxt.config.ts
├── package.json
└── tsconfig.json
Nuxt4
v4.2.2 現在のデフォルトのディレクトリ構造は以下の通りです。
Nuxt4 では、デフォルトのソースディレクトリが <rootDir> から app/ に変更されます。
root/
├── .nuxt/
├── .output/
├── app/
│ ├── assets/
│ ├── components/
│ ├── composables/
│ ├── layouts/
│ ├── middleware/
│ ├── pages/
│ ├── plugins/
│ ├── utils/
│ ├── app.vue
│ ├── app.config.ts
│ ├── error.vue
├── content/
├── modules/
├── node_modules/
├── public/
├── server/
├── shared/
├── .env/
├── .gitignore
├── .nuxtignore
├── .nuxtrc
├── nuxt.config.ts
├── package.json
└── tsconfig.json
ただ、移行は必須ではなく、設定により従来のディレクトリ構造を維持することも可能です。
下記のように、nuxt.config.ts で srcDir に . を指定します。
※ . はプロジェクトルートのこと
export default defineNuxtConfig({
srcDir: '.'
})
データ取得(useAsyncData / useFetch)の変更
API 通信などで利用される useAsyncData や useFetch には、重要な変更が2点あります。
デフォルト値が undefined に変更
これまで data や error の初期値は null でしたが、Nuxt4 からは undefined に統一されます。
Nuxt3
型定義を抜粋
export function useFetch<DataT, ErrorT> (
url: string | Request | Ref<string | Request> | (() => string | Request),
options?: UseFetchOptions<DataT>,
): Promise<AsyncData<DataT, ErrorT>>
-- 中略 --
export function useAsyncData<DataT, DataE> (
handler: AsyncDataHandler<DataT>,
options?: AsyncDataOptions<DataT>,
): AsyncData<DataT, DataE>
export function useAsyncData<DataT, DataE> (
key: MaybeRefOrGetter<string>,
handler: AsyncDataHandler<DataT>,
options?: AsyncDataOptions<DataT>,
): Promise<AsyncData<DataT, DataE>>
type AsyncData<DataT, ErrorT> = {
data: Ref<DataT | null>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
clear: () => void
error: Ref<ErrorT | null>
status: Ref<AsyncDataRequestStatus>
pending: Ref<boolean>
}
Nuxt4
型定義を抜粋
export function useFetch<DataT, ErrorT> (
url: string | Request | Ref<string | Request> | (() => string | Request),
options?: UseFetchOptions<DataT>,
): Promise<AsyncData<DataT, ErrorT>>
-- 中略 --
export function useAsyncData<DataT, DataE> (
handler: AsyncDataHandler<DataT>,
options?: AsyncDataOptions<DataT>,
): AsyncData<DataT, DataE>
export function useAsyncData<DataT, DataE> (
key: MaybeRefOrGetter<string>,
handler: AsyncDataHandler<DataT>,
options?: AsyncDataOptions<DataT>,
): Promise<AsyncData<DataT, DataE>>
type AsyncData<DataT, ErrorT> = {
data: Ref<DataT | undefined>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
clear: () => void
error: Ref<ErrorT | undefined>
status: Ref<AsyncDataRequestStatus>
pending: Ref<boolean>
}
戻り値の data が shallowRef になる
useFetch や useAsyncData が返す data オブジェクトは、従来の ref (deep reactive) ではなく shallowRef になります。
※ shallowRef とは、オブジェクトの参照自体(.value の置き換え)のみを検知し、ネストされたプロパティの変更には反応しない「浅い」リアクティブ参照です。
目的としては、パフォーマンスの向上と不要な再レンダリングの抑制です。
もし従来の挙動(深いリアクティブ)が必要な場合は、オプションで deep: true を指定します。
const { data } = await useAsyncData(async () => {
return { userName: '...' }
},
{ deep: true }
)
TypeScript サポートの強化
これまでディレクトリごとに tsconfig.json を分けて型定義を行っていました。
しかし、Nuxt4 では app/, shared/、/node、server/ ごとに TypeScript の設定ファイルが .nuxt/tsconfig.*.json として自動生成されるようになります。
これにより、root/tsconfig.json でそれぞれの tsconfig を参照することができ、よりシンプルに管理できるようになります。
{
"files": [],
"references": [
{
"path": "./.nuxt/tsconfig.app.json"
},
{
"path": "./.nuxt/tsconfig.server.json"
},
{
"path": "./.nuxt/tsconfig.shared.json"
},
{
"path": "./.nuxt/tsconfig.node.json"
}
]
}
変更点(v4.1.0 以降のリリースノート)
実験的な Rolldown サポート
Rust 製の高速ビルドツールである Rolldown の利用が可能になりました。
プロジェクトで試したい場合は、下記のように Vite を rolldown 版に上書きします。
※ yarn の場合
{
"resolutions": {
"vite": "npm:rolldown-vite@latest"
}
}
データ取得の中止制御が追加
useFetch や useAsyncData でデータ取得を中止するための abortController オプションが追加されました。
具体的には、AbortController インスタンスを生成し、signal プロパティを useFetch や useAsyncData のオプションに渡すことで、データ取得の中止が可能になります。
オプションを渡すことで
- reflesh()
- データの再取得を行う際に、前回のリクエスト(未完了の場合)を中止できる
- clear()
- データを消去しつつ、現在進行中のリクエストを中止できる
<script setup lang="ts">
const controller = new AbortController()
const { data, error, clear, refresh } = await useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', {
signal
}))
refresh()
clear()
</script>
これにより、これまで発生していた無駄な API リクエストを削減でき、パフォーマンスの向上が期待できます。
エラーページの変更
開発モードでエラーが発生した際、自作のエラーページ(error.vue)を描画した上で、その上に開発者用のエラー画面が表示されるようになりました。
具体的な、画面イメージは、以下のリリースノート - Better Error Pages in Development をご確認ください。
Vite Environment API の実験的サポート (Vite 6)
⚠️ Nuxt5 での正式サポートに向けた機能
Vite 6 で導入される「Environment API」を Nuxt で利用するための実験的オプションが追加されました。
単一の Vite サーバーで処理できるようになるため、開発体験やそもそものパフォーマンスが向上します。
export default defineNuxtConfig({
experimental: {
viteEnvironmentApi: true
}
})
最後に
改めてリリースノートで発表されている内容には、Nuxt5 に向けた機能も含まれており、Nuxt5 も非常に楽しみです。
少しでもどなたかのキャッチアップの一助になれば幸いです。