📝 Nuxt 3 公式ドキュメント 日本語訳
💎 ハイブリッド Vue フレームワーク
Vue 3 で次のアプリケーションを構築し、ハイブリッドレンダリング、パワフルなデータフェッチなどの新機能を体験してください。Nuxt 3 は、Web 開発をシンプルかつパワフルにするオープンソースのフレームワークです。
新機能を搭載
Nuxt 3 は、より小さなコアで再設計され、より速いパフォーマンスとより優れた開発者経験のために最適化されています。
-
🪶 より軽く
- 最新のブラウザで最大 75 倍、サーバーデプロイとクライアントバンドルサイズを小さくしました。
-
🐇 より速く
- nitro(ナイトロと呼ぶらしい)による動的なサーバーのコード分割でコールドスタート(ハードウェアが初期化された状態から再起動すること)を最適化しました。
-
❎ ハイブリッド
soon
- ISG(Incremental Static Generation)などの高度なモードが使用可能になりました。
-
🔷 Suspense
- ナビゲーションの前でも後でも、あらゆるコンポーネントでデータをフェッチできます。
-
♻️Composition API
- Composition API と Nuxt 3 の composables を使用して、真のコードの再利用性を実現します。
-
🧰 Nuxt CLI
- 新しい依存ゼロの体験により、雛形の作成とモジュールの統合が容易になりました。
-
🐞 Nuxt Devtools
soon
- ブラウザ上で情報を確認したり、quick fix を使うこどより速く作業ができます。
-
🛠️ Nuxt Kit
- TypeScript とクロスバージョンの互換性による全く新しいモジュール開発を確立します。
-
⏬ Webpack 5
- 設定なしで、ビルド時間の短縮とバンドルサイズの縮小を実現しました。
-
⚡ Vite
- Vite をパンドラとして使用することで、光速の HMR(Hot Module Replacement、画面の再描画なしに JS の変更をブラウザに適用する機能)を体験してください。
-
💚 Vue 3
- Vue 3 は、あなたの次の web アプリケーションのための強固な基盤です。
-
🕵️ TypeScript
- ネイティブな TypeScript と ESM で構築されているため、余計な手順は必要ありません。
⚙️ Nitro エンジン
私たちは、Nuxt の新しいサーバーエンジンである Nitro の開発に 9 ヶ月間取り組みました。Nitro は、Nuxt サーバーとそれを超える新しい フルスタック機能 を解放します。
開発環境では、Rollup と Node.js workers をサーバーのコードとコンテキストの分離に使用します。また、server/api
にあるファイルと server/middleware
にある サーバーミドルウェア を読み込んで、サーバー API を生成します 。
本番環境では、アプリとサーバーをひとつのユニバーサルな .output
ディレクトリに構築します。この 出力は軽量 で、最小化され、あらゆる Node.js モジュール(polyfills を除く)から削除されています。この出力は、Node.js、サーバーレス、Workers、Edge Side Rendering、純粋な静的レンダリングなど、JavaScript をサポートするあらゆるシステムでデプロイすることが可能です。
出力はランタイムコードと組み合わされ、あらゆる環境で Nuxt サーバーを実行し(実験的なブラウザの Service Workers を含む!)、静的ファイルを提供でき、JAMStack のための 真のハイブリッドフレームワーク となります。さらに、ネイティブストレージレイヤーが実装されており、マルチソース、ドライバー、ローカルアセットをサポートしています。
Nitro サーバーの基盤は、rollup と h3 で、高いパフォーマンスと移植性のために構築された最小限の http フレームワークです。
🌉 Nuxt Bridge
私たちは、Vue 3
に移行し、4 年間の開発期間を経て Nuxt を書き直し、将来に向けて強固な基盤にしました。
Nuxt 3 へのスムーズなアップグレード
Nuxt 2 と Nuxt 3 間のアップグレードをできるだけ簡単に行えるようにしました。
- ✅ レガシーなプラグインやモジュールは引き続き動作します
- ✅ Nuxt 2 の構成と互換性あり
- ✅ 部分ページオプション API が利用可能
既存の Nuxt 2 プロジェクトに Nuxt 3 の経験を取り入れる
Nuxt 3 の新機能を開発するにあたり、その一部を Nuxt 2 にバックポートしました。
- ✅
Nitro サーバー
と Nuxt 2 の併用 - ✅ Nuxt 2 で Nuxt 3 と同じ Composition API を使用
- ✅ Nuxt 2 で新しい CLI と Devtools を使用
- ✅ Nuxt 3 への段階的なアップグレード
- ✅ Nuxt 2 のモジュールエコシステムとの互換性
- ✅ 少しずつアップグレード(Nitro、Composition API、Nuxt Kit)
はじめかた
はじめに
Nuxt 3 を使いはじめるのは簡単です。
Nuxt ってなに?
Nuxt について初めて学ぶ方、Nuxt 3 についてもっとよく知りたい方は、まず コンセプトセクション
を読むことをお勧めします。
前提条件
はじめる前に、推奨されるセットアップがインストールされていることを確認してください。
-
Node.js* (最新 LTS 版) 👉
ダウンロード
-
Visual Studio Code 👉
ダウンロード
-
Volar Extension 👉
ダウンロード
-
Take Over Mode
を有効にする(推奨) - ... または TypeScript Vue Plugin(Volar) を追加 👉
ダウンロード
-
- すでに Node.js をインストールしている場合は、
node --version
でv14
またはv16
を使用していることを確認してください。
export default defineNuxtConfig({
typescript: {
shim: false
}
})
Nuxt 3 か Bridge か?
次に、ゼロからはじめるか、既存の Nuxt 2 プロジェクトをアップグレードするかを決定します。
新しい Nuxt プロジェクトを開始する
- ℹ️ Vue 3 を使って楽しむ
- ℹ️ すべての新しい composables を使用可能
- ℹ️ 新しいテンプレートシステムと規約が有効です
Nuxt 2 プロジェクトの移行
既存の Nuxt 2 プロジェクトをお持ちの場合は、まず Nuxt Bridge を使用することを 強くお勧めします 。この方法では、変更を最小限に抑えながら、ほとんどの新機能を試すことができます。
- ℹ️ リスクはありません!いつでも設定からモジュールを削除することができます
- ℹ️ あなたのプロジェクトを Nuxt 3 にほぼ対応させることができます
- ℹ️ Vue 3 用に大きく書き換えることなく、新しい開発者体験の改良を楽しむことができます
- ℹ️ プラットフォームに依存しない最適化されたデプロイメントのための Nitro エンジンの使用
- ℹ️ Nuxt 3 の安定化と欠陥の発見に貢献する
- ℹ️ Nuxt Bridge は、現時点では Nuxt 3 よりも安定しています
比較
以下の表は、Nuxt の 3 つのバージョンを簡単に比較したものです。
特徴/バージョン | Nuxt 2 | Nuxt Bridge | Nuxt 3 |
---|---|---|---|
Vue | 2 | 2 | 3 |
安定性 | 😊 安定してる | 😌 ちょっと安定 | 😬 安定してない |
パフォーマンス | 🏎 速い | ✈️ より速い | 🚀 もっとも速い |
Nitro エンジン | ❌ | ✅ | ✅ |
ESM サポート | 🌙 部分的 | 👍 ベター | ✅ |
TypeScript | ☑️ オプトイン | 🚧 部分的 | ✅ |
Composition API | ❌ | 🚧 部分的 | ✅ |
Options API | ✅ | ✅ | ✅ |
Components Auto Import | ✅ | ✅ | ✅ |
<script setup> 構文 | ❌ | 🚧 部分的 | ✅ |
Auto Imports | ❌ | ✅ | ✅ |
Webpack | 4 | 4 | 5 |
Vite | ⚠️ 部分的 | 🚧 部分的 | 🚧 実験的 |
Nuxt CLI | ❌ 古い | ✅ nuxti | ✅ nuxti |
静的サイト | ✅ | ✅ | 🚧 |
インストール
Nuxt 3 を使いはじめるのは簡単です。
オンラインで試す
Nuxt 3 はオンラインの sandbox などを使用してブラウザから使いはじめることができます。
新しいプロジェクト
ターミナルを開くか、Visual Studio Code
から 統合ターミナル
を開き、次のコマンドを使用して新しいスタータープロジェクトを作成します:
npx nuxi init nuxt3-app
or
yarn dlx nuxi init nuxt3-app
or
pnpm dlx nuxi init nuxt3-app
visual studio code で nuxt3-app
フォルダを開いてください:
code nuxt3-app
依存関係をインストールします:
yarn install
or
npm install
or
pnpm install --shamefully-hoist
開発サーバー
これで、yarn dev
を使って開発モードで nuxt アプリを起動できるようになります:
yarn dev -o
or
npm run dev -- -o
or
pnpm run dev -- -o
次のステップ
これで Nuxt 3 プロジェクトが作成できたので、アプリケーションの構築を開始する準備ができました。
-
コンセプト
について学ぶ -
使用方法
について詳しく知る
Bridge
既存の Nuxt 2 のプロジェクトで Nuxt 3 の機能を体験する。
Bridge は、Nuxt モジュールをインストールして有効にするだけで、Nuxt 3 の新機能の多くを体験できる前方互換性のあるレイヤーです。
Nuxt Bridge を使用することで、プロジェクトを Nuxt 3 に(ほぼ)対応させることができ、大幅な書き換えや変更を加えるリスクなしに最高の開発者体験を得ることができます。
Nuxt 2 をアップグレード
開発サーバー(nuxt dev
)が起動していないことを確認し、package lock ファイル(package-lock.json
と yarn.lock
)を削除し、最新の nuxt-edge
をインストールしてください:
- "nuxt": "^2.15.0"
+ "nuxt-edge": "latest"
そのあと、依存関係を再インストールしてください:
yarn install
or
npm install
Nuxt Bridge をインストール
開発依存として @nuxt/bridge-edge
をインストールします:
yarn add --dev @nuxt/bridge@npm:@nuxt/bridge-edge
or
npm install -D @nuxt/bridge@npm:@nuxt/bridge-edge
scripts をアップデート
また、Nuxt が Nitro サーバーをビルド出力として生成するようになったことを反映して、package.json
内の scripts を更新する必要があります。
Nuxi
Nuxt 3 では、新しい Nuxt CLI コマンドである nuxi が導入されました。Nuxt Bridge のより良いサポートを利用するために、以下のように scripts を更新してください:
{
"scripts": {
- "dev": "nuxt",
+ "dev": "nuxi dev",
- "build": "nuxt build",
+ "build": "nuxi build",
- "start": "nuxt start",
+ "start": "nuxi preview"
}
}
静的ターゲット
nuxt.config
で target: 'static'
を設定している場合は、ビルド scripts を nuxi generate
に更新することを確認する必要があります。
{
"scripts": {
"build": "nuxi generate"
}
}
サーバーターゲット
それ以外の場合は、nuxi build
コマンドを使用します。
{
"scripts": {
"build": "nuxi build",
"start": "nuxi preview"
}
}
nuxt.config
をアップデート
設定ファイルでは、module.exports
、require
、require.resolve
などの CommonJS の構文を使わないように注意してください。
代わりに、static import
、dynamic import()
、export default
を使用することができます。また、nuxt.config.ts
に名前を変更することで TypeScript を使用することも可能であり、推奨されています。
import { defineNuxtConfig } from '@nuxt/bridge'
export default defineNuxtConfig({
// 設定
})
tsconfig.json
をアップデート
TypeScript を使用している場合、tsconfig.json
を編集することで、自動生成された Nuxt の型を利用することができます。
{
+ "extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
...
}
}
Composition API に移行
以前、@vue/composition-api
や @nuxtjs/composition-api
を使用していた場合は、composition api 移行ガイド
をお読みください。
CommonJS から ESM に移行
Nuxt 3 は TypeScript と ECMAScript Modules をネイティブでサポートしています。詳細とアップグレードについては、Native ES Modules
をご覧ください。
互換性のないモジュールや旧式のモジュールを削除
-
nuxt/content
を削除(1.x)してください。nuxt 3 用の書き換えが予定されています (2.x) -
nuxt-vite
を削除してください。Bridge で同機能を実現します -
nuxt/typescript-build
を削除してください。Bridge で同機能を実現します -
nuxt/typescript-runtime
とnuxt-ts
を削除してください。Nuxt 2 はランタイムサポートを内蔵しています -
nuxt/nitro
を削除してください。Bridge は同じ機能を持っています -
vue/composition-api
を依存関係から削除してください (移行ガイド
) -
nuxtjs/composition-api
を依存関係から削除してください(およびnuxt.config
のモジュールから) (移行ガイド
)
ビルドした Nitro フォルダを git から除外
.gitignore
ファイルに .output
フォルダを追加します。
すべてがうまくいくことを確認する
✅ nuxi dev
と nuxi build
(または nuxi generate
)で試してみて、すべてがうまくいくかどうか確認してください。
🐛 何か問題がありますか? issue を作成してお知らせください。また、その間に bridge を無効にすることも簡単にできます。
import { defineNuxtConfig } from '@nuxt/bridge'
export default defineNuxtConfig({
bridge: false // bridge 統合の一時的な無効化
})
新しいプラグインフォーマット(オプション)
Nuxt 2 とは形式が若干異なる Nuxt 3 プラグイン API に移行することができるようになりました。
プラグインは 1 つの引数(nuxtApp
)だけを取るようになりました。詳しくはドキュメント
をご覧ください。
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.provide('injected', () => 'my injected function')
// `nuxtApp.$injected` で利用可能になりました。
})
useMeta
(オプション)
新しい Nuxt Bridge は新しい Nuxt 3 meta API を提供し、新しい useMeta
composable でアクセスすることができます。
<script setup>
import { useMeta } from '#app'
useMeta({
title: 'My Nuxt App',
})
</script>
また、nuxt.config
でこの機能を明示的に有効にする必要があります:
import { defineNuxtConfig } from '@nuxt/bridge'
export default defineNuxtConfig({
bridge: {
meta: true
}
})
この useMeta
composable は、(vue-meta
ではなく) @vueuse/head
を内部に使って <head>
を操作しています。したがって、Nuxt 2 のネイティブな head()
プロパティと useMeta
の両方を使用することは、競合する可能性があるので、推奨されません。
この composable の使い方の詳細は、ドキュメント
を参照してください。
機能フラグ
オプションで bridge からいくつかの機能を無効にしたり、より安定性の低い機能をオプトインすることができます。通常の場合、デフォルトのままにしておくのが一番です!
最新のデフォルトは、bridge/src/module.ts
で確認できます。
import { defineNuxtConfig } from '@nuxt/bridge'
export default defineNuxtConfig({
bridge: {
// -- オプトインの特徴 --
// Webpack 4 の代わりに Vite をバンドラーとして使用する
// vite: true,
// Nuxt 3 互換の useMeta を使用可能にする。
// meta: true,
// -- デフォルトの特徴 --
// Nitro の代わりにレガシーサーバーを使用する
// nitro: false,
// Nuxt 3 互換の `nuxtApp` インターフェースを無効にする
// app: false,
// Composition API サポートを無効にする
// capi: false,
// ... あるいは、従来の Composition API のサポートを無効にする
// capi: {
// legacy: false
// },
// モジュールをトランスパイルしない
// transpile: false,
// <script setup> サポートを無効にする
// scriptSetup: false,
// composables の自動インポートを無効にする
// autoImports: false,
// モジュールの非互換性についての警告を表示しない
// constraints: false
},
vite: {
// Vite 用の設定
}
})
コマンド
Nuxi は Nuxt 3 の新しい CLI 体験です
Nuxt 3 には、開発用サーバーを起動するコマンドと、本番用アセットを作成するコマンドの大きく 2 つがあります。
Nitro サーバー
を導入したことにより、Nuxt 3 は開発依存となったため、package.json
に 2 つのコマンドを追加するだけでいいです:
"scripts": {
"dev": "nuxi dev",
"build": "nuxi build",
}
そして、npm run <command>
または yarn <command>
を使って各コマンドを実行することができます。
開発サーバー
http://localhost:3000
を開いて、ホットモジュール交換による開発モードの Nuxt を起動する:
yarn dev
or
npm run dev
HTTPS https://localhost:3000
(自己署名証明書)を使って開発モードで Nuxt を起動する:
yarn dev --https
or
npm run dev -- --https
実運用に向けたビルド
Nuxt アプリケーションを本番用にビルドするには、以下を実行します:
yarn build
or
npm run build
Nuxt は、アプリケーション、サーバー、および依存関係をすべて含む .output
ディレクトリを作成し、デプロイする準備ができます。Nitro を使用して Nuxt アプリケーションをデプロイする場所と方法については、デプロイメント
のセクションを参照してください。
CLI 用語集
移行
Nuxt 3 への移行ガイドです。作業中です 🚧
Nuxt 2 から Nuxt 3 へ
現時点では、Nuxt 2 から Nuxt 3 への移行ガイドはありませんし、さらなる変更の可能性があるため、移行は推奨されません。できるだけスムーズに移行できるよう、安定した移行ガイドとツールの提供に取り組んでいます。代替ツールについては、Bridge
をご確認ください。
Nuxt 2 から削除された機能、Nuxt 3 にまだ実装されていない機能、Nuxt 3(および Bridge)で新たに追加された機能があります。
Nuxt Bridge の要件以外で、Nuxt 3 における顕著な、または重要な変更点は以下のとおりです:
- ℹ️ Vue アプリのテンプレートを書き直しました
- ℹ️ Vue を
3.x
にアップグレード - ℹ️ 非同期データ取得のために
<Suspense>
を使用 - ℹ️ Webpack 5.x (
vite
を使用しない場合) - ℹ️ コンポーネントの発見機能を書き直しました
- ℹ️ メインである
app.vue
コンポーネントを導入 - ℹ️ 新しい
layouts
システムを導入 - ℹ️
pages/
ディレクトリの規約を変更
下表は、全体的な機能比較のハイライトです。
特徴/バージョン | Nuxt 2 | Nuxt 3 | 必要な変更か | 詳細 |
---|---|---|---|---|
Vue のバージョン | 2 | 3 | はい | |
app.vue |
❌ | ✅ | - | |
Suspense | ❌ | ✅ | - | |
Assets | ✅ | ✅ | いいえ | |
Components | ✅ | ✅ | いいえ | |
Layouts | ✅ | ✅ | はい | |
Middleware | ✅ | ✅ | はい | |
Pages | ✅ | ✅ | はい | |
Pages: Dynamic Params | ✅ | ✅ | はい | |
Pages: _.vue | ✅ | ✅ | はい(新しい名前付けシステム) | |
Plugins | ✅ | ✅ | はい(デフォルトで互換性がある) | |
Transitions | ✅ | ✅ | ? | |
Options API: asyncData
|
✅ | ✅ | はい(defineNuxtComponent を使ってください) |
|
Options API: fetch
|
✅ | ❌ | 代わりに asyncData を使ってください |
|
Error Pages | ✅ | 🚧 | はい | ランタイムエラーへの対応 |
Store | ✅ | 🚧 | はい | グローバルストアをサポート |
Static builds | ✅ | 🚧 | ルートキャッシングルール(ハイブリッド) |
モジュールの互換性
すべての Nuxt 2 モジュールは、bridge に移行する場合、またはすでにガイドラインに従っている場合は、Nuxt 3 と前方互換性があるはずです。
@nuxt/kit
で作成されたすべての(今後の)モジュールは、Nuxt 3 / Bridge 専用の機能に依存していない限り、Nuxt 2 プロジェクトと後方互換性があるべきです(bridge を使用していない場合も)。
プラグイン互換性
ほとんどの Nuxt 2 プラグインは、私たちが導入する魔法の互換レイヤーを使って Nuxt 3 と前方互換性があるはずです。
Nuxt 3 のプラグインは Nuxt 2 との後方互換性がありません。
Vue との互換性
composition API やコンポーネントを使用するプラグインは、Vue 2 または Vue 3 に排他的に対応する必要があります。
vue-demi
を使用することで、Nuxt 2 と 3 の両方に互換性があるはずです。
モジュールの移行
Nuxt 3 ユーザーがあなたのモジュールを追加すると、@nuxt/kit
から互換性のあるモジュールコンテナレイヤーが自動的に導入されるので、あなたのコードが以下のガイドラインに従っている限り、そのまま動作し続けるはずです。
nuxt/bridge
でテストする
@nuxt/bridge
への移行は、Nuxt 3 をサポートするための最初で最も重要なステップです。
モジュール内にフィクスチャやサンプルがある場合、その設定に @nuxt/bridge
パッケージを追加します (例
を参照)。
CommonJS から ESM への移行
Nuxt 3 は TypeScript と ECMAScript Modules をネイティブにサポートしています。詳細とアップグレードについては、Native ES Modules
を参照してください。
プラグインがデフォルトでエクスポートされるようにする
export default
を持たない Nuxt プラグイン(グローバルな Vue プラグインなど)を導入する場合は、export default () => { }
を末尾に追加することを確認してください。
// ~/plugins/vuelidate.js
import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
// ~/plugins/vuelidate.js
import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
export default () => { }
ランタイムモジュールを避ける
Nuxt 3 では、Nuxt はビルド時のみの依存関係になりました。これは、モジュールが Nuxt ランタイムにフックしようとすべきではないことを意味します。
モジュールのニーズは、(モジュールではなく)buildModules
に追加されただけでも動作するはずです。例えば、
- Nuxt モジュール内で
process.env
を更新し、nuxt プラグインで読み込むことは避け、代わりにruntimeConfig
を使用します。 - (*)
vue-renderer:*
のようなランタイムフックに依存した制作は避けましょう。 - (*)
serverMiddleware
をモジュール内部でインポートして追加することは避けてください。代わりに、ファイルパスを参照して追加することで、モジュールのコンテキストに依存しないようにします。
(*) nuxt dev
の目的のみで、if (nuxt.options.dev) { }
でガードされている場合は別です。
TypeScript を使用する(オプション)
必須ではありませんが、Nuxt のエコシステムのほとんどが TypeScript の利用にシフトしていますので、移行を検討されることを強くお勧めします。
ガイド
Nuxt とは?
Nuxt の目標は、優れた開発者体験を念頭に置き、web 開発を直感的かつ高性能にすることです。
なんで Nuxt なの?
Nuxt とは何かを理解するためには、モダンなアプリケーションを作るために何が必要かを理解する必要があります。
- ✅ リアクティビティ(反応性)とウェブコンポーネントをもたらすJavaScript フレームワークである、
Vue.js
を私たちは選びました。 - ✅ 開発中の Hot Module Replacement(画面の再描画なしに JS の変更をブラウザに適用する機能)をサポートし、本番用にコードをバンドルするモジュールバンドラーである、
Webpack 5
とVite
の両方をサポートしています。 - ✅ レガシーな(古い)ブラウザをサポートしながら、最新の JavaScript 構文を記述するためのトランスパイラ(あるプログラミング言語から他のプログラミング言語に変換するもの)である
esbuild
を使用しています。 - ✅ 開発中のアプリケーションを提供するためのサーバーでありながら、サーバーサイドレンダリングや API ルートをサポートする Nuxt はサーバーレス、ワーカー、Node.js などの多様なデプロイと比類のないパフォーマンスのために
h3
を使用しています。 - ✅ クライアントサイドのナビゲーションを処理するためのルーティングライブラリである、
vue-router
を私たちは選びました。
これは氷山の一角です。想像してみてください。プロジェクトにこれらすべてを設定し、動作させ、そして長期間にわたって保守することを。私たちは 2016 年 10 月からこれを行い、あらゆる Vue アプリケーションにベストな最適化とパフォーマンスを提供するために、すべての設定をチューニングしています。
Nuxt がこれらすべてを引き受けます。だからあなたは大事なこと: つまり web アプリケーションをつくること に集中することができます。
この設定に加えて、Nuxt はあなたが設定ではなく、作成に集中できるように、特定の機能に焦点を当てた従うべき ディレクトリ構造
を提供します。
どのように機能するの?
Nuxt は様々な コアパッケージ
で構成されています:
- ℹ️ コアエンジン:
nuxt 3
- ℹ️ バンドラー:
@nuxt/vite-builder
と@nuxt/webpack-builder
- ℹ️ コマンドラインインターフェース:
nuxi
- ℹ️ サーバーエンジン:
@nuxt/nitro
- ℹ️ 開発キット:
@nuxt/kit
- ℹ️ Nuxt 2 Bridge:
@nuxt/bridge
Nuxt の機能、および各パッケージの範囲を完全に把握するために、各コンセプトを読むことをお勧めします。
あなたは Nuxt を使いますか?
Nuxt は、Vue.js プロジェクトの基幹であり、柔軟性を保ちながら自信を持ってプロジェクトを構築するための構造を提供します。
強力なモジュールエコシステムとフックエンジンによって拡張可能で、REST や GraphQL エンドポイント、お気に入りの CMS、CSS フレームワークなどを簡単に接続することができます。PWA とAMP のサポートは、Nuxt のプロジェクトにモジュールを追加するだけです。
あなたは Nuxt を使う勇気をもっていますか?
オープンな issue
に挑戦してみよう。これは実際にコードに飛び込むことで、Nuxt を学ぶ最良の方法なのです。Nuxt をより良くするためのアプローチや代替案をもたらすことさえできるかもしれませんよ!それで、あなたはなにを待っているのですか?さあ、飛び込みましょう!
Vue.js 開発
Nuxt は、フロントエンドフレームワークとして Vue を使用し、コンポーネントの自動インポートやファイルベースのルーティングなどの機能を追加しています。Nuxt 3 は、Vue の新しいメジャーリリースである Vue 3 を統合し、Nuxt ユーザーのための新しいパターンを可能にします。
Vue.js
Nuxtを使用するために Vue の深い知識は必要ありませんが、ドキュメントを読み、
vuejs.org
にあるいくつかの例を見てみることをお勧めします
Nuxt は、フロントエンドフレームワークとして常に Vue を使用しています。私たちが Nuxt を Vue の上に構築することを選択したのは、以下のような理由からです。
- Vue のリアクティビティ(反応的な)・モデルは、データの変化が自動的にインターフェースの変化を引き起こすというものです。
- コンポーネントベースのテンプレートは、HTML をウェブの共通言語として維持しながら、直感的なパターンを可能にし、インターフェイスの一貫性を保ちつつ、パワフルなものにします。
- 小規模なプロジェクトから大規模な web アプリケーションまで、Vue はスケールアップしても優れたパフォーマンスを発揮し続け、アプリケーションがユーザーに価値を提供し続けることを保証します。
Vue と Nuxt
単一ファイルコンポーネント
Vue の単一ファイルコンポーネント(SFC、または *.vue
ファイル)は、Vue コンポーネントのマークアップ(<template>
)、ロジック(<script>
)、スタイル(<style>
)をカプセル化したものです。Nuxt は、シームレスな開発者体験を提供する Hot Module Replacement(画面の再描画無しに JS の変更をブラウザに適用してくれるもの)
により、SFC のためのゼロ設定体験を提供します。
コンポーネントの自動インポート
Nuxt プロジェクトの components/
ディレクトリに作成されたすべての Vue コンポーネントは、インポートしなくてもあなたのプロジェクトで利用できるようになります。コンポーネントがどこにも使用されていない場合、プロダクションのコードには含まれません。
Vue Router
ほとんどのアプリケーションでは、複数のページと、それらの間を移動する方法が必要です。これをルーティングと呼びます。Nuxt は、pages/
ディレクトリと命名規則を使用して、公式の Vue Router ライブラリ
を使用してファイルにマッピングされたルートを直接作成します。
例
app.vue
ファイルは、ブラウザのウィンドウに表示されるページを表すエントリーポイントです。
コンポーネントの <template>
内では、components/
ディレクトリに作成された<Welcome>
コンポーネントをインポートすることなく使用しています。
<template>
の内容をカスタムのウェルカムメッセージに置き換えてみてください。右のブラウザウィンドウは、リロードせずに自動的に変更内容をレンダリングします。
Nuxt 2 または Vue 2 の以前のユーザーであれば、Vue 2 と Vue 3 の違いや、Nuxt がこれらの進化をどのように統合しているかを知るために読み続けてください。
そうでない場合は、次の章で Nuxt のもう一つの重要な機能を発見してください: レンダリングモード
Nuxt 2 / Vue 2 とのちがい
Nuxt 3 は、Vue 3 をベースにしています。新しい Vue のメジャーバージョンでは、Nuxt が活用できるいくつかの変更点が導入されています:
- パフォーマンスの向上
- Composition API
- TypeScript サポート
より高速なレンダリング
Vue Virtual DOM (VDOM)はゼロから書き直され、より良いレンダリングパフォーマンスを可能にしました。さらに、コンパイルされた単一ファイルコンポーネントを扱う場合、Vue コンパイラは、静的マークアップと動的マークアップを分離することによって、ビルド時にコンポーネントをさらに最適化することができます。
その結果、最初のレンダリング(コンポーネント作成)と更新が高速化され、メモリ使用量も少なくなります。Nuxt 3 では、サーバーサイドレンダリングの高速化も可能になりました。
より小さいバンドル
Vue 3 と Nuxt 3 では、バンドルサイズの縮小に重点が置かれています。バージョン 3 では、テンプレートディレクティブや組み込みコンポーネントなど、Vue の機能のほとんどが Tree shaking(実行されないコードを削除すること)が可能になっています。それらを使用しない場合、プロダクションバンドルにはそれらが含まれません。
こうすることで、最小限のVue 3アプリケーションを gzip で 12kb に圧縮することができます。
Composition API
Vue 2 でコンポーネントにデータやロジックを提供する唯一の方法は Options API で、data
や methods
のようなあらかじめ定義されたプロパティをテンプレートに返すことができるようになりました:
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment(){
this.count++
}
}
}
</script>
Vue 3 で導入された Composition API は、Options API に代わるものではありませんが、アプリケーション全体でより良いロジックの再利用を可能にし、複雑なコンポーネントで懸念事項によってコードをグループ化する、より自然な方法を提供します。
<script>
定義の setup
キーワードを使用して、上記のコンポーネントを Composition API と Nuxt 3 の自動インポートされた Reactivity API で書き直したものがこちらです:
<script setup>
const count = ref(0)
const increment = () => count.value++
</script>
Nuxt 3 の目標は、Composition API を中心とした優れた開発者体験を提供することです。
- Vue と Nuxt 3
内蔵の composables
から自動インポートされたReactivity 関数
を使用します。 -
composables/
ディレクトリに自動インポートされた再利用可能な独自の関数を記述します。
TypeScript サポート
Vue 3 と Nuxt 3 はどちらも Typescript で書かれています。完全に型付けされたコードベースはミスを防ぎ、API の使い方を文書化します。これは、Typescript を活用するためにアプリケーションを書かなければならないということではありません。Nuxt 3 では、ファイル名を .js
から .ts
に変更したり、コンポーネント内に <script lang="ts">
を追加することで、TypeScript を使用することができます。
レンダリングモード
ブラウザとサーバーは、Javascript のコードを解釈して、Vue.js コンポーネントを HTML 要素にレンダリングすることができます。このステップをレンダリングと呼びます。Nuxt はクライアントサイドとユニバーサルレンダリングの両方をサポートしています。この 2 つのアプローチには長所と短所があり、このセクションで説明します。
クライアントサイドのみのレンダリング
従来の Vue.js アプリケーションは、すぐにブラウザ(または クライアント)上でレンダリングされます。そして、ブラウザが現在のインターフェイスを作成するための命令を含むすべての Javascript コードをダウンロードして解析した後、Vue.js は HTML 要素を生成します。
この手法により、複雑でダイナミックな UI を構築し、スムーズなページ遷移を実現することができますが、異なる長所と短所があります。
長所
- 開発スピード: 完全にクライアントサイドで作業する場合、window オブジェクトのようなブラウザ専用の API を使用するなど、コードのサーバー互換性を気にする必要がありません。
- 低コスト: サーバーを稼働させる場合、Javascript をサポートするプラットフォームで稼働させる必要があるため、インフラストラクチャのコストがかかります。クライアント専用のアプリケーションは、HTML、CSS、Javascript のファイルだけで、どんな静的なサーバーでもホストすることができます。
- オフライン: コードはすべてブラウザ上で実行されるため、インターネットが利用できない状態でも、きれいに動作し続けることができます。
短所
- パフォーマンス: ユーザーは、ブラウザが JavaScript ファイルをダウンロードし、解析し、実行するのを待つ必要があります。ダウンロードはネットワークに依存し、解析と実行はユーザーのデバイスに依存するため、時間がかかり、ユーザーエクスペリエンスに影響を与える可能性があります。
- 検索エンジン最適化: クライアントサイドレンダリングで配信されるコンテンツのインデックス作成と更新には、すでに構築された HTML ドキュメントよりも多くの時間がかかります。これは、先ほど説明したパフォーマンスの欠点と関係があります。検索エンジンのクローラーは、ページをインデックスするための最初の試みで、インターフェイスが完全にレンダリングされるのを待たないからです。したがって、純粋なクライアントサイド・レンダリングでは、検索結果ページでのコンテンツの表示と更新に時間がかかります。
事例
クライアントサイドレンダリングは、インデックス作成の必要がない、または同じユーザーを頻繁に使用する、高度にインタラクティブな ウェブアプリケーション に適した方法です。ブラウザのキャッシュを活用して、SaaS、バックオフィスアプリケーション、オンラインゲーム など、次回以降の訪問時にダウンロードフェーズをスキップすることができます。
ユニバーサルレンダリング
ユニバーサル(クライアントサイド+サーバーサイド)レンダリングを有効にしてブラウザが URL をリクエストすると、完全にレンダリングされた HTML ページがブラウザに返されます。ページが事前に生成されてキャッシュされている場合も、オンザフライでレンダリングされる場合も、Nuxt はサーバー環境で Javascript(Vue.js)のコードを実行し、HTML ドキュメントを生成していることに変わりはありません。ユーザーは、クライアントサイドレンダリングとは逆に、我々のアプリケーションのコンテンツをすぐに手に入れることができます。このステップは、PHP や Ruby のアプリケーションが行う従来の サーバーサイドレンダリング に似ています。
動的なインターフェースやページ遷移など、クライアントサイドレンダリング方式の利点を失わないために、サーバー上で実行される JavaScript コードは、HTML ドキュメントがダウンロードされるとバックグラウンドでクライアントによってロードされます。HTML 文書がブラウザによって再び解釈されると(ユニバーサルレンダリング のため)、Vue.js が文書を制御し、インタラクティブ性を実現します。
静的なページをブラウザ上でインタラクティブにすることを "ハイドレーション "と呼びます。
ユニバーサルレンダリングにより、Nuxt アプリケーションはクライアントサイドレンダリングの利点を生かしながら、ページの読み込み時間を短縮することができる。さらに、コンテンツがすでに HTML ドキュメント内に存在するため、クローラーはオーバーヘッドなしにインデックスを作成することができる。
長所
- パフォーマンス: ブラウザは、Javascript で生成されたものよりも静的なコンテンツをより速く表示できるため、ユーザーはページのコンテンツにすぐにアクセスすることができます。同時に、Nuxt はハイドレーションプロセスが発生しても、web アプリケーションのインタラクティブ性を維持します。
- 検索エンジン最適化: ユニバーサルレンダリングは、ページの HTML コンテンツ全体をクラシックなサーバーアプリケーションとしてブラウザに配信します。ウェブクローラーは、ページのコンテンツを直接インデックスすることができます。そのため、ユニバーサルレンダリングは、迅速にインデックスしたいあらゆるコンテンツに最適な選択肢となります。
短所
- 開発の制約: サーバーとブラウザーの環境は同じ API を提供していないため、両側でシームレスに動作するコードを書くのは難しい場合があります。幸いなことに、Nuxt はガイドラインと特定の変数を提供し、コードの一部がどこで実行されるかを決定するのに役立ちます。
- 高コスト: オンザフライでページをレンダリングするためには、サーバーを稼働させる必要があります。これには、従来のサーバーと同様に毎月のコストがかかります。しかし、ユニバーサルレンダリングでは、クライアントサイドのナビゲーションをブラウザが引き継ぐため、サーバーの呼び出しは大幅に削減されます。
事例
ユニバーサルレンダリングは非常に汎用性が高く、ほとんどのユースケースに対応できます。特に、ブログ、マーケティングウェブサイト、ポートフォリオ、Eコマース、マーケットプレイス など、コンテンツ重視のウェブサイトに適しています。
概要
クライアントサイドとユニバーサルレンダリングは、ブラウザでインターフェイスを表示するための異なる戦略です。
デフォルトでは、Nuxt はより良いユーザー体験とパフォーマンスを提供し、検索エンジンのインデックスを最適化するために ユニバーサルレンダリング を使用しますが、1 行の設定
でレンダリングモードを切り替えることができます。
Nuxt 3 で登場
多くの場合、Nuxt 2 で実行されるユニバーサルレンダリングは、ユーザーと開発者に良い経験を提供します。しかし、Nuxt 3 では、ハイブリッドレンダリングとエッジサイドレンダリングを導入することにより、ユニバーサルレンダリングをさらに進化させます。
ハイブリッドレンダリング
ハイブリッドレンダリングは、ルートごとに異なるキャッシュルールを許可し、与えられた URL の新しいリクエストにサーバーがどのように応答すべきかを決定します。
現時点では、Nuxt アプリケーションのすべてのページ(または ルート)は、クライアントサイドまたはユニバーサルという同じレンダリングモードを使用する必要があります。しかし、さまざまなケースで、いくつかのページは構築時に生成され、他のページはクライアントサイドでレンダリングされる必要があります。たとえば、管理セクションを持つコンテンツウェブサイトを考えてみましょう。すべてのコンテンツページは主に静的で一度だけ生成されるべきですが、管理セクションは登録が必要で、より動的なアプリケーションのように動作します。
実装を議論し、コミュニティのフィードバックを収集するオープン RFC を読む
CDN エッジワーカーでのレンダリング
従来、サーバーサイドやユニバーサルレンダリングは、Node.js でなければできませんでした。Nuxt 3 は、CDN エッジワーカーのコードを直接レンダリングすることで、レイテンシー(データ通信の待ち時間)とコストを削減し、さらにレベルアップします。
Nitro は、Nuxt 3 を支える新しい サーバーエンジン
です。Node.js、Deno、Workers などをクロスプラットフォームでサポートします。Nitro のデザインはプラットフォームにとらわれず、Nuxt アプリケーションをユーザーにより近いエッジでレンダリングし、レプリケーションとさらなる最適化を可能にします。
Nuxt 3 アプリケーションをサーバーレスプロバイダーにデプロイする
自動インポート
Nuxt は、ヘルパー関数、Composables、Vue API を明示的にインポートしなくても、アプリケーション全体で使用できるように自動インポートします。ディレクトリ構造に基づいて、すべての Nuxt アプリケーションは、独自のコンポーネント、composables、プラグインのために自動インポートを使用することもできます。コンポーネント、composables、プラグインは、これらの関数を使用することができます。
古典的なグローバル宣言とは異なり、Nuxt は型付けと IDE の補完とヒントを保持し、プロダクションコードで実際に使用されるものだけを含めます。
Nuxt 自動インポート
Nuxt は、データの取得
、アプリのコンテキスト
や ランタイム設定
へのアクセス、状態
の管理、コンポーネントやプラグインの定義を行う関数やコンパイラを自動でインポートします。
<script setup>
/* useAsyncData() と $fetch() は 自動インポートされます */
const { data, refresh, pending } = await useAsyncData('/api/hello', () => $fetch('/api/hello'))
</script>
Vue 自動インポート
Vue 3 は、ref
や computed
といった Reactivity API や、Nuxt が自動でインポートするライフサイクルフックやヘルパーを公開しています。
<script setup>
/* ref() and computed() are auto-imported */
const count = ref(1)
const double = computed(() => count.value * 2)
</script>
ディレクトリベースの自動インポート
Nuxt は定義されたディレクトリに作成されたファイルを直接自動インポートします。
- Vue compoents のための
components/
- Vue composables のための
composables/
明示的なインポート
Nuxt の自動インポートはすべて #imports
というエイリアスで公開されており、必要に応じてこれを使用してインポートを明示することができます:
<script setup>
import { ref, computed } from '#imports'
const count = ref(1)
const double = computed(() => count.value * 2)
</script>
サーバーエンジン
Nuxt 3 は、"Nitro(ナイトロ)" と呼ばれる新しいサーバーエンジンを搭載しています。
このエンジンには多くの利点があります。
- ✅ Node.js、ブラウザ、サービスワーカーなどのクロスプラットフォームのサポート
- ✅ サーバーレスを即座にサポート
- ✅ API ルートのサポート
- ✅ 自動的なコード分割と非同期ロードされたチャンク
- ✅ 静的サイト+サーバーレスサイトのハイブリッドモード
- ✅ ホットモジュールリローディング機能付き開発サーバー
API レイヤー
サーバーの API のエンドポイント
とミドルウェア
は、内部で h3
を使用している Nitro によって追加されます。
以下のような主要な機能があります:
- ハンドラは、自動的に処理される JSON レスポンスに対して、オブジェクト/配列を直接返すことができる
- ハンドラは待ち受けとなるプロミスを返すことができる(
res.end()
、next()
もサポートされている) - ボディパース、クッキー処理、リダイレクト、ヘッダーなどのヘルパー関数
詳しくは h3 ドキュメント
をご覧ください。
API を直接呼び出す
Nitro では、グローバルに利用可能な $fetch
ヘルパーを使用して、ルートを「直接」呼び出すことができます。これは、ブラウザ上で実行された場合はサーバーへの API 呼び出しを行いますが、サーバー上で実行された場合は単に関連する関数を呼び出すので、追加の API 呼び出しを節約することができます 。
$fetch
API は、ohmyfetch
を使用しており、主な機能は以下の通りです。
- JSON レスポンスの自動パース(必要に応じて生のレスポンスにアクセス可能)
- リクエストボディとパラメータは自動的に処理され、正しい
Content-Type
ヘッダーが追加されます。
$fetch
機能の詳細については、ohmyfetch
を参照してください。
型付き API ルート
API ルート(またはミドルウェア)を使用する場合、res.end()
を使用してレスポンスを送信するのではなく、値を返す限り、Nitro はこれらのルートに対する型付けを生成します。
これらの型には、$fetch()
や useFetch()
を使用する際にアクセスすることができます。
スタンドアローン(単独で動作しているシステム)サーバー
Nitro は node_modules
とは独立したスタンドアローンサーバー dist を生成します。
Nuxt 2 のサーバーはスタンドアローン(単独で動作しているシステム)ではなく、nuxt start
の実行(nuxt-start
や nuxt
ディストリビューションを使用)やカスタムプログラムによる使用など、nuxt core の一部が関与する必要があり、壊れやすくサーバーレスやサービスワーカーの環境には向いていないものでした。
この dist は、nuxt build
を実行すると、.output
ディレクトリに生成されます。
この出力は、ランタイムコードの両方と組み合わされ、Nuxt サーバーをあらゆる環境(実験的なブラウザサービスワーカーを含みます!)で実行し、静的ファイルを提供する、JAMstack のための真のハイブリッドフレームワークとなっています。さらに、ネイティブストレージレイヤーが実装されており、マルチソース、ドライバ、ローカルアセットをサポートしています。
ネイティブ ES モジュール
Nuxt 3(および Bridge)は Native ES Modules を使用しています。
このガイドでは、ES モジュールとは何か、また Nuxt アプリ(または上流のライブラリ)を ESM に対応させる方法について説明します。
背景
CommonJS モジュール
CommonJS (CJS) は Node.js によって導入されたフォーマットで、分離された JavaScript モジュール間で機能を共有することができます (詳しくはこちら
)。この構文にはすでに馴染みがあるかもしれません:
const a = require('./a')
module.exports.a = a
webpack や Rollup などのバンドラーはこの構文に対応しており、CommonJS で書かれたモジュールをブラウザで利用することができます。
ESM の構文
ESM vs CJS について話すとき、ほとんどの場合、モジュール
を記述するための異なる構文について話しているのです。
import a from './a'
export { a }
ECMAScript Modules(ESM) が標準になる前に(10 年以上かかった!)、webpack
のようなツールや TypeScript のような言語でさえ、いわゆる ESM 構文 をサポートするようになりました。しかし、実際の仕様とはいくつかの重要な違いがあります; ここでは 役に立つ説明
をしています。
'ネイティブ' ESM とは?
あなたは長い間、ESM 構文を使ってアプリを書いてきたかもしれません。Nuxt 2 では、あなたが書いたすべてのコードを適切なフォーマット(サーバは CJS、ブラウザは ESM)にコンパイルしています。
パッケージにインストールするモジュールを使用する場合は、少し事情が異なりました。サンプルライブラリは、CJS 版と ESM 版の両方を公開しており、どちらを使うか選択できるようになっています:
{
"name": "sample-library",
"main": "dist/sample-library.cjs.js",
"module": "dist/sample-library.esm.js"
}
つまり、Nuxt 2 では、バンドル (webpack) はサーバビルド用に CJS ファイル ('main') を取り込み、クライアントビルド用に ESM ファイル ('module') を使用することになります。
しかし、最近の Node.js LTS リリースでは、Node.js の中で ネイティブの ESM モジュールを使用する
ことができるようになりました。つまり、デフォルトではありませんが、Node.js 自体が ESM 構文を使って JavaScript を処理できるようになったのです。ESM 構文を有効にする最も一般的な方法は、次の 2 つです:
-
package.json
でtype: 'module'
を設定し、拡張子は.js
を使用し続ける。 -
.mjs
のファイル拡張子を使用する(推奨)
Nuxt Nitro では、このように .output/server/index.mjs
ファイルを出力しています。これは、Node.js にこのファイルをネイティブ ES モジュールとして扱うように指示するものです。
Node.js の文脈で有効なインポートとは何ですか?
モジュールを require
するのではなく import
する場合、Node.js は異なる方法でモジュールを解決します。例えば、sample-library
を import すると、Node.js は main
ではなく、そのライブラリの package.json
にある exports
や module
のエントリを探します。
これは、const b = await import('sample-library')
のような動的インポートにも当てはまります。
Node は以下の種類のインポートをサポートしています(docs
を参照)。
-
.mjs
で終わるファイル - ESM 構文を使用することが想定されています。 -
.cjs
で終わるファイル - CJS 構文を使用することが想定されています。 -
.js
で終わるファイル -package.json
にtype: 'module'
がない限り、CJS 構文を使用することが期待されます。
どのような問題があるのでしょうか?
長い間、モジュールの作者は ESM 構文のビルドを作成してきましたが、.esm.js
や .es.js
といった規約を使用していました。これは今まで問題にはならなかったのですが、webpack のようなファイル拡張子を特に気にしない bundler によってのみ使用されてきたからです。
しかし、Node.js の ESM コンテキストで .esm.js
ファイルを持つパッケージをインポートしようとすると、うまくいかず、次のようなエラーが表示されます:
(node:22145) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
/path/to/index.js:1
export default {}
^^^^^^
SyntaxError: Unexpected token 'export'
at wrapSafe (internal/modules/cjs/loader.js:1001:16)
at Module._compile (internal/modules/cjs/loader.js:1049:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
....
at async Object.loadESM (internal/process/esm_loader.js:68:5)
また、Node.js が CJS とみなしている ESM-syntax ビルドからの名前付きインポートがある場合にも、このエラーが発生する可能性があります:
file:///path/to/index.mjs:5
import { named } from 'sample-library'
^^^^^
SyntaxError: Named export 'named' not found. The requested module 'sample-library' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'sample-library'
const { named } = pkg
at ModuleJob._instantiate (internal/modules/esm/module_job.js:120:21)
at async ModuleJob.run (internal/modules/esm/module_job.js:165:5)
at async Loader.import (internal/modules/esm/loader.js:177:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
ESM 問題のトラブルシューティング
これらのエラーが発生した場合、ほぼ間違いなく上流のライブラリに問題があります。上流のライブラリは Node でインポートできるように ライブラリを修正する
必要があります。
ライブラリのトラインスパイル
その間、build.transpile
にこれらのライブラリを追加することで、Nuxt にこれらのライブラリをインポートしようとしないように指示することができます。
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
build: {
transpile: ['sample-library']
}
})
また、これらのライブラリによってインポートされる他のパッケージを追加する必要があることが分かるかもしれません。
ライブラリのエイリアス
また、場合によっては、CJS バージョンのライブラリに手動でエイリアスを設定する必要があることもあります。
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
alias: {
'sample-library': ['sample-library/dist/sample-library.cjs.js']
}
})
デフォルトのエクスポート
CommonJS 形式の依存関係では、module.exports
または exports
を使用して、デフォルトのエクスポートを提供することができます:
module.exports = { test: 123 }
// or
exports.test = 123
require
を使うこのような依存関係が必要な場合は、通常うまくいきます。
const pkg = require('cjs-pkg')
console.log(pkg) // { test: 123 }
Node.js のネイティブ ESM モード、
esModuleInterop` を有効にした typescript、Webpack などのバンドラーでは、このようなライブラリをデフォルトでインポートできる互換性の仕組みが提供されています。この仕組みは、しばしば "interop require default" と呼ばれます:
import pkg from 'cjs-pkg'
console.log(pkg) // { test: 123 }
しかし、構文検出が複雑であったり、バンドル形式が異なるため、デフォルトの interop に失敗して、以下のような結果になる可能性があります。
import pkg from 'cjs-pkg'
console.log(pkg) // { default: { test: 123 } }
また、動的インポート構文(CJS ファイルと ESM ファイルの両方)を使用する場合、常にこのような状況が発生します:
ライブラリ作者のためのガイド
ESM の互換性の問題は、比較的簡単に解決できるのが良いところです。主に 2 つのオプションがあります。
-
ESM ファイルの名前を変更し、末尾を
.mjs
にすることができます。
これは推奨される最もシンプルな方法です。ライブラリの依存関係やビルドシステムの問題を解決する必要があるかもしれませんが、ほとんどの場合、この方法で問題が解決されるはずです。また、CJS ファイルの名前を変更して、末尾を.cjs
にすると、より明確になります。 -
ライブラリ全体を ESM のみにすることもできます。
これは、package.json
でtype: 'module'
を設定し、ビルドされたライブラリが ESM 構文を使用することを保証することを意味します。しかし、依存関係の問題に直面する可能性があります - このアプローチは、あなたのライブラリは、ESM コンテキストでのみ消費できることを意味します。
移行
CJS から ESM への最初のステップは、require
の使用法を import
に変更することです:
module.exports = ...
exports.hello = ...
or
export default ...
export const hello = ...
const myLib = require('my-lib')
or
import myLib from 'my-lib'
// or
const myLib = await import('my-lib').then(lib => lib.default || lib)
ESM モジュールでは、CJS と異なり、require
、 require.resolve
、 __filename
、 __dirname
グローバルは使用できないので、import()
、 import.meta.filename
に置き換えてください。
unjs/mlly
の createCommonJS
を使って、ESM に CJS 互換のコンテキストを作ることができます (あるいはインライン shim を使う)。
import { createCommonJS } from 'mlly'
const { __dirname, __filename, require } = createCommonJS(import.meta.url)
or
import { fileURLToPath } from 'url'
import { dirname } from 'path'
import { createRequire } from 'module'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const require = createRequire(import.meta.url)
const someFile = require.resolve('./lib/foo.js')
or
import { resolvePath } from 'mlly'
const someFile = await resolvePath('my-lib', { url: import.meta.url })
ベストプラクティス
- デフォルトのエクスポートではなく、名前付きのエクスポートを優先します。これは CJS のコンフリクトを減らすのに役立ちます。(
デフォルトエクスポート
のセクションを参照) - Node.js のビルトインや CommonJS、Node.js のみへの依存はできるだけ避け、Nitro のポリフィルを必要とせずにブラウザや Edge Worker で使用できるライブラリにすること。
- 条件付きエクスポートで新しい
エクスポート
フィールドを使用する(続きを読む
)。
{
"exports": {
".": {
"import": "./dist/mymodule.mjs"
}
}
}
TypeScript
Nuxt 3 は完全に型付けされており、コーディング時に正確な型情報にアクセスできるよう、便利なショートカットを提供しています。
型チェック
Nuxt は、パフォーマンス上の理由から、nuxi dev
や nuxi build
を実行する際にデフォルトで型チェックを行いません。しかし、nuxi を使用して手動で型をチェックすることができます
。
yarn nuxi typecheck
自動生成される型
nuxi dev
や nuxi build
を実行すると、IDE の型サポート(と型チェック)のために、以下のファイルが生成されます。
.nuxt/nuxt.d.ts
このファイルには、使用しているすべてのモジュールの型と、Nuxt 3 が必要とする主要な型が含まれています。IDE はこれらのタイプを自動的に認識するはずです。
ファイル内のいくつかの参照は、buildDir
(.nuxt
)内にのみ生成されるファイルであるため、完全な型付けのためには、nuxi dev
または nuxi build
を実行する必要があります。
.nuxt/tsconfig.json
このファイルには、Nuxt や使用しているモジュールによって注入された解決済みのエイリアスを含む、プロジェクトに推奨される TypeScript の基本設定が含まれており、~/file
や #build/file
といったエイリアスに対して、フルタイプのサポートとパスの自動補完を行うことができます。
この設定を拡張する方法については、こちらをご覧ください。
より厳密な型チェック
TypeScript は、プログラムの安全性と解析性を高めるために、ある種のチェック機能を備えています。
コードベースを TypeScript に変換し、使い慣れたら、これらのチェックを有効にすることで、より安全性を高めることができます。(続きを読む
)
厳密な型チェックを有効にするためには、nuxt.config
を更新する必要があります:
export default defineNuxtConfig({
typescript: {
strict: true
}
})
ドキュメント
データフェッチ
Nuxt は、アプリケーション内のデータ取得を処理するために、useFetch、useLazyFetch、useAsyncData、useLazyAsyncData を提供します。
useAsyncData
pages、components、plugins の中で、useAsyncData
を使用すると、非同期に解決されるデータにアクセスすることができます。
使い方
const {
data: Ref<DataT>,
pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>,
error?: any
} = useAsyncData(
key: string,
fn: () => Object,
options?: { lazy: boolean, server: boolean }
)
パラメータ
- key: データ取得がリクエスト間で適切に重複を回避できるようにするための一意のキー
- fn 値を返す非同期関数。
-
options:
-
lazy: ナビゲーションをブロックするのではなく、ルートをロードした後に非同期関数を解決するかどうか (デフォルトは
false
) - default: 非同期関数が解決する前に、データのデフォルト値を設定するファクトリ関数 - lazy: true オプションと一緒に使うと特に便利です。
-
server: サーバサイドでデータを取得するかどうか (デフォルトは
true
) - transform: 解決後にfnの結果を変更するために使用できる関数
- pick: fn結果からこの配列で指定されたキーのみをピックする。
-
lazy: ナビゲーションをブロックするのではなく、ルートをロードした後に非同期関数を解決するかどうか (デフォルトは
useAsyncData
は、以下のプロパティを持つオブジェクトを返す。
- data: 渡された非同期関数の結果
- pending: データがまだ取得されているかどうかを示すブール値
- refresh: データを強制的に更新するために使用することができる関数です。
- error: データ取得に失敗した場合のエラーオブジェクト
ボンネットの中では、lazy: false
は <Suspense>
を使用して、データが取得される前にルートの読み込みをブロックしています。より快適なユーザーエクスペリエンスのために、lazy: true
を使用し、代わりにロード状態を実装することを検討してください。
例
let counter = 0
export default () => {
counter++
return JSON.stringify(counter)
}
<script setup>
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
</script>
<template>
Page visits: {{ data }}
</template>
useLazyAsyncData
この composable は、lazy: true
オプションを設定した useAsyncData
と同じように動作します。言い換えれば、非同期関数はナビゲーションをブロックしません。つまり、データが null
(または、カスタムのdefault
ファクトリ関数で提供した任意の値)である状況を処理する必要があることを意味します。
useFetch
page、components、plugins 内で、useFetch
を使用して、任意の URL から普遍的に取得することができます。
この composables は、useAsyncData
と $fetch
の便利なラッパーを提供します。これは、URL とフェッチオプションに基づいてキーを自動的に生成し、API のレスポンスタイプも推測します。
使い方
const {
data: Ref<DataT>,
pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>,
error?: any
} = useFetch(url: string, options?)
利用可能なオプション:
-
key
: カスタムキーを指定する -
ohmyfetch
からのオプション-
method
: リクエストメソッド -
params
: クエリパラメータ -
headers
: リクエストヘッダー -
baseURL
:リクエストのベース URL
-
- “useAsyncData` からのオプション
lazy
server
default
pick
transform
useFetch
によって返されるオブジェクトは、useAsyncData
によって返されるオブジェクトと同じプロパティを持ちます(上記参照)。
例
<script setup>
const { data } = await useFetch('/api/count')
</script>
<template>
Page visits: {{ data.count }}
</template>
useLazyFetch
この composable は、lazy: true
オプションを設定した useFetch
と同じように動作します。言い換えれば、非同期関数はナビゲーションをブロックしません。つまり、データが null
(または、カスタムの default
ファクトリ関数で提供した値)である状況を処理する必要があります。
Isomorphic(同型) fetch
ブラウザで fetch
を呼び出すと、cookie
などのユーザーヘッダが直接 API に送られます。しかし、サーバサイドレンダリングでは、fetch
リクエストはサーバから発信されるため、ユーザのブラウザクッキーは含まれません。
useRequestHeaders
を使って、サーバーサイドから API にアクセスし、クッキーをプロキシすることができます。
例:
以下の例では、同型の fetch
呼び出しにリクエストヘッダを追加し、API エンドポイントが、もともとユーザーによって送信されたのと同じ cookie
ヘッダーにアクセスできるようにします。
<script setup>
const { data } = useFetch('/api/me', {
headers: useRequestHeaders(['cookie'])
})
</script>
ベストプラクティス
これらの composables が返すデータは、ページのペイロード内に格納されます。つまり、返されたキーのうち、あなたのコンポーネントで使用されていないものはすべて、ペイロードに追加されることになります。
api/mountains/everest
が次のようなオブジェクトを返すと想像してください:
{
"title": "Mount Everest",
"description": "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. The China–Nepal border runs across its summit point",
"height": "8,848 m",
"countries": [
"China",
"Nepal"
],
"continent": "Asia",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg"
}
もし、あなたのコンポーネントで title
と description
だけを使うつもりなら、$fetch
の結果か pick
オプションでキーを選択することができます:
<script setup>
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
</script>
<template>
<h1>{{ mountain.title }}</h1>
<p>{{ mountain.description }}</p>
</template>
async setup を使用
async setup()
を使用している場合、現在のコンポーネント・インスタンスは最初の await
の後に失われます。(これは Vue 3 の制限です。) 複数の useFetch
の呼び出しなど、複数の非同期処理を使用したい場合は、<script setup>
を使用するか、setup の最後でまとめて await する必要があります。
<script>
export default defineComponent({
async setup() {
const [{ data: organization }, { data: repos }] = await Promise.all([
useFetch(`https://api.github.com/orgs/nuxt`),
useFetch(`https://api.github.com/orgs/nuxt/repos`)
])
return {
organization,
repos
}
}
})
</script>
<template>
<header>
<h1>{{ organization.login }}</h1>
<p>{{ organization.description }}</p>
</header>
</template>
State
Nuxt は、コンポーネント間でリアクティブで SSR に優しい共有状態を作成するために useState composableを提供します。
useState
は SSR に優しい ref
の代替品です。その値は、サーバー側のレンダリング(クライアント側のハイドレーション中)後に保持され、一意のキーを使用してすべてのコンポーネント間で共有されます。
署名
useState<T>(key: string, init?: () => T): Ref<T>
- key: データ取得がリクエスト間で適切に重複しないようにするための一意のキー
- init: 状態が開始されていないときに初期値を提供する関数
- T: (typescriptのみ)状態の種類を指定します
👉 `useState` は `setup` もしくはライフサイクルフックでのみ機能します。
ベストプラクティス
例
基本的な使い方
この例では、コンポーネントローカルのカウンタ状態を使用しています。useState('counter')
を使用する他のコンポーネントは、同じリアクティブステートを共有します。
<script setup>
const counter = useState('counter', () => Math.round(Math.random() * 1000))
</script>
<template>
<div>
Counter: {{ counter }}
<button @click="counter++">
+
</button>
<button @click="counter--">
-
</button>
</div>
</template>
StackBlitz を開く
応用
この例では、HTTPリクエストヘッダからユーザーのデフォルトロケールを検出し、locale
状態を保持するコンポーザブルを使用しています。
StackBlitz を開く
state を共有する
自動インポートされる composables
を使用することで、グローバルな型安全の状態を定義し、アプリ全体でインポートすることができます。
export const useCounter = () => useState<number>('counter', () => 0)
export const useColor = () => useState<string>('color', () => 'pink')
<script setup>
const color = useColor() // Same as useState('color')
</script>
<template>
<p>Current color: {{ color }}</p>
</template>
メタタグ
自分のサイトのメタタグは、いくつかの方法でカスタマイズすることができます。
useMeta
Composable
setup
関数内で、メタタグに対応するキーを持つメタプロパティのオブジェクトで useMeta
を呼び出すことができます: title
, base
, script
, style
, meta
and link
, 同様に htmlAttrs
と bodyAttrs
です。あるいは、反応するメタデータ用のオブジェクトを返す関数を渡すこともできます
例:
export default {
setup () {
useMeta({
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
],
bodyAttrs: {
class: 'test'
}
})
}
}
メタコンポーネント
Nuxt は <Title>
, <Base>
, <Script>
, <Style>
, <Meta>
, <Link>
, <Body>
, <Html>
, <Head>
コンポーネントを提供し、コンポーネントのテンプレート内で直接メタデータと対話できるようにします。
これらのコンポーネント名はネイティブの HTML 要素に一致するため、テンプレート内で大文字で記述することが非常に重要です。
<Head>
と <Body>
はネストした meta タグを受け入れることができますが(美観上の理由から)、これは最終的なHTML の中でネストした meta タグがレンダリングされる場所に影響を与えません。
例:
<template>
<div>
Hello World
<Html :lang="dynamic > 50 ? 'en-GB' : 'en-US'">
<Head>
<Title>{{ dynamic }} title</Title>
<Meta name="description" :content="`My page's ${dynamic} description`" />
<Link rel="preload" href="/test.txt" as="script" />
<Style type="text/css" :children="styleString" />
</Head>
</Html>
<button class="blue" @click="dynamic = Math.random() * 100">
Click me
</button>
</div>
</template>
<script>
export default {
data: () => ({ dynamic: 49, styleString: 'body { background-color: green; }' })
}
</script>
NuxtApp
Nuxt 3 では、composables、components、plugins の中でランタイムアプリコンテキストにアクセスすることができます。
Nuxt 2 では、これは Nuxt コンテキスト
と呼ばれていました。
NuxtAppへのアクセス
composables、plugins、components 内では、useNuxtApp
でnuxtApp
にアクセスすることができます。
import { useNuxtApp } from '#app'
function useMyComposable () {
const nuxtApp = useNuxtApp()
// access runtime nuxt app instance
}
プラグインも便宜上、第1引数として nuxtApp
を受け取ります。プラグインについてもっと読む。
ヘルパーの提供
すべての composables とアプリケーションで使用できるヘルパーを提供することができます。これは通常、Nuxtプラグイン内で行われます。
const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Hello ${name}!`)
console.log(nuxtApp.$hello('name')) // Prints "Hello name!"
Nuxt 2 のプラグインでは、これは inject function
と呼ばれていました。
ランタイム設定
Nuxt は、アプリケーションおよび API ルート内のランタイムの設定を定義するための API を提供します。
ランタイムの設定を公開する
アプリの残りの部分に設定と環境変数を公開するには、privateRuntimeConfig
または publicRuntimeConfig
オプションを使って nuxt.config
ファイルに実行時設定を定義する必要があります(アプリのクライアントサイド部分でアクセス可能にしたいのかどうかに基づいています)。
例:
export default defineNuxtConfig({
publicRuntimeConfig: {
API_BASE: '/api'
},
privateRuntimeConfig: {
API_SECRET: '123'
}
})
publicRuntimeConfig
に API_BASE
を追加すると、Nuxt はページのペイロードに API_BASE
を追加します。こうすることで、サーバとブラウザの両方で普遍的に API_BASE
にアクセスすることができます。
環境変数
設定を提供する最も一般的な方法は、環境変数
を使用することです。Nuxt CLI には dotenv
のサポートが組み込まれています。
プロセスの環境変数に加えて、プロジェクトのルートディレクトリに .env
ファイルがあれば、自動的に process.env
に読み込まれ、nuxt.config
ファイルとモジュール内でアクセスできるようになります。
**例:*:
BASE_URL=https://nuxtjs.org
API_SECRET=api_secret_token
export default defineNuxtConfig({
publicRuntimeConfig: {
BASE_URL: process.env.BASE_URL
},
privateRuntimeConfig: {
API_SECRET: process.env.API_SECRET
}
})
💡 Tip: 必要ではありませんが、同一のランタイムの設定名を環境変数として使用することで、プラットフォーム環境変数を使用して、実運用環境で簡単に上書きすることができます。
ランタイムの設定にアクセスする
Vue アプリ
Nuxt アプリの Vue パート内で、useRuntimeConfig()
を呼び出してランタイムの設定にアクセスする必要があります。
Note: クライアントサイドとサーバーサイドで動作が異なります。
- クライアント側では、
publicRuntimeConfig
のみが利用可能で、このオブジェクトは書き込み可能かつ反応可能です。 - サーバー側では、
publicRuntimeConfig
とprivateRuntimeConfig
の両方がマージされ、オブジェクトはコンテキスト共有を避けるために読み取り専用になります。
<template>
<div>
<div>Token: {{ config.API_AUTH_TOKEN }}</div>
</div>
</template>
<script setup>
const config = useRuntimeConfig()
</script>
🛑 Security note: API_AUTH_TOKEN
がプライベートな設定の場合、上記の例は絶対に使用しないでください。privateRuntimeConfig
を使用する場合でも、そのような設定を payload や html に公開しないように注意しなければなりません!
プラグイン
もし、任意の(カスタム)プラグイン内でランタイムの設定を使用したい場合は、defineNuxtPlugin
関数内でuseRuntimeConfig()
を使用することができます。
例えば、
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig();
const url = process.server ? config.serverUrl : config.clientUrl;
// Do something with url & isServer.
});
API routes
API ルート内では、virtual #config
から直接インポートすることで、ランタイムの設定にアクセスすることができます。
import config from '#config'
export default async () => {
const result = await $fetch('https://my.api.com/test', {
headers: {
Authorization: `Bearer ${config.API_AUTH_TOKEN}`
}
})
return result
}
ランタイムの設定を入力する
現在、ランタイムの設定を手動で入力することが可能です。
declare module '@nuxt/schema' {
interface PublicRuntimeConfig {
testConfig: string
}
interface PrivateRuntimeConfig {
token: string
}
}
// It is always important to ensure you import/export something when augmenting a type
export {}
Cookies
Nuxt は、Cookie を読み書きするための SSR に適した composable を提供します。
使用方法
ページ、コンポーネント、プラグインの中で、useCookie
を使用して、特定のクッキーにバインドされたリアクティブなリファレンスを作成することができます。
const cookie = useCookie(name, options)
例
以下の例では、counter
という Cookie を作成しています。この Cookie が存在しない場合、初期値としてランダムな値が設定されます。counter
変数を更新するたびに、それに応じて Cookie も更新されます。
<template>
<div>
<h1> Counter: {{ counter || '-' }}</h1>
<button @click="counter = null">
reset
</button>
<button @click="counter--">
-
</button>
<button @click="counter++">
+
</button>
</div>
</template>
<script setup>
const counter = useCookie('counter')
counter.value = counter.value || Math.round(Math.random() * 1000)
</script>
Open on StackBlitz
オプション
Cookie composable は、Cookie の動作を変更するためのオプションをいくつか用意しています。
ほとんどのオプションは、cookie
パッケージに直接渡されます。
maxAge
/ expires
maxAge
は、Max-Age
Set-Cookie
属性の値となる数値(秒)を指定します。指定された数値は切り捨てにより整数に変換されます。デフォルトでは、最大年齢は設定されていません。
expires
は、 Expires
Set-Cookie
属性の値として Date オブジェクトを指定します。デフォルトでは、有効期限は設定されていません。ほとんどのクライアントはこれを「非永続的なクッキー」とみなし、ウェブブラウザアプリケーションを終了するなどの条件でこれを削除します。
httpOnly
HttpOnly
Set-Cookie
属性の boolean
値を指定します。真値の場合は HttpOnly
属性が設定され、そうでない場合は設定されません。デフォルトでは、HttpOnly
属性は設定されていません。
secure
secure は、Secure
Set-Cookie
属性の boolean
値を指定します。真値の場合は、Secure
属性が設定され、それ以外の場合は設定されません。デフォルトでは、Secure
属性は設定されていません。
domain
domain は、Domain
Set-Cookie
属性の値を指定します。デフォルトでは、ドメインは設定されておらず、ほとんどのクライアントは、現在のドメインにのみクッキーを適用することを考慮します。
path
path は、Path
Set-Cookie
属性の値を指定します。デフォルトでは、このパスは "デフォルトパス "とみなされます。
sameSite
sameSite は、SameSite
Set-Cookie
属性の boolean
値または string
値を指定します。
-
true
を指定すると、SameSite
属性がStrict
に設定され、同サイトが厳格に施行されます。 -
false
を指定すると、SameSite
属性は設定されません。 -
'lax'
は、SameSite
属性をLax
に設定し、同サイトの強制を緩やかにします。 -
'none'
は、明示的なクロスサイトクッキーのために、SameSite
属性をNone
に設定します。 -
'strict'
は、SameSite
属性を厳密な同サイト強制のためのStrict
に設定します。
異なる実施レベルに関するより詳しい情報は、仕様書を参照してください。
encode
クッキーの値をエンコードするために使用する関数を指定します。クッキーの値は文字数が限られている(単純な文字列でなければならない)ので、この関数を使うと、値をクッキーの値に適した文字列にエンコードすることができます。
デフォルトのエンコーダーは、JSON.stringify
+ encodeURIComponent
です。
decode
クッキーの値をデコードするために使用する関数を指定します。クッキーの値は文字数が限られている(単純な文字列でなければならない)ので、この関数を使うと、以前にエンコードしたクッキーの値を JavaScript の文字列や他のオブジェクトにデコードすることができます。
デフォルトのデコーダーは decodeURIComponent
+ destr
です。
API ルートにおける Cookie の扱い
h3
パッケージの useCookie
と setCookie
を使用すると、サーバー API ルートにクッキーを設定することができます。
例:
import { useCookie, setCookie } from 'h3'
export default (req, res) => {
// counter クッキーを読み取る
let counter = useCookie(req, 'counter') || 0
// 1 まで counter クッキーを増やす
setCookie(res, 'counter', ++counter)
// JSON レスポンスを送る
return { counter }
}
SSR ユーティリティ
Nuxt は、ファーストクラスのサーバサイドレンダリングをサポートする composables とユーティリティを提供します。
useRequestHeaders
ページ、コンポーネント、プラグインの中で useRequestHeaders
を使用すると、受信したリクエストヘッダーにアクセスすることができます。
// すべてのリクエストヘッダーを取得する
const headers = useRequestHeaders()
// cookie リクエストヘッダーのみを取得する
const headers = useRequestHeaders(['cookie'])
Nuxt ディレクトリ
.nuxt/ ディレクトリは、開発中の Nuxt が Vue アプリケーションを生成するために使用されます。
このディレクトリは、あなたが、ディレクトリ構造に基づいて Nuxt が生成するファイルについてもっと学びたい場合に興味深いものです。
Output ディレクトリ
output/ ディレクトリは、Nuxt がアプリケーションを本番用にビルドする際に作成されます。
このディレクトリは、Nuxt アプリケーションを本番環境にデプロイする際に使用されるように作られています。詳しくは、デプロイメントのセクション
をご覧ください。
Assets ディレクトリ
assets/ ディレクトリは、ビルドツール(Webpack または Vite)で処理されるすべての Web サイト資源を追加するために使用されます。
このディレクトリには、通常、次のような種類のファイルが含まれています:
- スタイルシート(CSS、SASS など)
- フォント
-
public/
ディレクトリから配信されない画像。
サーバーからアセットを提供したい場合は、public/
ディレクトリを参照することを推奨します。
Components ディレクトリ
components/ ディレクトリには、すべての Vue コンポーネントを配置し、pages や他のコンポーネントの中に取り込むことができます(
詳細はこちら)。
Nuxt は components/
ディレクトリにあるコンポーネントを自動的にインポートします(使用しているモジュールによって登録されているコンポーネントも同様です)。
| components/
--| TheHeader.vue
--| TheFooter.vue
<template>
<div>
<TheHeader />
<slot />
<TheFooter />
</div>
</template>
コンポーネントの名前
以下のような、ネストされたディレクトリにコンポーネントがある場合:
| components/
--| base/
----| foo/
------| Button.vue
...すると、コンポーネントの名前は、それ自身のパスディレクトリとファイル名に基づいて、重複するセグメントは削除されます。したがって、コンポーネントの名前は次のようになります:
<BaseFooButton />
動的なコンポーネント
Vue の <component :is="someComputedComponent">
構文を使用したい場合は、Vue が提供する resolveComponent
ヘルパーを使用する必要があります。
例えば:
<template>
<component :is="clickable ? MyButton : 'div'" />
</template>
<script setup>
const MyButton = resolveComponent('MyButton')
</script>
また、推奨はしませんが、すべてのコンポーネントをグローバルに登録することもできます。この場合、すべてのコンポーネントに対して非同期チャンクが作成され、アプリケーション全体で利用できるようになります。
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
components: {
+ global: true,
+ dirs: ['~/components']
},
})
動的なインポート
コンポーネントを動的にインポートする(コンポーネントの lazy-loading とも呼ばれる)ために必要なことは、コンポーネントの名前に Lazy
というプレフィックスを追加することです。
<template>
<div>
<TheHeader />
<slot />
<LazyTheFooter />
</div>
</template>
これは、コンポーネントが常に必要とされるわけではない場合に特に有効です。Lazy
プレフィックスを使用すると、適切なタイミングまでコンポーネントコードの読み込みを遅らせることができ、JavaScript のバンドルサイズを最適化するのに便利です。
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">Show List</button>
</div>
</template>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>
<ClientOnly>
コンポーネント
Nuxt は、意図的にクライアント側のみでコンポーネントをレンダリングするための <ClientOnly>
コンポーネントを提供します。クライアントにのみコンポーネントをインポートするには、クライアントサイドのみのプラグインにコンポーネントを登録します。
<template>
<div>
<Sidebar />
<ClientOnly>
<!-- このコンポーネントは、クライアントサイドでのみレンダリングされます -->
<Comments />
</ClientOnly>
</div>
</template>
クライアント側で <ClientOnly>
がマウントされるまでのフォールバック(システムの一部に障害が発生したとき、システム全体の運転を停止させることなく、違った方法で処理を行うこと)として、スロットを使用します。
<template>
<div>
<Sidebar />
<ClientOnly>
<!-- このコンポーネントは、クライアントサイドでのみレンダリングされます -->
<Comments />
<template #fallback>
<!-- これはサーバーサイドでレンダリングされます -->
<p>Loading comments...</p>
</template>
</ClientOnly>
</div>
</template>
ライブラリ作者
自動 tree shake(実行されないコードを削除すること)とコンポーネント登録ができる Vue コンポーネントライブラリの作成が超簡単です✨
components:dirs
フックを使用すると、Nuxt モジュールにユーザー設定をすることなく、簡単にディレクトリリストを拡張することができます。
このようなディレクトリ構造をイメージしてください:
| node_modules/
---| awesome-ui/
------| components/
---------| Alert.vue
---------| Button.vue
------| nuxt.js
| pages/
---| index.vue
| nuxt.config.js
そして、awesome-ui/nuxt.js
で components:dirs
フックを使用することができます:
import { join } from 'pathe'
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
hooks: {
'components:dirs'(dirs) {
// ./components ディレクトリをリストに追加する
dirs.push({
path: join(__dirname, 'components'),
prefix: 'awesome'
})
}
}
})
以上です!これで、あなたのプロジェクトでは、nuxt.config
ファイルで UI ライブラリを Nuxt モジュールとしてインポートすることができます:
export default {
buildModules: ['awesome-ui/nuxt']
}
... そして、モジュールコンポーネント(接頭辞が awesome-
)を直接 pages/index.vue
で使用します:
<template>
<div>
My <AwesomeButton>UI button</AwesomeButton>!
<awesome-alert>Here's an alert!</awesome-alert>
</div>
</template>
使用する場合のみ自動的にコンポーネントをインポートし、node_modules/awesome-ui/components/
にあるコンポーネントを更新する際にも HMR(Hot Module Replacement、画面の再描画無しに JS の変更をブラウザに適用してくれるもの) をサポートするようにします。
Composables ディレクトリ
Nuxt 3 は composables/ ディレクトリをサポートしており、自動インポート機能を使って Vue の composables をアプリケーションに自動的にインポートすることができます !
ファイルのスキャン方法
composables/
ディレクトリのトップレベルにあるファイル(またはサブディレクトリ内のインデックスファイル)だけが、composables のスキャン対象となります。
例:
composables
| - useFoo.ts
| - useBar
| --- supportingFile.ts
| --- index.ts
useFoo.ts
と useBar/index.ts
だけがインポートとして検索され、後者がデフォルトのエクスポートであれば、index
ではなく useBar
として登録されるでしょう。
例(名前付き export を使用)
export const useFoo = () => {
return useState('foo', () => 'bar')
}
例(default export を使用)
// useFoo()(拡張子なしのファイル名のキャメルケース)として利用可能になります
export default function () {
return useState('foo', () => 'bar')
}
これで自動インポートができるようになりました:
<template>
<div>
{{ foo }}
</div>
</template>
<script setup>
const foo = useFoo()
</script>
Layouts ディレクトリ
Nuxt は、アプリケーション全体で使用できるカスタマイズ可能なレイアウトフレームワークを提供し、共通の UI またはコードパターンを再利用可能なレイアウトコンポーネントに抽出するために理想的です。
レイアウトは、layouts/
ディレクトリに置かれ、使用時に非同期インポートによって自動的に読み込まれます。layouts/default.vue
を作成すると、アプリ内のすべてのページでこのレイアウトが使用されます。その他のレイアウトは、ページのメタデータの一部として layout
プロパティを設定するか(~/pages
統合を使用している場合)、<NuxtLayout>
コンポーネントを使って使用されます。
アプリケーションにレイアウトが 1 つしかない場合は、代わりに app.vue
を使用することができます。
app.vue
でレイアウトを有効にする
例: -| layouts/
---| custom.vue
-| app.vue
レイアウトファイルでは、<slot />
を使って、レイアウトのコンテンツが読み込まれる場所を定義する必要があります。例えば:
<template>
<div>
一部のレイアウトコンテンツを共有
<slot />
</div>
</template>
そのレイアウトを app.vue
でどのように使うかを説明します。
<template>
<NuxtLayout name="custom">
Hello world!
</NuxtLayout>
</template>
~/pages
でレイアウトを設定する
例: -| layouts/
---| custom.vue
-| pages/
---| index.vue
このように、pages コンポーネントの中でレイアウトを設定することができます:
<script>
// これは、 `<script setup>` の内部でも同様に動作します
definePageMeta({
layout: 'custom',
})
</script>
~/pages
による手動制御
例: たとえ ~/pages
統合を使っていても、<NuxtLayout>
コンポーネント(アプリケーション全体でグローバルに利用可能)を使って layout: false
を設定すれば、完全に制御することができます。
<template>
<NuxtLayout name="custom">
<template #header> Some header template content. </template>
The rest of the page
</NuxtLayout>
</template>
<script setup>
definePageMeta({
layout: false,
})
</script>
例: レイアウトの変更
また、レイアウトに ref や computed プロパティを使用することもできます。
<template>
<div>
<button @click="enableCustomLayout">Update layout</button>
</div>
</template>
<script setup>
const route = useRoute()
function enableCustomLayout () {
route.meta.layout = "custom"
}
definePageMeta({
layout: false,
})
</script>
Middleware ディレクトリ
Nuxt は、アプリケーション全体で使用できるカスタマイズ可能なルートミドルウェアフレームワークを提供し、特定のルートにナビゲートする前に実行したいコードを抽出するのに適しています。
ルートミドルウェアは 3 種類あります:
- 匿名(またはインライン)ルートミドルウェアは、使用するページで直接定義されます。
- 名前付きルートミドルウェアは、
middleware/
ディレクトリに置かれ、ページで使用される際に非同期インポートによって自動的に読み込まれます。 - グローバルルートミドルウェアは、
middleware/
ディレクトリに置かれ(拡張子は.global
で)、ルートが変更されるたびに自動的に実行されるルートミドルウェアです。
最初の 2 種類のルートミドルウェア(匿名、名前付きルートミドルウェア)は、definePageMeta で定義することができます
。
フォーマット
ルートミドルウェアは、現在のルートと次のルートを引数として受け取るナビゲーションガードです。
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
return navigateTo('/')
})
Nuxt は、ミドルウェアから直接返すことができるグローバルに利用可能なヘルパーを 2 つ提供しています。
-
navigateTo (route: string | Route)
- プラグインやミドルウェアの中で、指定されたルートにリダイレクトします。また、クライアントサイドで直接呼び出すこともできて、ページナビゲーションを実行します。 -
abortNavigation (err?: string | Error)
- オプションのエラーメッセージとともに、ナビゲーションを中止します。
vue-router のドキュメント
にあるナビゲーションガードとは異なり、3 番目の next()
引数は渡されず、リダイレクトやルートのキャンセルは、ミドルウェアから値を返すことによって処理されます。可能な戻り値は以下の通りです:
- nothing - ナビゲーションをブロックせず、次のミドルウェア関数があればそれに移行するか、ルートナビゲーションを完了します。
-
navigateTo('/')
またはnavigateTo({ path: '/' })
- 指定されたパスにリダイレクトします。 -
abortNavigation()
- 現在のナビゲーションを停止します。 -
abortNavigation(error)
- エラーで現在のナビゲーションを拒否します。
ミドルウェアを動的に追加する
プラグインなどのヘルパー関数 addRouteMiddleware()
を使って、 グローバルまたは名前つきルートミドルウェアを手動で追加することができます。
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('このグローバルルートミドルウェアはプラグインで追加され、ルート変更のたびに実行されます')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('この名前付きミドルウェアはプラグインで追加され、既存の同名のミドルウェアを上書きします')
})
})
例: 名前付きルートミドルウェア
-| middleware/
---| auth.ts
ページファイル内で、このルートミドルウェアを参照することができます。
<script setup>
definePageMeta({
middleware: ["auth"]
// or middleware: 'auth'
})
</script>
さて、そのページへのナビゲーションが完了する前に、auth
ルートミドルウェアが実行されます。
Node modules ディレクトリ
node_modules ディレクトリは、パッケージマネージャ(npm または yarn )によって作成され、プロジェクトの依存関係を保存します。
Pages ディレクトリ
Nuxt は、Vue Router を裏で使用して、Web アプリケーション内にルートを作成するためのファイルベースのルーティングを提供します。
使い方
pages は、Vue コンポーネントで、拡張子は vue
、js
、tss
または tsx
です。
<template>
<h1>Index page</h1>
</template>
or
// https://vuejs.org/guide/extras/render-function.html
export default defineComponent({
render () {
return h('h1', 'Index page')
}
})
or
// https://vuejs.org/guide/extras/render-function.html#jsx-tsx
export default defineComponent({
render () {
return <h1>Index page</h1>
}
})
pages/index.vue
ファイルは、アプリケーションの /
ルートにマップされます。
app.vue
を使用している場合は、<NuxtPage />
コンポーネントを使用して、現在のページを表示することを確認してください。
<template>
<div>
<!-- 全ページで共有されるマークアップ(例: NavBar) -->
<NuxtPage />
</div>
</template>
動的なルート
角括弧([])の中に何かを入れると、それが 動的なルート
のパラメータになります。複数のパラメータを混在させたり、ファイル名やディレクトリ内の非ダイナミックテキストも混在させることができます。
例
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue
上記の例では、$route
オブジェクトを介してコンポーネント内の group/id にアクセスすることができます:
<template>
<p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>
users-admins/123
に移動するとレンダリングされます:
<p>admins - 123</p>
Composition API を使ってルートにアクセスしたい場合は、 グローバルな useRoute
関数を使えば、 Options API の this.$route
と同じようにルートにアクセスすることができます。
<script setup>
const route = useRoute()
if (route.params.group === 'admins' && !route.params.id) {
console.log('Warning! Make sure user is authenticated!')
}
</script>
Catch all ルート
catch-all ルート(Next.js にあるやつ)が必要な場合は、[...slug].vue
のような名前のファイルを使用して作成します。これは、そのパスの下にある すべての ルートにマッチするので、非ダイナミック・テキストをサポートしません。
<template>
<p>{{ $route.params.slug }}</p>
</template>
/hello/world
に移動するとレンダリングされます:
<p>["hello", "world"]</p>
ネストしたルート
<NuxtPage>
を使用して、ネストしたルート
を表示することができます。
例:
-| pages/
---| parent/
------| child.vue
---| parent.vue
このファイルツリーは、これらのルートを生成します:
[
{
path: '/parent',
component: '~/pages/parent.vue',
name: 'parent',
children: [
{
path: 'child',
component: '~/pages/parent/child.vue',
name: 'parent-child'
}
]
}
]
child.vue
コンポーネントを表示するには、pages/parent.vue
の中に <NuxtPage>
コンポーネントを挿入する必要があります:
<template>
<div>
<h1>I am the parent view</h1>
<NuxtPage :foobar="123" />
</div>
</template>
子ルートキー
<NuxtPage>
コンポーネントが再レンダリングされるタイミングをより細かく制御したい場合(例えば、遷移のため)、pageKey
プロパティを通じて文字列または関数を渡すか、definePageMeta
を通じて key
値を定義することが可能です:
<template>
<div>
<h1>I am the parent view</h1>
<NuxtPage :page-key="someKey" />
</div>
</template>
もしくは、代わりとして:
<script setup>
definePageMeta({
key: route => route.fullPath
})
</script>
ページメタデータ
アプリ内の各ルートに対してメタデータを定義したい場合があります。これは、definePageMeta
マクロを使用して行うことができ、<script>
と <script setup>
の両方で機能します:
<script setup>
definePageMeta({
title: 'My home page'
})
</script>
このデータは、アプリの残りの部分で route.meta
オブジェクトからアクセスすることができます。
<script setup>
const route = useRoute()
console.log(route.meta.title) // 自分のホームページ
</script>
ネストされたルートを使用している場合、これらすべてのルートからのページメタデータは、単一のオブジェクトにマージされます。ルートメタの詳細については、vue-router のドキュメント
を参照してください。
defineEmits
や defineProps
(Vue のドキュメント
を参照)と同様に、definePageMeta
は コンパイラマクロ です。これはコンパイルされてしまうので、コンポーネント内で参照することはできません。その代わり、このマクロに渡されたメタデータは、コンポーネントの外に持ち出されます。したがって、ページメタオブジェクトはコンポーネント(またはコンポーネント上で定義された値)を参照することはできません。しかし、インポートされたバインディングを参照することはできます。
<script setup>
import { someData } from '~/utils/example'
const title = ref('')
definePageMeta({
title, // これはエラーになります
someData
})
</script>
特別なメタデータ
もちろん、アプリ内で使用するメタデータは自由に定義していただいてかまいません。しかし、definePageMeta
で定義されるメタデータの中には、特定の目的を持ったものがあります:
keepalive
Nuxt は definePageMeta
で keepalive: true
を設定すると、自動的に Vue の <KeepAlive> コンポーネント
でページをラップしてくれます。これは、例えば、動的な子ルートを持つ親ルートで、ルートが変わってもページの状態を保持したい場合に便利でしょう。また、<KeepAlive>
に渡す props を設定することもできます(詳しくは こちら
をご覧ください)。
key
ルートキー
の項目を確認してください。
layout
ルートのレンダリングに使用するレイアウトを定義することができます。これは false (レイアウトを無効にする)、文字列、または何らかの方法で反応させたい場合は ref/computed のいずれかになります。詳細は、レイアウトディレクトリ
の項目をご覧ください。
middleware
このページを読み込む前に適用するミドルウェアを定義することができます。このミドルウェアは、一致する親子ルートで使用されている他のすべてのミドルウェアと統合されます。文字列、関数(global before guard パターン
に従った匿名/インライン化されたミドルウェア関数)、または文字列/関数の配列が利用できます。詳細は、名前付きミドルウェア
の項目をご覧ください。
layoutTransition
と pageTransition
ページやレイアウトをラップする <transition>
コンポーネントに遷移のプロパティを定義したり、false
を渡してそのルートの <transition>
ラッパーを無効にしたりすることが可能です。詳細は、トランジション
の項目をご覧ください。
ナビゲーション
アプリケーションのページ間を移動するには、<NuxtLink>
コンポーネントを使用する必要があります。
このコンポーネントは Nuxt に含まれているため、他のコンポーネントのようにインポートする必要はありません。
HTML の <a>
タグと似ていますが、href="/about"
の代わりに to="/about"
を使用する点が異なります。vue-router
を使ったことがあるなら、<NuxtLink>
は <RouterLink>
の代わりと考えることができます。
これは、pages
フォルダーにある index.vue
のページへのシンプルなリンクです:
<template>
<NuxtLink to="/">Home page</NuxtLink>
</template>
また、<NuxtLink>
コンポーネントは、すべての内部リンクに使用する必要があります。つまり、サイト内のページへのリンクはすべて <NuxtLink>
を使用する必要があります。<a>
タグは、すべての外部リンクに使用されます。つまり、他のサイトへのリンクは <a>
タグを使用します。
<template>
<div>
<h1>Home page</h1>
<NuxtLink to="/about">
About (internal link that belongs to the Nuxt App)
</NuxtLink>
<a href="https://nuxtjs.org">External Link to another page</a>
</div>
</template>
ルーターオプション
デフォルトの vue-router のオプション
を設定することができます。
注意: history
と routes
のオプションは、常に Nuxt によってオーバーライド(ある場所で定義された設定や属性などを、別の定義で上書きすること)されます。
app/router.options
の使い方
ルーターオプションの指定には、この方法が推奨されます。
import type { RouterOptions } from '@nuxt/schema'
// https://router.vuejs.org/api/#routeroptions
export default <RouterOptions>{
}
nuxt.config
の使い方
注意: JSON シリアライズ(オプジェクト化されたデータを JSON 文字列に変換すること)可能なオプションのみ設定可能です。
linkActiveClass
linkExactActiveClass
end
sensitive
strice
export default defineNuxtConfig({
router: {
// https://router.vuejs.org/api/#routeroptions
options: {}
}
})
Plugins ディレクトリ
Nuxt は plugins ディレクトリにあるファイルを自動的に読み込んでロードします。ファイル名に .server または .client という接尾辞を使用すると、サーバサイドまたはクライアントサイドでのみプラグインを読み込むことができます。
どのファイルが登録されるか
plugins/
ディレクトリのトップレベルにあるファイル(またはサブディレクトリ内のインデックスファイル)だけがプラグインとして登録されます。
例えば:
plugins
| - myPlugin.ts
| - myOtherPlugin
| --- supportingFile.ts
| --- componentToRegister.vue
| --- index.ts
myPlugin.ts
と myOtherPlugin/index.ts
のみが登録されることになります。
プラグインを作成する
プラグインに渡される唯一の引数は nuxtApp
です。
export default defineNuxtPlugin(nuxtApp => {
// nuxtApp でなにかする
})
自動的にヘルパーを提供する
NuxtApp
インスタンスにヘルパーを提供したい場合、プラグインからヘルパーをprovide
キーで返します。例えば:
export default defineNuxtPlugin(() => {
return {
provide: {
hello: () => 'world'
}
}
})
別のファイルで、これを使うことができます:
<template>
<div>
{{ $hello() }}
</div>
</template>
<script setup lang="ts">
// または、このように使うこともできます
const { $hello } = useNuxtApp()
</script>
タイピングプラグイン
プラグインからヘルパーを返すと、自動的に型付けされます。useNuxtApp()
の戻り値やテンプレート内で型付けされているのがわかると思います。
高度なプラグイン
高度な使用例では、以下のように注入されたプロパティの型を宣言することができます:
declare module '#app' {
interface NuxtApp {
$hello (msg: string): string
}
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$hello (msg: string): string
}
}
export { }
Vue プラグイン
Google Analytics のタグを追加する vue-gtag
のように、Vue のプラグインを使用したい場合は、Nuxt のプラグインを使用することができます。
これをもっと簡単にするための Open RFC(Request for Comments、インターネット技術の標準的な仕様を記した文書)があります!
nuxt/framework#1175
を参照してください。
まず、必要なプラグインをインストールします。
yarn add --dev vue-gtag-next
それから、プラグインファイル plugins/vue-gtag.client.js
を作成します。
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
property: {
id: 'GA_MEASUREMENT_ID'
}
})
})
Public ディレクトリ
public/
ディレクトリは、サーバールートに直接提供され、名前を維持しなければならない(例: robots.txt
)、またはおそらく変更されない(例: favicon.ico
)公開ファイルを含んでいます。
Server ディレクトリ
Nuxt は、server/ ディレクトリを使用して、アプリケーションのあらゆるバックエンドロジックを作成します。HMR(Hot Module Replacement、画面の再描画なしに JS の変更をブラウザに適用する機能)をサポートし、強力な機能を備えています。
server/
ディレクトリには、プロジェクトで使用する API エンドポイントやサーバーミドルウェアが含まれます。
API ルート
Nuxt は、~/server/api
ディレクトリにあるファイルを自動的に読み込んで、API エンドポイントを作成します。
各ファイルは、API リクエストを処理するデフォルトの関数をエクスポートする必要があります。これは、promise または JSON データを直接返すことができます(または、res.end()
を使用します)。
例
Hello World
export default (res, req) => 'Hello World'
http://localhost:3000/api/hello
で結果をご覧ください。
Async 関数
export default async (req, res) => {
await someAsyncFunction()
return {
someData: true
}
}
例: Node.js を使ったスタイル
import type { IncomingMessage, ServerResponse } from 'http'
export default async (req: IncomingMessage, res: ServerResponse) => {
res.statusCode = 200
res.end('Works!')
}
リクエストデータへのアクセス
import { useBody, useCookies, useQuery } from 'h3'
export default async (req, res) => {
const query = useQuery(req)
const body = await useBody(req) // only for POST request
const cookies = useCookies(req)
return { query, body, cookies }
}
詳しくは、he methods
をご覧ください。
サーバーミドルウェア
Nuxt は、~/server/middleware
にあるファイルを自動的に読み込んで、プロジェクト用のサーバーミドルウェアを作成します。
これらのファイルは、独自のルートにマッピングされる API ルート
とは異なり、すべてのリクエストで実行されます。これは、通常すべてのレスポンスに共通のヘッダーを追加したり、レスポンスを記録したり、リクエストチェーン(最初の API コールから返されたデータを使って、別のAPIコールを行うこと)であとで使えるように受信リクエストオブジェクトを変更したりするためです。
各ファイルは、リクエストを処理するためのデフォルトの関数をエクスポートする必要があります。
export default async (req, res) => {
req.someValue = true
}
req
/res
オブジェクトにはなんの違いもないので、タイプするのは簡単です。
import type { IncomingMessage, ServerResponse } from 'http'
export default async (req: IncomingMessage, res: ServerResponse) => {
req.someValue = true
}
アプリケーションのより深い部分にリクエストを渡すには、関数の内部で返すことができます。
export default async (req, res) => {
const isNotHandledByThisMiddleware = req.url.includes('/some-unhandled-url-path/')
if(isNotHandledByThisMiddleware) {
return
}
// ここに実際のロジックを書く
}
Gitignore ファイル
.gitignore ファイルは、git が無視すべき、意図的に追跡されていないファイルを指定します。詳しくは、git のドキュメントをご覧ください。
.gitignore
ファイルには、少なくとも 以下の項目を記述しておくことを推奨します。
# Nuxt dev/build 出力
.output
.nuxt
# Node 依存関係
node_modules
# システムファイル
*.log
App ファイル
app.vue ファイルは、Nuxt 3 アプリケーションの主要なコンポーネントです。
最小限の使い方
Nuxt 3 では、pages/
ディレクトリはオプションです。存在しない場合、Nuxt は vue-router
の依存関係を含めません。これは、ランディングページやルーティングを必要としないアプリケーションで作業するときに便利です。
<template>
<h1>Hello World!</h1>
</template>
pages ディレクトリの使い方
pages/
ディレクトリがある場合、現在のページを表示するには、<NuxtPage>
コンポーネントを使用します:
<template>
<div>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</div>
</template>
あなたが、ページ間のページ周りの構造をカスタマイズする可能性があるなら、layout/
ディレクトリをチェックしてみてください。
Nuxt 設定ファイル
Nuxt は、拡張子が .js, .ts, .mjs のいずれかの nuxt.config ファイル 1 つで簡単に設定することができます。
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
// 自分の Nuxt の設定
})
すべての異なる設定プロパティを確認する。
alias
-
Type:
object
- Default
{
"~~": "/<rootDir>",
"@@": "/<rootDir>",
"~": "/<rootDir>",
"@": "/<rootDir>",
"assets": "/<rootDir>/assets",
"public": "/<rootDir>/public"
}
- Version: 2, 3
JavaScript や CSS 内のカスタムディレクトリにアクセスするためのエイリアスを追加定義することで、DX を向上させることができます。
例:
import { resolve } from 'pathe'
export default {
alias: {
'images': resolve(__dirname, './assets/images'),
'style': resolve(__dirname, './assets/style'),
'data': resolve(__dirname, './assets/other/data')
}
}
app
Nuxt アプリの構成
baseURL
-
Type:
string
- Default
"/"
- Version: 2, 3
Nuxt アプリケーションの基本パス。
これは、環境変数 BASE_PATH を設定することで、実行時に設定することができます。
BASE_PATH=/prefix/ node .output/server/index.mjs
buildAssetsDir
buildDir
-
Type:
string
- Default
"/_nuxt/"
- Version: 2, 3
構築されたサイトアセットのフォルダ名で、
baseURL
(設定されている場合はcdnURL
)に対する相対パスです。これは構築時に設定されるため、実行時にカスタマイズする必要はありません。
cdnURL
- Default
null
- Version: 2, 3
公開フォルダを提供する絶対 URL(本番環境のみ)。
これは、環境変数 CDN_URL の設定により、実行時に別の値を設定することができます。
例:
CDN_URL=https://mycdn.org/ node .output/server/index.mjs
buildModules
-
Type:
array
- Version: 2, 3
開発・ビルド時にのみ必要とされるモジュール。
モジュールは Nuxt の拡張機能で、コア機能を拡張し、無限の統合機能を追加することができます。 各モジュールは文字列(パッケージを参照したり、ファイルへのパス)、最初の文字列がモジュール、2 番目のオブジェクトがオプションのタプル、またはインラインモジュール関数のいずれかです。Nuxt はモジュール配列の各アイテムを node require path (node_modules
の中で) を使って解決しようとし、~
alias が使われている場合はプロジェクトの srcDir
から解決されます。
例:
modules: [
// Using package name
'@nuxtjs/axios',
// Relative to your project srcDir
'~/modules/awesome.js',
// Providing options
['@nuxtjs/google-analytics', { ua: 'X1234567' }],
// Inline definition
function () {}
]
css
-
Type:
array
- Version: 2, 3
グローバルに設定したい(すべてのページに含まれる)CSSファイル/モジュール/ライブラリを定義することができます。
Nuxt は拡張子から自動的にファイルの種類を推測し、適切なプリプロセッサを使用します。ただし、必要なローダーを使用する場合は、インストールする必要があります。
例:
css: [
// Load a Node.js module directly (here it's a Sass file)
'bulma',
// CSS file in the project
'@/assets/css/main.css',
// SCSS file in the project
'@/assets/css/main.scss'
]
dev
dir
extends
extensions
hooks
ignore
ignoreOptions
ignorePrefix
meta
modules
postcss
privateRuntimeConfig
publicRuntimConfig
rootDir
serverMiddleware
srcDir
ssr
vite
watchers
webpack
Nuxt ignore ファイル
.nuxtignore ファイルを使用すると、ビルド時にプロジェクトのルートディレクトリ(rootDir)にある layout、pages、components、composables、middleware のファイルを Nuxt が無視するように設定できます。.nuxtignore ファイルは .gitignore や .eslintignore ファイルと同じ仕様で、各行が無視すべきファイルを示す glob パターン(ワイルドカードなどでファイルパスを表記できるもの)になっています。
注意: nuxt.config
ファイルで ignoreOptions
、ignorePrefix
と ignore
を設定することもできます。
例
# layout/foo.vue を無視する
layouts/foo.vue
# 最後に -ignore.vue と名の付く layout ファイルを無視する
layouts/*-ignore.vue
# pages/bar.vue を無視する
pages/bar.vue
# pages/ignore ディレクトリ下のファイルをすべて無視する
pages/ignore/*.vue
# middleware/foo/bar.js を除く middleware/foo ディレクトリ下のファイルをすべて無視する
middleware/foo/*.js
!middleware/foo/bar.js
詳細は、
gitignore ドキュメント
を確認してください。
Package ファイル
package.json ファイルはアプリケーションのすべての依存関係とスクリプトを含みます(詳細を確認する)。
TypeScript 設定ファイル
Nuxt は、Nuxt プロジェクトで使用している解決済みのエイリアスを含む .nuxt/tsconfig.json ファイル、および他の賢明なデフォルトを自動的に生成します。プロジェクトのルートに以下のような内容の tsconfig.json を作成することで、この恩恵を受けることができます。
必要に応じて、このファイルの内容をカスタマイズすることができます。ただし、パス
をカスタマイズする必要がある場合、自動生成されたパスエイリアスを上書きしてしまうことに注意してください。その代わりに、必要なパスエイリアスは nuxt.config
内の alias
プロパティに追加して、自動生成される tsconfig
に取り込まれるようにすることをお勧めします。
概要
Nuxt 3 は Nuxt 2 の完全な書き直しであり、また新しい一連の基礎技術に基づくものです。つまり、Nuxt 2 のアプリを Nuxt 3 に移行する際には大きな変化が生じますが、安定版リリースに向けて移行はより簡単になると思われます。
これらの重要な変更点の一部を紹介します:
- Composition API とscript setup のデフォルト化を含む Vue 2 から Vue 3 への移行
- webpack 4 と Babel から Vite または webpack 5 と esbuild への移行
- ランタイムの Nuxt 依存から、nitropack でコンパイルされた最小限のスタンドアローンサーバー(単独で動作しているシステム)に移行しました
構成
nuxt.config
Nuxt アプリケーションの出発点は、nuxt.config
ファイルのままです。
移行
- 型付き設定スキーマを提供する新しい関数
defineNuxtConfig
に移行する必要があります。
export default {
// ...
}
or
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
// ...
})
-
router.extendRoutes
を使用していた場合、新しいpages/extend
フックに移行できます:
export default {
router: {
extendRoutes (routes) {
//
}
}
}
or
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
hooks: {
'pages:extend' (routes) {
//
}
}
})
ESM 構文
Nuxt 3 は、ESM ネイティブフレームワーク
です。unjs/jiti
は、nuxt.config
フィアルを読み込む際に半互換性を提供しますが、このファイルでは、require
と module.exports
の使用は避けてください。
-
module.exports
をexport default
に変更する -
const lib = require('lib')
をimport lib from 'lib'
に変更する
Async 構成
Nuxt の読み込み動作をより予測しやすくするために、非同期設定構文は非推奨となります。非同期操作には、Nuxt フックを使用することを検討してください。
Dotenv
Nuxt は、.env
ファイルを読み込むためのサポートを内蔵しています。nuxt.config
から直接インポートするのは避けてください。
モジュール
Nuxt と Nuxt モジュールは、ビルド時のみになりました。
移行
-
buildModule
をすべてmodule
に移動させる - モジュールの Nuxt 3 との互換性を確認する
TypeScript
Nuxt の TypeScript の統合を使用すると、アプリケーションの移行がより簡単になります。これは、アプリケーションを TypeScript で書く必要があるということではなく、Nuxt がエディタに自動的に型ヒントを提供するということです。
Nuxt の TypeScript サポートについては ドキュメント
を参照してください。
移行
- 以下の内容で tsconfig.json を作成します
{
"extends": "./.nuxt/tsconfig.json"
}
-
npx nuxi prepare
を実行して、.nuxt/tsconfig.json
を生成します -
ドキュメント
にある指示に従って Volar をインストールします
Vue の変更点
Vue のベストプラクティスとして推奨されているものに多くの変更があり、また Vue 2 と 3 の間には多くの破壊的な変更があります。
Vue 3 への移行ガイド
と、特に 破壊的な変更点のリスト
を読むことを推奨します。
現在、Nuxt 3 RC で Vue 3 の移行ビルドを使用することはできません。
Vuex
Nuxt は Vuex との統合を提供しなくなりました。代わりに、Vue の公式の推奨は、Nuxt モジュールを介して Nuxt サポートを内蔵している pinia を使用することです。pinia の詳細については、こちらをご覧ください
。
Vuex を使い続けたい場合は、以下の手順
で手動で Vuex 4 に移行することができます。
自動インポート
Nuxt 3 は、最小限の摩擦で済むアプローチを採用しており、可能な限りコンポーネントやコンポーザブルを自動でインポートすることができます。
自動インポートについてもっと読む
移行
- Nuxt 2 で
@nuxt/components
を使用していた場合、nuxt.config
のcomponents: true
を削除してください。もし、もっと複雑な設定をしていたのであれば、コンポーネントのオプションが多少変更されていることに注意してください。詳しくは、components のドキュメント
をご覧ください。
メタタグ
Nuxt 3 では、メタタグを管理する方法がいくつか用意されています。
-
nuxt.config
を通じて -
useMeta
コンポーザブルを通じて -
グローバルメタコンポーネント
を通じて
title
、base
、script
、stye
、meta
、link
、htmlAttrs
、bodyAttrs
をカスタマイズすることが可能です。
メタタグについてもっと読む。
移行
-
nuxt.config
で、head
をmeta
に名前変更してください。この共有された meta の設定を、代わりにapp.vue
に移行することを検討してください。(オブジェクトは、重複排除のためのhid
キーを持たなくなったことに注意してください) - コンポーネントでは、
head
オプションの名前をmeta
に変更します。コンポーネントの状態にアクセスする必要がある場合は、useMeta
を使用するように移行する必要があります。また、内蔵のメタコンポーネントの使用も検討してください。
useMeta
例: <script>
export default {
data: () => ({
title: 'My App',
description: 'My App Description'
})
head () {
return {
title: this.title,
meta: [{
hid: 'description',
name: 'description',
content: this.description
}]
}
}
}
</script>
or
<script setup>
const title = ref('My App')
const description = ref('My App Description')
// This will be reactive even you change title/description above
useMeta({
title,
meta: [{
name: 'description',
content: description
}]
})
</script>
例: 内蔵のメタコンポーネント
Nuxt 3 には、同じタスクを実行するために使用できるメタコンポーネントも用意されています。これらのコンポーネントは HTML タグに似ていますが、Nuxt によって提供され、同様の機能を備えています。
<script>
export default {
head () {
return {
title: 'My App',
meta: [{
hid: 'description',
name: 'description',
content: 'My App Description'
}]
}
}
}
</script>
or
<template>
<div>
<Head>
<Title>My App</Title>
<Meta name="description" content="My app description"/>
</Head>
<!-- -->
</div>
</template>
プラグインとミドルウェア
プラグイン
プラグインは異なるフォーマットを持ち、1 つの引数(nuxtApp
) を取るようになりました。詳しくは、ドキュメント
をご覧ください。
export default (ctx, inject) => {
inject('injected', () => 'my injected function')
})
or
export default defineNuxtPlugin(nuxtApp => {
// `nuxtApp.$injected` が使用可能になりました
nuxtApp.provide('injected', () => 'my injected function')
// 代わりにこのフォーマットは、自動型サポート付きです
return {
provide: {
injected: () => 'my injected function'
}
}
})
移行
-
defineNuxtPlugin
ヘルパー関数を使用するようにプラグインを移行します -
plugins
フォルダにあるnuxt.cofig
プラグイン配列のエントリーをすべて削除してください。このディレクトリのトップレベルにあるすべてのファイル(および任意のサブディレクトリにあるインデックスファイル)は自動的に登録されます。モードをクライアントまたはサーバーに設定する代わりに、ファイル名でそれを示すことができます。例えば、~/plugins/my-plugin.client.ts
はクライアントサイドでのみ読み込まれます。
ルートミドルウェア
ルートミドルウェアは、別の形式を持ちます。
export default function ({ store, redirect }) {
// ユーザーの認証がされていない場合
if (!store.state.authenticated) {
return redirect('/login')
}
}
or
export default defineNuxtRouteMiddleware((to, from) => {
const auth = useState('auth')
if (!auth.value.authenticated) {
return navigateTo('/login')
}
})
Nuxt 2 と同様、~/middleware
フォルダに配置されたルートミドルウェアが自動的に登録されます。そのあと、コンポーネントで名前を付けて指定することができます。ただし、これはコンポーネントのオプションとしてではなく、definePageMeta
で行われます。
navigateTo
は、いくつかのルートヘルパー関数のひとつで、詳細は ルートミドルウェアに関するドキュメント
をご覧ください。
移行
-
defineNuxtRouteMiddleware
ヘルパー関数を使用するようにルートミドルウェアを移行してください。 - グローバルミドルウェア(
nuxt.config
など)は、例えば~/middleware/auth.global.ts
のように.global
という拡張子をつけて~/middleware
フォルダに配置することができます。
pages とレイアウト
app.vue
Nuxt 3 は、~/app.vue
を介してアプリへのセントラルエントリーポイントを提供します。もし、ソースディレクトリに app.vue
ファイルがなければ、Nuxt は独自のデフォルトバージョンを使用します。
このファイルは、アプリの起動時に一度だけ実行する必要のあるカスタムコードや、アプリの各ページに存在するコンポーネントを置くのに最適な場所です。例えば、レイアウトが 1 つしかない場合は、代わりに app.vue
に移動させることができます。
app.vue
についてもっと読む
移行
-
app.vue
ファイルを作成し、アプリのトップレベルで一度だけ実行する必要があるロジックを含めることを検討してください。ここで、その例を確認することができます
。
レイアウト
アプリで複数のページにレイアウトを使用している場合は、わずかな変更で済みます。
Nuxt 2 では、<Nuxt>
コンポーネントはレイアウト内で現在のページをレンダリングするために使用されます。Nuxt 3 では、レイアウトは代わりにスロットを使用するため、そのコンポーネントを <slot />
に置き換える必要があります。これにより、名前付きスロットやスコープ付きスロットを使った高度なユースケースも可能になります。レイアウトについてもっと読む
。
また、definePageMeta
コンパイラマクロを使用して、ページで使用されるレイアウトを定義する方法を変更する必要があります。レイアウトはケバブケースになります。
そのため、layouts/customLayout.vue
は、ページ内で参照されると custom-layout
になります。
移行
-
<Nuxt />
を<slot />
に置き換える。 -
definePageMeta
を使って、ページで使用するレイアウトを選択します。 -
~/layouts/_error.vue
を~/error.vue
に移動します。エラー処理のドキュメント
を参照してください。
~/layouts/custom.vue
例: <template>
<div id="app-layout">
<main>
- <Nuxt />
+ <slot />
</main>
</div>
</template>
<script>
+ definePageMeta({ layout: 'custom' })
export default {
- layout: 'custom'
}
</script>
ページ
Nuxt 3 には、ソースディレクトリに pages/
ディレクトリが存在することをトリガーとして、vue-router
の統合がオプションで用意されています。もし、1 ページしかないのであれば、app.vue
に移動してビルドを軽くすることを検討してもよいでしょう。
動的なルート
Nuxt 3 の動的なルートの定義形式は Nuxt 2 と若干異なるため、pages/
内のファイル名を一部変更する必要があるかもしれません。
- 以前は動的経路のパラメータを定義するのに
_id
を使用していましたが、現在は[id]
を使用します。 - 以前は
_.vue
を使ってキャッチオールルートを定義していましたが、現在は[...slug].vue
を使っています。
ネストされたルート
Nuxt 2 では、<Nuxt>
と <NuxtChild>
を使用して、(親と子のコンポーネントを持つ)ネストされたルートを定義していました。Nuxt 3 では、これらは単一の <NuxtPage>
コンポーネントに置き換えられました。
Page key と keep-alive props
<Nuxt>
にカスタム ページ キーまたは keep-alive props を渡していた場合、definePageMeta
を使用してこれらのオプションを設定できるようになりました。
詳しくは、Nuxt コンポーネントフックの移行
を参照してください。
ページとレイアウトの遷移
ページやレイアウトの遷移をコンポーネントオプションで直接定義していた場合、definePageMeta
を使用して遷移を設定する必要があります。
pages/
の詳細についてはこちらをご覧ください。
移行
- 動的なパラメータを持つページの名前を、新しい形式に合うように変更します。
-
<Nuxt>
と<NuxtChild>
を<NuxtPage>
に更新してください。 - Composition API を使用している場合は、
this.$route
とthis.$router
もuseRoute
とuseRouter
というコンポーザブルを使用するように移行してください。
例: 動的なルート
- URL: /users
- Page: /pages/users/index.vue
- URL: /users/some-user-name
- Page: /pages/users/_user.vue
- Usage: params.user
- URL: /users/some-user-name/edit
- Page: /pages/users/_user/edit.vue
- Usage: params.user
- URL: /users/anything-else
- Page: /pages/users/_.vue
- Usage: params.pathMatch
- URL: /users
- Page: /pages/users/index.vue
- URL: /users/some-user-name
- Page: /pages/users/[user].vue
- Usage: params.user
- URL: /users/some-user-name/edit
- Page: /pages/users/[user]/edit.vue
- Usage: params.user
- URL: /users/anything-else
- Page: /pages/users/[...slug].vue
- Usage: params.slug
definePageMeta
例: ネストされたルートと <template>
<div>
<NuxtChild keep-alive :keep-alive-props="{ exclude: ['modal'] }" :nuxt-child-key="$route.slug" />
</div>
</template>
<script>
export default {
transition: 'page' // or { name: 'page' }
}
</script>
<template>
<div>
<NuxtPage />
</div>
</template>
<script setup>
// このコンパイラマクロは、<script> と <script setup> のどちらでも動作します
definePageMeta({
// 文字列または計算されたプロパティを渡すこともできます
key: route => route.slug,
transition: {
name: 'page',
},
keepAlive: {
exclude: ['modal']
},
})
</script>
NuxtLink コンポーネント
ほとんどの構文と機能は、グローバルな NuxtLink
コンポーネントと同じです。ショートカットの <NLink>
形式を使用していた場合は、<NuxtLink>
を使用するように更新する必要があります。
<NuxtLink>
は、外部リンクを含むすべてのリンクのドロップイン置き換えになりました。このコンポーネントの詳細と、これを拡張して独自のリンク・コンポーネントを提供する方法については、ドキュメント
を参照してください。
プログラマティックなナビゲーション
Nuxt 2 から Nuxt 3 に移行する場合、ユーザーをプログラム的にナビゲートする方法を更新する必要があります。Nuxt 2 では、this.$router
を使用して基礎となる Vue Router にアクセスすることができました。Nuxt 3 では、ルートとパラメータを Vue Router に渡すことができる navigateTo()
ユーティリティメソッドを使用することができます。
注意: navigateTo
で常に 待ち受ける
か、関数から返すことでその結果を連鎖させるようにしてください。
<script>
export default {
methods: {
navigate(){
this.$router.push({
path: '/search',
query: {
name: 'first name',
type: '1'
}
})
}
}
}
</script>
<script setup>
const router = useRouter();
function navigate(){
return navigateTo({
path: '/search',
query: {
name: 'first name',
type: '1'
}
})
}
</script>
コンポーネントオプション
asyncData
と fetch
コンポーネントのオプション
Nuxt 3 は、API からデータをフェッチする
ための新しいオプションを提供します。
アイソモーフィックフェッチ
Nuxt 2 では、@nuxtjs/axios
または @nuxt/http
を使用してデータをフェッチするか、ポリフィルドのグローバルフェッチのみを使用することができます。
Nuxt 3 では、Fetch API
または unjs/ohmyfetch
を使用する $fetch
メソッドと同じ API を持つグローバルに利用可能な フェッチ
メソッドを使用することができます。これには以下のような利点があります:
- サーバー上で実行されている場合は、
直接 API を呼び出す
ことを「スマートに」処理し、クライアント上で実行されている場合は、あなたの API をクライアントサイドで呼び出すことを処理することができます。(サードパーティの API を呼び出すことも可能です)。 - さらに、レスポンスの自動解析やデータの文字列化など、便利な機能も備えています。
直接 API を呼び出したり
、データを取得したりする
方法について詳しく知れます。
コンポーザブルの使用
Nuxt 3 には、データを取得するための新しい複合変数、useAsyncData
と useFetch
があります。これらにはそれぞれ「遅延」バージョン(useLazyAsyncData
と useLazyFetch
)があり、クライアントサイドのナビゲーションをブロックしないようにすることができます。
Nuxt 2 では、以下のような構文でコンポーネントのデータをフェッチします:
export default {
async asyncData({ params, $http }) {
const post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
return { post }
},
// or alternatively
fetch () {
this.post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
}
}
メソッドやテンプレートの中で、コンポーネントが提供する他のデータを使用するのと同じように、post
変数を使用することができます。
Nuxt 3 では、setup()
メソッドまたは <script setup>
タグで composables
を使用してこのデータフェッチを行うことができます:
<script setup>
// Define params wherever, through `defineProps()`, `useRoute()`, etc.
const { data: post, refresh } = await useAsyncData('post', () => $fetch(`https://api.nuxtjs.dev/posts/${params.id}`) )
// Or instead - useFetch is a convenience wrapper around useAsyncData when you're just performing a simple fetch
const { data: post, refresh } = await useFetch(`https://api.nuxtjs.dev/posts/${paramsÌ.id}`)
</script>
Nuxt 3 テンプレート内で post
を使用したり、refresh
を呼び出してデータを更新することができるようになりました。
マイグレーション
- page/component 内の
asyncData
フックをuseAsyncData
またはuseFetch
に置き換えてください。 - コンポーネント内の
fetch
フックをuseAsyncData
またはuseFetch
に置き換えてください。
head
meta tag migration
を参照してください。
key
definePageMeta
コンパイラマクロ内でキーを定義できるようになりました。
- <script>
- export default {
- key: 'index'
- // or a method
- // key: route => route.fullPath
- }
+ <script setup>
+ definePageMeta({
+ key: 'index'
+ // or a method
+ // key: route => route.fullPath
+ })
</script>
移行
- コンポーネントオプションから
definePageMeta
にkey
を移行する。
layout
レイアウトの移行
を参照してください。
loading
この機能は Nuxt 3 ではまだサポートされていません。
middleware
ミドルウェアの移行
を参照してください。
scrollToTop
この機能は Nuxt 3 ではまだサポートされていません。vue-router
のデフォルトのスクロール動作を上書きしたい場合は、 ~/app/router.options.ts
で上書きできます(詳しくは ドキュメント
をご覧ください)。
transition
レイアウトの移行
を参照してください。
validate
Nuxt 3 では validate フックがなくなりました。代わりに、カスタムミドルウェア関数を作成するか、ページの設定関数で直接エラーを投げることができる。
- <script>
- export default {
- async validate({ params, query, store }) {
- return true // if valid
- }
- }
+ <script setup>
+ definePageMeta({
+ middleware: [
+ async function (to, from) {
+ const nuxtApp = useNuxtApp()
+ if (!valid) {
+ return abortNavigation('Page not found')
+ }
+ }
+ ]
+ })
</script>
watchQuery
これは Nuxt 3 ではサポートされていません。代わりに、データのリフェッチをトリガするために直接ウォッチャーを使用することができます。
<script setup>
const route = useRoute()
const { data, refresh } = await useFetch('/api/user')
watch(() => route.query, () => refresh())
</script>
ランタイム設定
Nuxt 3 アプリ内で環境変数を参照したい場合は、ランタイム設定を使用する必要があります。
コンポーネント内でこれらの変数を参照する場合、セットアップメソッド(または Nuxt プラグイン)内で useRuntimeConfig
コンポーザブルを使用する必要があります。アプリの server/
部分では、インポートなしで useRuntimeConfig
を使用することができます。
ランタイム設定についてもっと読む
。
移行
- アプリで使用する環境変数を
nuxt.config
ファイルのruntimeConfig
プロパティに追加します。 - アプリの Vue 部分全体で、
process.env
をuseRuntimeConfig
に移行します。
例
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
runtimeConfig: {
secretKey: '', // variable that can only be accessed on the server side
public: {
BASE_URL: process.env.BASE_URL || 'https://nuxtjs.org' // variable that can also be accessed on the client side
}
},
})
<script setup>
const config = useRuntimeConfig().public
// instead of process.env.BASE_URL you will now access config.BASE_URL
</script>
const config = useRuntimeConfig().public
export default (req, res) => {
// you can now access config.BASE_URL
return {
baseURL: config.BASE_URL
}
}
ビルドツール
デフォルトでは、以下のビルドツールを使用します。
-
Vite
またはwebpack
Rollup
PostCSS
esbuild
このため、nuxt.config
にある以前のビルド設定のほとんどは、カスタム babel
設定を含めて無視されるようになりました。
もし Nuxt のビルドツールのどれかを設定する必要がある場合は、新しいトップレベルの vite
, webpack
, postcss
キーを使って、nuxt.config
で設定することができます。
さらに、Nuxt は TypeScript をサポートしています。詳細はこちら
。
ステップ
-
nuxt/typescript-build
と@nuxt/typescript-runtime
を依存関係やモジュールから削除します。 -
babel
の未使用の依存関係をプロジェクトから削除します。 - 明示的な
core-js
の依存関係を削除します。 -
require
をimport
に移行する。
サーバー
ビルドされた Nuxt 3 アプリケーションでは、ランタイムの Nuxt 依存はありません。これは、あなたのサイトが高いパフォーマンスと超スリムになることを意味します。しかし、それは同時にランタイム Nuxt サーバーフックにフックすることができなくなることも意味します。
Nitro サーバーエンジンの詳細については、こちらをご覧ください
。
ステップ
-
nuxt.config
にあるrender
キーを削除します。 -
server/api
と~/server/middleware
にあるファイルは自動的に登録されるので、serverMiddleware
配列から削除してください。 -
serverMiddleware
配列の他のアイテムは、インライン関数ではなく、ファイルや npm パッケージを直接指定するように更新してください。 -
server:
やvue-renderer:
などのサーバーフックを追加している場合は、これらを削除して、ランタイムフックやプラグインのnitropack
サポートを待つ必要があります。
モジュール
モジュールの互換性
Nuxt 3 は @nuxt/kit
自働ラッパーを使用した Nuxt 2 モジュールの基本的な後方互換性レイヤーを備えています。しかし、通常、モジュールを Nuxt 3 と互換性を持たせるための手順があり、クロスバージョン互換のために Nuxt Bridge の使用が必要な場合もあります。
そこで、@nuxt/kit
を用いて Nuxt 3 対応モジュールを作成するための 専用ガイド
を作成しました。現在のところ、これに従ってモジュールを書き換えるのが最適な移行方法です。このガイドの残りの部分は、モジュールの全面的な書き換えを避け、Nuxt 3 と互換性のあるモジュールを作成するための準備手順を含んでいます。
プラグインの互換性
Nuxt 3 のプラグインは Nuxt 2 と完全な後方互換性がありません。
Vue との互換性
Composition API を使用するプラグインやコンポーネントは、Vue 2 または Vue 3 を排他的にサポートする必要があります。
vue-demi
を使用することで、Nuxt 2 と 3 の両方と互換性があるはずです。
モジュールの移行
Nuxt 3 ユーザーがあなたのモジュールを追加すると、@nuxt/kit
から互換性のあるモジュールコンテナレイヤーが 自動的に注入される ので、あなたのコードが以下のガイドラインに従っている限り、そのまま動作し続けるはずです。
nuxt/bridge
でテストする
nuxt/bridge
への移行は、Nuxt 3 をサポートするための最初の、そして最も重要なステップです。
モジュールにフィクスチャやサンプルがある場合は、@nuxt/bridge
パッケージをその設定に追加してください (例
を参照してください)。
CommonJS から ESM への移行
Nuxt 3 は TypeScript と ECMAScript Modules をネイティブにサポートしています。詳細とアップグレードについては、Native ES Modules
を参照してください。
プラグインがデフォルトでエクスポートするようにする
Vue のグローバルプラグインなど、export default
を持たない Nuxt プラグインを注入する場合は、export default () => { }
を末尾に追加することを確認してください。
// ~/plugins/vuelidate.js
import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
// ~/plugins/vuelidate.js
import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
export default () => { }
ランタイムモジュールの回避
Nuxt 3 では、Nuxt はビルド時のみの依存関係になりました。これは、モジュールがNuxt ランタイムにフックすることを試みてはならないことを意味します。
(modules
の代わりに)buildModules
に追加するだけでも、あなたのモジュールは動作するはずです。例えば:
- Nuxt モジュール内で
process.env
を更新して、Nuxt プラグインで読み込むことは避けてください。代わりにruntimeConfig
を使用してください。 - (*)
vue-renderer:*
のようなランタイムフックに依存した制作は避ける。 - (*)
serverMiddleware
をモジュール内部で import して追加することは避けてください。代わりに、ファイルパスを参照して追加し、モジュールのコンテキストに依存しないようにします。
(*) ただし、nuxt dev
の目的のみで、if (nuxt.options.dev) { }
でガードされている場合は別です。
TypeScript を使用する(オプション)
必須ではありませんが、Nuxt のエコシステムのほとんどが TypeScript を使用するように移行しているので、移行を検討することを強くお勧めします。
Node.js サーバー
Node.js のサーバープリセットは、Nitro を使用して、任意の Node ホスティングにデプロイすることを発見してください。
- ✅ 何も指定されていない場合、または自動検出された場合の デフォルトの出力形式
- ✅ 最適なコールドスタートのタイミングでリクエストをレンダリングするために必要なチャンクのみをロードします
- ✅ Nuxt アプリを任意の Node.js ホスティングにデプロイするのに便利です
エントリーポイント
Node サーバープリセットでnuxt buildを実行すると、すぐに実行できるNodeサーバを起動するエントリポイントが生成されます。
node .output/server/index.mjs
例
$ node .output/server/index.mjs
Listening on http://localhost:3000
実行時にデフォルトを設定する
このプリセットは、以下の実行時環境変数を尊重します。
-
NITRO_PORT
またはPORT
(デフォルトは3000
) -
NITRO_HOST
またはHOST
(デフォルトは'0.0.0.0'
) -
NITRO_SSL_CERT
とNITRO_SSL_KEY
- 両方とも存在する場合、サーバーを HTTPS モードで起動します。Nitro サーバは、nginx や Cloudflare などの SSL を終了するリバースプロキシで動作させる必要があります。
PM2 を使う
pm2
を使用するには、ecosystem.config.js
を使用します:
module.exports = {
apps: [
{
name: 'NuxtAppName',
exec_mode: 'cluster',
instances: 'max',
script: './.output/server/index.mjs'
}
]
}
詳細
Static ホスティング
Nuxt アプリケーションを任意の静的ホスティングサービスにデプロイするには、2 つの方法があります。
- 静的サイト生成(Static Site Generation: SSG)は、ビルド時にアプリケーションのすべてのルートを事前にレンダリングします。また、すべてのページに対して、Nuxt はクローラーを使用して対応する HTML ファイルを生成します。
-
ssr: false
を使用すると、純粋なクライアントサイド出力が生成されます。
プリレンダリング
nuxi generate
コマンドを使用してアプリケーションをビルドします。HTML ファイルは .output/public
ディレクトリに生成されます。
npx nuxi generate
クライアントサイドのみのレンダリング
ルートをプリレンダリングしたくない場合、静的ホスティングを使用する別の方法は、nuxt.config
ファイルで ssr
プロパティを false
に設定することです。そうすると、nuxi build
コマンドは、古典的なクライアントサイド Vue.js アプリケーションのように index.html
エントリーポイントを出力します。
defineNuxtConfig({
ssr: false
})
応用
Nitro
がビルド中に取得しプリレンダリングするルートを手動で指定することができます。
defineNuxtConfig({
nitro: {
prerender: {
routes: ['/user/1', '/user/2']
}
}
})
デプロイメントプリセット
Node.js サーバーと静的ホスティングサービスに加えて、Nuxt 3 プロジェクトは、十分にテストされたいくつかのプリセットと最小限の設定でデプロイすることが可能です。
Nuxt の設定
を使用して、使用するプリセットを明示的に設定することができます:
export default {
nitro: {
preset: 'node-server'
}
}
または、nuxt build
を実行する際に、直接 NITRO_PRESET
環境変数を使用します:
NITRO_PRESET=node-server nuxt build
🔎 可能なすべてのデプロイメントプリセットとプロバイダについて、Nitro デプロイメント
を確認します。
サポートしているホスティングプロバイダ
Nuxt 3 は、最小限の設定で複数のクラウドプロバイダーに導入することができます:
- ☁️ AWS
- 🅰️ Azure
- 🌩️ CloudFlare
- ☁️ Degital Ocean
- ⚡️ Firebase
- ☁️ heroku
- ☁️ layer0
- 💠 Netlify
- ☁️ Render
- ☁️ Stormkit
- 🔺 Vercel
Nuxt はどのように動いているのか?
Nuxt は、Web アプリケーションを構築するための最小限の、しかし高度にカスタマイズ可能なフレームワークです。このガイドは、Nuxt の内部をよりよく理解し、Nuxt の上に新しいソリューションやモジュール統合を開発するのに役立ちます。
Nuxt のインターフェース
nuxi dev
で開発モード、または nuxi build
で本番アプリケーションをビルドして nuxt を起動すると、内部で nuxt
と呼ばれる共通のコンテキストが作成されます。これは、nuxt.config
ファイルでマージされた正規化されたオプション、いくつかの内部状態、そして unjs/hookable
で駆動する強力なフッキングシステム
を持ち、異なるコンポーネントが互いに通信することを可能にします。これは Builder Core と考えることができます。
このコンテキストは、nuxt/kit
コンポーザブルでグローバルに利用可能です。そのため、1 プロセスあたり 1 つの Nuxt のインスタンスしか実行できません。
Nuxt のインターフェースを拡張し、ビルドプロセスの異なるステージにフックするには、Nuxt Modules
を使用します。
詳しくは ソースコード
を見てください。
import { resolve } from 'pathe'
import { createHooks } from 'hookable'
import type { Nuxt, NuxtOptions, NuxtConfig, ModuleContainer, NuxtHooks } from '@nuxt/schema'
import { loadNuxtConfig, LoadNuxtOptions, nuxtCtx, installModule, addComponent, addVitePlugin, addWebpackPlugin, tryResolveModule } from '@nuxt/kit'
// Temporary until finding better placement
/* eslint-disable import/no-restricted-paths */
import pagesModule from '../pages/module'
import metaModule from '../head/module'
import componentsModule from '../components/module'
import autoImportsModule from '../auto-imports/module'
/* eslint-enable */
import { distDir, pkgDir } from '../dirs'
import { version } from '../../package.json'
import { ImportProtectionPlugin, vueAppPatterns } from './plugins/import-protection'
import { UnctxTransformPlugin } from './plugins/unctx'
import { addModuleTranspiles } from './modules'
import { initNitro } from './nitro'
export function createNuxt (options: NuxtOptions): Nuxt {
const hooks = createHooks<NuxtHooks>()
const nuxt: Nuxt = {
_version: version,
options,
hooks,
callHook: hooks.callHook,
addHooks: hooks.addHooks,
hook: hooks.hook,
ready: () => initNuxt(nuxt),
close: () => Promise.resolve(hooks.callHook('close', nuxt)),
vfs: {}
}
return nuxt
}
async function initNuxt (nuxt: Nuxt) {
// Register user hooks
nuxt.hooks.addHooks(nuxt.options.hooks)
// Set nuxt instance for useNuxt
nuxtCtx.set(nuxt)
nuxt.hook('close', () => nuxtCtx.unset())
// Add nuxt types
nuxt.hook('prepare:types', (opts) => {
opts.references.push({ types: 'nuxt' })
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/plugins.d.ts') })
// Add vue shim
if (nuxt.options.typescript.shim) {
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/vue-shim.d.ts') })
}
// Add module augmentations directly to NuxtConfig
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/schema.d.ts') })
})
// Add import protection
const config = {
rootDir: nuxt.options.rootDir,
patterns: vueAppPatterns(nuxt)
}
addVitePlugin(ImportProtectionPlugin.vite(config))
addWebpackPlugin(ImportProtectionPlugin.webpack(config))
// Add unctx transform
addVitePlugin(UnctxTransformPlugin(nuxt).vite({ sourcemap: nuxt.options.sourcemap }))
addWebpackPlugin(UnctxTransformPlugin(nuxt).webpack({ sourcemap: nuxt.options.sourcemap }))
// Init user modules
await nuxt.callHook('modules:before', { nuxt } as ModuleContainer)
const modulesToInstall = [
...nuxt.options.buildModules,
...nuxt.options.modules,
...nuxt.options._modules
]
// Add <NuxtWelcome>
addComponent({
name: 'NuxtWelcome',
filePath: tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')
})
addComponent({
name: 'NuxtLayout',
filePath: resolve(nuxt.options.appDir, 'components/layout')
})
// Add <NuxtErrorBoundary>
addComponent({
name: 'NuxtErrorBoundary',
filePath: resolve(nuxt.options.appDir, 'components/nuxt-error-boundary')
})
// Add <ClientOnly>
addComponent({
name: 'ClientOnly',
filePath: resolve(nuxt.options.appDir, 'components/client-only')
})
// Add <ServerPlaceholder>
addComponent({
name: 'ServerPlaceholder',
filePath: resolve(nuxt.options.appDir, 'components/server-placeholder')
})
// Add <NuxtLink>
addComponent({
name: 'NuxtLink',
filePath: resolve(nuxt.options.appDir, 'components/nuxt-link')
})
for (const m of modulesToInstall) {
if (Array.isArray(m)) {
await installModule(m[0], m[1])
} else {
await installModule(m, {})
}
}
await nuxt.callHook('modules:done', { nuxt } as ModuleContainer)
await addModuleTranspiles()
// Init nitro
await initNitro(nuxt)
await nuxt.callHook('ready', nuxt)
}
export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
const options = await loadNuxtConfig(opts)
// Temporary until finding better placement for each
options.appDir = options.alias['#app'] = resolve(distDir, 'app')
options._majorVersion = 3
options._modules.push(pagesModule, metaModule, componentsModule, autoImportsModule)
options.modulesDir.push(resolve(pkgDir, 'node_modules'))
options.build.transpile.push('@nuxt/ui-templates')
options.alias['vue-demi'] = resolve(options.appDir, 'compat/vue-demi')
options.alias['@vue/composition-api'] = resolve(options.appDir, 'compat/capi')
if (options.telemetry !== false && !process.env.NUXT_TELEMETRY_DISABLED) {
options._modules.push('@nuxt/telemetry')
}
const nuxt = createNuxt(options)
if (opts.ready !== false) {
await nuxt.ready()
}
return nuxt
}
export function defineNuxtConfig (config: NuxtConfig): NuxtConfig {
return config
}
// For a convenience import together with `defineNuxtConfig`
export type { NuxtConfig }
NuxtApp インターフェース
ブラウザやサーバでページをレンダリングするとき、nuxtApp
と呼ばれる共有コンテキストが作成されます。このコンテキストは、vue インスタンス、ランタイムフック、そして ssrContext やハイドレーションのためのペイロードなどの内部ステートを保持します。ランタイムコア と考えることができます。
このコンテキストは、nuxt プラグイン内の useNuxtApp()
や <script setup>
、vue のコンポーザブルを使ってアクセスすることができます。ユーザー間でコンテキストを共有しないように、ブラウザではグローバルな利用が可能ですが、サーバーでは利用できません。
nuxtApp
インターフェースを拡張し、異なるステージやアクセスコンテキストにフックするには、Nuxt Plugins
を使うことができます。
このインターフェイスの詳細については Nuxt App
を参照してください。
nuxtApp
は以下のプロパティを持ちます。
注意: これは内部的なインターフェイスであり、安定版リリースまでいくつかのプロパティが変更される可能性があります。
const nuxtApp = {
vueApp, // the global Vue application: https://vuejs.org/api/application.html#application-api
// These let you call and add runtime NuxtApp hooks
// https://github.com/nuxt/framework/blob/main/packages/nuxt/src/app/nuxt.ts#L18
hooks,
hook,
callHook,
// Only accessible on server-side
ssrContext: {
url,
req,
res,
runtimeConfig,
noSSR,
},
// This will be stringified and passed from server to client
payload: {
serverRendered: true,
data: {},
state: {}
}
provide: (name: string, value: any) => void
}
詳しくは、ソースコード
をご覧ください。
ランタイムコンテキストとビルドコンテキスト
Nuxt は Node.js を使ってプロジェクトをビルドとバンドルをしますが、ランタイム側のコンテキストも持っています。
どちらの領域も拡張可能ですが、そのランタイムコンテキストはビルド時とは分離されています。そのため、状態やコード、ランタイム設定以外のコンテキストを共有することは想定されていません。
nuxt.config
と Nuxt Modules
はビルドコンテキストを拡張するために使用でき、Nuxt Plugins
はランタイムを拡張するために使用できます。
本番用のアプリケーションをビルドするとき、nuxi build
は nuxt.config
や Nuxt modules
とは無関係に .output
ディレクトリにスタンドアローンなビルドを生成します。
ライフサイクルフック
Nuxt は、unjs/hookable で提供されるフックを使って、ほとんどすべての面を拡張できる強力なフッキングシステムを提供します。
Nuxt Hooks(ビルド時)
これらのフックは、Nuxt モジュール
とビルドコンテキストで利用可能です。
nuxt.config
での使い方
export default defineNuxtConfig({
hooks: {
'close': () => { }
}
})
Nuxt モジュールでの使い方
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
nuxt.hook('close', async () => { })
})
})
App Hooks (ランタイム)
アプリフックは、主に Nuxt Plugin
がレンダリングのライフサイクルにフックするために使用されますが、Vue のコンポーザブルでも使用できます。
Plugins での使い方
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
/* your code goes here */
})
})
モジュール開発者ガイド
Nuxt は、Web アプリケーションを開発するための統合とベストプラクティスのプリセットを使用して、ゼロコンフィグ体験を提供します。強力な設定とフックシステムにより、Nuxt Framework のほぼすべての側面をカスタマイズすることが可能で、カスタマイズに関しては無限に可能な統合を追加することができます。Nuxt の仕組みについては、
Nuxt internals のセクションで詳しく説明されています。
Nuxt は、Nuxt Modules と呼ばれる強力な API を公開しています。Nuxt モジュールは、nuxi dev
を使用して開発モードで nuxt を起動するとき、または nuxi build
を使用して本番用にプロジェクトを構築するときに順次実行されるシンプルな非同期関数です。Nuxt Modules を使うことで、Nuxt プロジェクト自体に不必要な定型文を追加することなく、カスタムソリューションをカプセル化し、適切にテストし、npm パッケージとして共有することができます。Nuxt モジュールは、Nuxt ビルダーのライフサイクルイベントにフックし、ランタイムアプリのテンプレートを提供し、設定を更新し、またはニーズに基づいた他のカスタムアクションを実行することができます。
クイックスタート
せっかちな人のために、module-builder
と module starter template
を使ってすぐに始めることができます:
npx nuxi init -t module my-module
スターターテンプレートとモジュールスターターは、Nuxt モジュールを作成する際の標準的なパスです。
次のステップ:
- お好みの IDE で
my-module
を開いてください (Visual Studio Code をお勧めします) - パッケージマネージャを使用して依存関係をインストールします(Yarn を推奨します)。
-
npm run dev:prepare
を使ってローカルファイルが生成されていることを確認します。 -
npm run dev
を使ってプレイグラウンドを開始します。 - Nuxtモジュールの詳細については、このドキュメントに従ってください。
モジュールの構造
Nuxt モジュールはインラインのユーザーオプションと nuxt
の引数を受け付けるシンプルな関数です。
残りのロジックをどのように処理するかはモジュール作者であるあなた次第です。
Nuxt 3 から、モジュールはすべての Nuxt Kit
ユーティリティの恩恵を受けることができるようになりました。
// modules/module.mjs
export default async (inlineOptions, nuxt) => {
// ここに好きなものは何でもできます..
console.log(inlineOptions.token) // `123`
console.log(nuxt.options.dev) // `true` or `false`
nuxt.hook('ready', async nuxt => {
console.log('Nuxt is ready')
})
}
export default defineNuxtConfig({
modules: [
// パッケージ名を使用する(推奨される使い方)
'@nuxtjs/example',
// ローカルのモジュールを読み込む
'./modules/example',
// インラインオプションを使用してモジュールを追加する
['./modules/example', { token: '123' }]
// インラインモジュールを定義する
async (inlineOptions, nuxt) => { }
]
})
Nuxt モジュールの定義
Nuxt モジュールの作成には、面倒で一般的なタスクが含まれます。Nuxt Kit
は、defineNuxtModule
を使用して Nuxt モジュールを定義するための便利で標準的な API を提供します:
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
meta: {
// 通常、モジュールの npm パッケージ名
name: '@nuxtjs/example',
// モジュールオプションを保持する `nuxt.config` のキー
configKey: 'sample',
// 互換性の制約
compatibility: {
// サポートされている nuxt バージョンの Semver バージョン
nuxt: '^3.0.0'
}
},
// モジュールのデフォルトの構成オプション
defaults: {},
hooks: {},
async setup(moduleOptions, nuxt) {
// -- ここにモジュールロジックを追加します --
}
})
defineNuxtModule
の結果は、(inlineOptions, nuxt)
サインを持つラッパー関数です。これは、デフォルトと他の必要なステップを適用し、呼ばれたときに setup
関数を呼び出します。
defineNuxtModule
の 特徴:
- ✅ モジュールオプションを自動的にマージするための
defaults
とmeta.configKey
のサポート - ✅ 型ヒントと自動型推論
- ✅ 基本的な Nuxt 2 互換性のためのシム追加
- ✅
meta.name
またはmeta.configKey
から計算されたユニークなキーを使用して、モジュールが一度だけインストールされるようにする。 - ✅ Nuxt フックの自動登録
- ✅ モジュールメタに基づく互換性問題の自動チェック
- ✅ Nuxt の内部利用のために
getOptions
とgetMeta
を公開する。 - ✅ 最新バージョンの
@nuxt/kit
からdefineNuxtModule
を使用している限り、後方および上位互換性を保証する - ✅ モジュールビルダーツールとの統合
ベストプラクティス
非同期モジュール
Nuxt モジュールは非同期処理を行うことができます。例えば、ある API を取得したり、非同期関数を呼び出す必要があるモジュールを開発したいと思うかもしれません。
公開されたインターフェースには常に接頭辞を付ける
Nuxt モジュールは、他のモジュールや内部との衝突を避けるために、公開された設定、プラグイン、API、Composable、コンポーネントに対して明示的な接頭辞を提供する必要があります。
理想的には、あなたのモジュール名をプレフィックスとして付けるべきです(あなたのモジュールが nuxt-foo
と呼ばれる場合、<Foo>
と useBar()
ではなく、<FooButton>
と useFooBar()
を公開してください)。
TypeScript フレンドリーにする
Nuxt 3 は、最高の開発体験を提供するために、ファーストクラスの TypeScript インテグレーションを備えています。
型を公開し、typescript を使用してモジュールを開発することは、typescript を直接使用しない場合でも、ユーザーに利益をもたらすことができます。
CommonJS の構文を避ける
Nuxt 3 では、ネイティブ ESM に依存しています。詳しくは、Native ES Modules
をお読みください。
モジュールのエコシステム
Nuxt は、Nuxt モジュールと統合の健全で豊かなエコシステムを持つ傾向があります。ここにいくつかのベストプラクティスを示しますので、参加してコントリビュートしてください!
モジュールの使い方を文書化する
モジュールの使い方を readme ファイルに記述することを検討してください:
- なぜこのモジュールを使うのか?
- このモジュールの使い方は?
- このモジュールは何をするのか?
インテグレーションサイトやドキュメントへのリンクは、常に良いアイデアです。
nuxt-
接頭辞を使う
npm パッケージには あなたのモジュールを発見しやすくするために、npm パッケージの名前に nuxt-
接頭辞を使ってください。これは常にアイデアを起草して試すのに最適な出発点です!
modules.nuxtjs.org にモジュールをリストアップする
作業中のモジュールがあり、modules.nuxtjs.org
に掲載されたいですか?nuxt/modules
リポジトリで issue を開いてください。Nuxt チームは、登録前にベストプラクティスを適用するための手助けをします。
特定の Nuxt バージョンで宣伝しないでください。
Nuxt 3、Nuxt Kit、その他の新しいツールは、前方および後方互換性の両方を考慮して作られています。
エコシステムの断片化を避けるために「X for Nuxt 3」ではなく「X for Nuxt」を使用し、Nuxt のバージョン制約を設定するために meta.compatibility
を使用することを推奨します。
nuxt-community に参加する
あなたのモジュールを nuxt-community に移動することで、常に誰かが助けてくれますし、この方法で一つの完璧なソリューションを作るために力を合わせることができます。
もし既に公開され動作しているモジュールがあり、それを nuxt-community
に移したい場合は、nuxt/modules
で issue
を開いてください。
例
Nuxt プラグインを提供する
一般的に、モジュールは実行時ロジックを追加するために1つ以上の実行プラグインを提供します。
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'
export default defineNuxtModule<ModuleOptions>({
setup (options, nuxt) {
// Create resolver to resolve relative paths
const { resolve } = createResolver(import.meta.url)
addPlugin(resolve('./runtime/plugin'))
}
})
CSS ライブラリを追加する
モジュールがCSSライブラリを提供する場合、重複を避けるために、ユーザーがすでにライブラリを含んでいるかどうかを確認し、モジュールにCSSライブラリを無効にするオプションを追加するようにしてください。
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
nuxt.options.css.push('font-awesome/css/font-awesome.css')
}
})
クリーンアップモジュール
もしあなたのモジュールがハンドルを開いたり、ウォッチャーを開始したら、nuxt のライフサイクルが終了したときにそれをクローズする必要があります。これには close
フックを使います:
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
nuxt.hook('close', async nuxt => {
// Your custom code here
})
}
})
Nuxt キット
Nuxt Kitは、Nuxt HooksやNuxt Builder Coreとの対話やNuxtモジュールの開発を超簡単にするための、コンポーザブルなユーティリティを提供します。
使い方
依存関係のインストール
最新の nuxt キットは、package.json
の dependencies
セクションに追加することでインストールできます。ただし、@nuxt/kit
パッケージが既に nuxt によってインストールされている場合でも、常に明示的にインストールすることを検討してください。
{
"dependencies": {
"@nuxt/kit": "npm:@nuxt/kit-edge@latest"
}
}
キットユーティリティをインポートする
import { useNuxt } from '@nuxt/kit'
Nuxt kit は esm 専用パッケージ
なので、require('@nuxt/kit')
することができません。回避策として、CommonJS のコンテキストで使用するために、動的インポートを使用することができます:
// これは動作しません!
// const kit = require('@nuxt/kit')
async function main() {
const kit = await import('@nuxt/kit')
}
main()
ES モジュール
Nuxt 3(および Bridge)はNative ES Modulesを使用しています。
このガイドでは、ES モジュールとは何か、そして Nuxt アプリ(または、アップストリームライブラリ)を ESM と互換性を持たせる方法について説明します。
背景
CommonJS モジュール
CommonJS (CJS) は Node.js によって導入されたフォーマットで、分離された JavaScript モジュール間で機能を共有することができます(詳しくは こちらをご覧ください
)。この構文については、すでにご存知の方もいらっしゃるかもしれません。
const a = require('./a')
module.exports.a = a
webpack や Rollup などのバンドラーはこの構文に対応しており、CommonJS で書かれたモジュールをブラウザで利用することができます。
ESM の構文
ほとんどの場合、ESM と CJS について話すとき、彼らは モジュール
を書くための異なる構文について話しています。
import a from './a'
export { a }
ECMAScript Modules (ESM) が標準になる前に(10 年以上かかった!)、webpack
などのツールや TypeScript などの言語でも、いわゆる ESM 構文 がサポートされ始めました。しかし、実際の仕様とはいくつかの重要な違いがあります;ここでは、役に立つ説明をしています
。
ネイティブ ESMとは?
長い間、ESM 構文を使ってアプリを書いてきたかもしれません。Nuxt 2 では、あなたが書いたすべてのコードを適切な形式(サーバは CJS、ブラウザは ESM)にコンパイルしています。
パッケージにインストールするモジュールを使用する場合、状況は少し異なりました。サンプルライブラリは、CJS と ESM の両方のバージョンを公開し、どちらを使うか選択できるようになっています:
{
"name": "sample-library",
"main": "dist/sample-library.cjs.js",
"module": "dist/sample-library.esm.js"
}
つまり、Nuxt 2 では、バンドル(webpack)はサーバビルド用に CJS ファイル ('main') を取り込み、クライアントビルド用に ESM ファイル ('module')を使用することになります。
しかし、最近の Node.js LTS リリースでは、Node.js 内で ネイティブの ESM モジュールを使用する
ことができるようになりました。つまり、デフォルトでは行いませんが、Node.js 自体が ESM 構文を使用して JavaScript を処理することができます。ESM 構文を有効にする最も一般的な方法は、次の 2 つです:
-
package.json
でtype: 'module'
を設定し、拡張子.js
を使い続ける。 -
.mjs
ファイル拡張子を使用する(推奨)。
Nuxt Nitro では、.output/server/index.mjs
というファイルを出力しています。これは、Node.js にこのファイルをネイティブ ES モジュールとして扱うように指示するものです。
Node.js の文脈で有効な import とは何ですか?
モジュールを require
するのではなく、import
すると、Node.js は異なる方法でモジュールを解決します。例えば、sample-library
を import すると、Node.js は main
ではなく、そのライブラリの package.json
にある exports
や module
のエントリーを探します。
これは const b = await import('sample-library')
のような動的なインポートにも当てはまります。
Node は以下の種類のインポートをサポートしています(ドキュメント
を参照):
-
.mjs
で終わるファイル - これらは ESM 構文を使うことが期待されます。 -
.cjs
で終わるファイル - CJS 構文を使用することが想定されています。 -
.js
で終わるファイル -package.json
にtype: 'module'
がない限り、CJS シンタックスを使用することが期待されます。
どのような問題があるのでしょうか?
長い間、モジュール作者は ESM 構文のビルドを作成してきましたが、.esm.js
や .es.js
などのコンベンションを使用しており、package.json
の module
フィールドに追加してきました。これは今まで問題にはならなかったのですが、webpack のようなファイル拡張子を特に気にしない bundler によってのみ使用されてきたからです。
しかし、Node.js の ESM コンテキストで .esm.js
ファイルを持つパッケージを import しようとすると、うまくいかず、次のようなエラーが発生します:
(node:22145) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
/path/to/index.js:1
export default {}
^^^^^^
SyntaxError: Unexpected token 'export'
at wrapSafe (internal/modules/cjs/loader.js:1001:16)
at Module._compile (internal/modules/cjs/loader.js:1049:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
....
at async Object.loadESM (internal/process/esm_loader.js:68:5)
また、Node.js が CJS とみなしている ESM-syntax ビルドからの名前付きインポートがある場合にも、このエラーが発生する可能性があります:
file:///path/to/index.mjs:5
import { named } from 'sample-library'
^^^^^
SyntaxError: Named export 'named' not found. The requested module 'sample-library' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'sample-library';
const { named } = pkg;
at ModuleJob._instantiate (internal/modules/esm/module_job.js:120:21)
at async ModuleJob.run (internal/modules/esm/module_job.js:165:5)
at async Loader.import (internal/modules/esm/loader.js:177:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
ESM の問題のトラブルシューティング
これらのエラーが発生した場合、ほぼ間違いなく上流のライブラリに問題があります。Node からのインポートをサポートするために ライブラリを修正する
必要があります。
ライブラリのトランスパイル
とりあえず、これらのライブラリを build.transpile
に追加することで、Nuxt がこれらのライブラリをインポートしないように指示することができます:
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
build: {
transpile: ['sample-library']
}
})
また、これらのライブラリによってインポートされる他のパッケージを追加する必要があることが分かるかもしれません。
ライブラリのエイリアス
場合によっては、ライブラリのエイリアスを CJS バージョンに手動で変更する必要があることもあります:
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
alias: {
'sample-library': 'sample-library/dist/sample-library.cjs.js'
}
})
NuxtApp
テスト
エッジチャンネル
コミュニティ
助けを得る
あるとき、あなたは助けを必要とする問題があることに気づくかもしれません。でも、心配しないでください。私たちは開発者の友好的なコミュニティであり、手助けするのが大好きです。
"どのように〇〇したらいいかわからない。"
このドキュメントを一通り読んで、できるはずだと思ったのに、方法がはっきりしない。一番良いのは、GitHub Discussion を開設する
ことです。
簡単だと思う質問でも、恥ずかしがらずに聞いてみてください - 私たちはみんなそこにいたのです! ❤️
あなたが出会うすべての人は、お金をもらっているからではなく、気にかけているからこそ助けてくれているのです。最も親切なことは、彼らがあなたを助けやすいようにすることです。以下にいくつかのアイデアを紹介します。
- 直面している問題だけでなく、あなたの目的が何であるかを説明する。"フォームの入力にアクセスできるようにする必要があるので、サーバーとクライアントの間で id を一致させようとしています"。
- まずドキュメントを読み、お気に入りの検索エンジンを使ったことを確認してください。"私は、'nuxt script setup' とググりましたが、コードサンプルをどこにも見つけられませんでした。" のような言い方で人々に知らせましょう。
- あなたが試したことを説明してください。あなたがどのような解決策を試したのか、そしてその理由を伝えましょう。そうすることで、人々のアドバイスがよりあなたの状況に適したものになることがよくあります。
- コードを共有する。エラーメッセージやスクリーンショットを見ただけでは、おそらくほかの人はあなたを助けられないでしょう。しかし、あなたのコードをコピー & ペーストできる形式で、できれば CodeSandbox のような最小限の複製という形で共有すれば、すべてが変わります。
そして最後に、ただ質問をすることです。質問をするために許可を得る必要はありません
し、誰かがあなたの「こんにちは」に返信するのを待つ
必要もありません。なぜなら、ほかの人は手助けをする前に質問全体が来るのを待っているので、反応が得られないかも知れません。
"バグがあるのでは?"
何かがドキュメントに書いてある通りに動いていない。それがバグかどうかもわからない。公開されている issues
や discussions
に目を通したが、何も見つからない。(クローズされた issue がある場合は、新しい issue を作成してください)
バグを報告する方法
をご覧になることをお勧めします。Nuxt 3 はまだ活発に開発されており、すべての問題はより良いものにするために役立ちます。
バグの報告
バグを完全になくすことはできません。オープンソースにおける最も価値ある役割の 1 つは、時間をかけてバグを報告することです。たとえあなたが根本的なコードを修正できなくても、バグをうまく報告することで、コードベースにもう少し精通したほかのだれかがパターンを発見したり、迅速な修正を行ったりすることができます。
ここでは、いくつかの重要なステップを紹介します。
それは本当にバグですか?
何か助けを求めているのか、それとも Nuxt 自体にバグがあると思うのか、よく考えてみてください。もし前者であれば、私たちはあなたを助けたいと思いますが、バグを報告するのではなく、助けを求めること
が最善の方法です。
issues を検索する
まず、公開されている issues
や discussions
を検索してください。同じようなバグを見つけたら、重複して作成するよりも、既存のスレッドにコメントする方がずっとよいでしょう。
最小限の再現を作成する
プロジェクトの他の部分とは別に、最小限の方法で、バグを確実に再現できることが重要です。そうすることで、問題の原因を絞り込むことができ、原因を突き止めるだけでなく、解決策を試すことも可能になります。
Nuxt 3 または Nuxt Bridge の sandbox で開始し、発生しているバグを再現するために必要な最小限のコードを追加します。
Nuxt 3:
Nuxt Bridge:
Vue 3:
問題を再現したら、(バグを再現しつつ)再現したコードからできる限り削除してください。再現をできるだけ少なくするために費やした時間は、その問題を解決しようとする人にとって大きな違いとなります。
原因の解明
Nuxt プロジェクトでは、nuxt モジュール
から他の JavaScript ライブラリ
まで、多くの可動部品があります。最も関連性が高く、具体的な場所でバグを報告するようにしてください。それはおそらく、問題を引き起こしている Nuxt モジュールか、Nuxt が依存している上流のライブラリでしょう。
貢献する
Nuxt はコミュニティプロジェクトで、あらゆる貢献を待ち望んでいます!❤️
Nuxt のエコシステムに貢献できる方法はさまざまです。
ドキュメントの改善
ドキュメントは、Nuxt の最も重要な部分の 1 つです。私たちは直感的なフレームワークを目指しており、そのためには開発者体験とドキュメントの両方を完璧なものにすることが重要なポイントになります。👌
もし、ドキュメントやエラーメッセージを改善できる箇所を見つけたら、ぜひ PR をお願いします!
issues を分類し、discussions に参加する
私たちの issues ボード
と discussions
をご覧ください。他のユーザーを助けたり、回避策を共有したり、複製を作成したり、あるいはバグを少し調べて発見を共有したりと - これらはすべて、大きな違いを生み出します。
バグを修正する、機能を追加する
バグを修正したり機能を追加したりする前に、そのバグについて記述した issues があることを確認します。特に新機能の場合は、作業を始める前にプロジェクトリーダーからフィードバックをもらう絶好の機会です。
開発環境のセットアップ
-
Nuxt 3 のリポジトリ
を自分の GitHub アカウントにフォーク
し、それをローカルのデバイスにクローン
します。 -
yarn
を実行して依存関係をインストールします。
依存関係を追加する場合は、
yarn add
を使用してください。yarn.lock
ファイルは、すべての Nuxt 依存性のための真実の源です。
- パッシブ開発の活性化のために
yarn stub
を実行します。 - 作業可能なブランチをチェックアウトし、変更をコミットします。
git checkout -b my-new-branch
例
PR の作業中、変更した内容が正しく動作しているかどうかを確認したくなることがあると思います。
そのためには、playground/
にあるサンプルアプリを修正し、yarn play
で実行することができます。ブランチにコミットしないように注意しましょう。しかし、PR の説明文にサンプルコードを追加しておくと便利です。これはレビュアーや他の Nuxt ユーザーがあなたが作った機能を深く理解するのに役立ちます。
テスト
すべての新しい機能には、(可能であれば)対応するユニットテストが必要です。このリポジトリにある test
フォルダは現在進行中ですが、すでにあるものの例に従って新しいテストを作成するよう、最善を尽くしてください。
PR を作成したり、ready-to-review としてマークする前に、ローカルで yarn test
を実行して、すべてのテストがパスしていることを確認してください。
コード整形
すでにお気づきかもしれませんが、私たちは ESLint を使用してコーディング規約を強制しています。変更をコミットする前に yarn lint
を実行して、コードスタイルが正しいかどうか確認してください。もしそうでなければ、yarn lint --fix
を使ってスタイルの変更のほとんどを修正することができます。もしエラーが残っている場合は、手動で修正する必要があります。
ドキュメント
新しい機能を追加したり、リファクタリングやその他の方法で Nuxt の動作を変更する場合、おそらくその変更を文書化したいと思うことでしょう。ドキュメントへの変更はすべて同じ PR に含めてください。最初のコミットでドキュメントを書き上げる必要はありません(ただし、プルリクエストが十分に成熟したらすぐに書き上げるようにしてください)。
最後のチェックリスト
PR を提出する際には、簡単なテンプレートがありますので、そちらに記入してください。チェックリストの該当する「答え」に全てチェックを入れてください。
特に、PR のタイトルが Conventional Commits のガイドライン
に従っていることを確認し、課題の説明で関連する課題(機能リクエストまたはバグレポート)にリンクしてください。
ロードマップ
Nuxt は常に進化しており、新しい機能やモジュールが随時追加されています。このページでは、現在予定されているリリースの状況やスケジュールを掲載しています。
📢 アナウンス
Nuxt 3 ベータ
Nuxt 3 リリース候補
📅 リリーススケジュール
リリース | 予定日 | 説明 |
---|---|---|
nuxt@3.0.0-rc.1 |
2022 年 3 月 | Nuxt v3 初リリース候補 |
nuxt@2.16 |
2022 年 3 月 | Nuxt v2 将来の互換性のための累積的な更新 |
nuxt@3.0.0 |
2022 年 6 月 | Nuxt v3 安定リリース |
最近のリリース
現在、Nuxt 2
が安定版とされています。
Nuxt 2.x の最新アップデートは nuxt-edge
npm パッケージ経由で入手でき、将来の互換性のために Nuxt 3 の最初のリリース候補と一緒にリリースされる予定です。Nuxt 3
は現在開発中で、nuxt3
npm パッケージとして利用でき、ベータテスト用に自動リリースされています。
リリース | npm | ステータス | 最終リリース | ドキュメント | リポジトリ |
---|---|---|---|---|---|
Nuxt 2.x | nuxt |
メンテナンス | npm v2.15.8 |
2.x ドキュメント |
nuxt/nuxt.js#2.x |
Nuxt 2.x(edge*) | nuxt-edge |
メンテナンス | npm v2.16.0-27358576.777a4b7f |
2.x ドキュメント |
nuxt/nuxt.js |
Nuxt 3.x(edge*) | nuxt 3 |
開発中 | npm v3.0.0-27470397.9ebea90 |
3.x ドキュメント |
nuxt/framework |
(*)Edge のリリースは、各コミットがテストに合格した後に自動化されます
🛣️ ロードマップ
ロードマップでは、Nuxt 3 に搭載される主な機能を紹介しています。
💡 今後の機能やアイデアについては、Discussions
や RFC
をチェックしてください。
マイルストーン | 予定日 | ノート | 説明 |
---|---|---|---|
Nitro パック | Q1 2022 | nuxt/framework#3161 |
Nuxt 3 用の安定したフル機能のサーバーエンジン |
生成とキャッシュ | Q1 2022 | nuxt/framework#3161 |
ルートキャッシングルール、フルスタティック生成 |
テスト Utils | Q1 2022 | nuxt/framework#3198 |
Nuxt 3 と新しいモジュールをテストするために nuxt/test-utils を書き直しました |
Nuxt 3 RC | Q1 2022 | nuxt/framework#3447 |
Nuxt 3 初リリース候補 |
Extending | Q1 2022 | nuxt/framework#3222 |
ネイティブエクステンションとテーマのサポート |
Content v2 | Q1 2022 | - | Nuxt 3 をサポートするために nuxt/content を書き直しました |
Devtools | Q2 2022 | - | Nuxt のための統合された devtools エクスペリエンス |
Auth | Q2 2022 | - |
nuxt-community/auth-module を Nuxt 3 対応に書き直したものです |
SEO & PWA | Q2 2022 | nuxt/framework#1823 |
nuxt-community/pwa-module からの移行は、組み込みの SEO ユーティリティとサービスワーカーをサポートするためです。 |
翻訳 | Q2/Q3 2022 |
nuxt/framework#4 (private) |
Nuxt 3 ドキュメントの安定した翻訳プロセスのための共同プロジェクトです。現在、アイデアとドキュメントツールのサポート(リモートソースによるコンテンツ v2)を申請中です。 |
コアモジュール
Nuxt フレームワークの他に、エコシステムに欠かせないモジュールがあります。それらのステータスは以下に更新されます。
モジュール | ステータス | Nuxt サポート | リポジトリ | 説明 |
---|---|---|---|---|
Content 1.x | メンテナンス | 2.x | nuxt/content |
メンテナンスのみ |
Content 2.x | 進行中の作業 | 3.x | nuxt/content-next |
プライベートベータ |
Auth | 進行中の作業 | 3.x | nuxt/auth |
プライベートベータ |
Image | アクティブ | 2.x | nuxt/image |
Nuxt 3 サポート予定 |
Telementry | アクティブ | 2.x | nuxt/telementry |
Nuxt 3 サポート予定 |
API
useAsyncData
pages、コンポーネント、およびプラグイン内で、useAsyncData を使用して、非同期で解決されるデータにアクセスできます。
型
function useAsyncData(
key: string,
handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
options?: AsyncDataOptions
): Promise<DataT>
type AsyncDataOptions = {
server?: boolean
lazy?: boolean
default?: () => DataT | Ref<DataT>
transform?: (input: DataT) => DataT
pick?: string[]
watch?: WatchSource[]
initialCache?: boolean
}
type DataT = {
data: Ref<DataT>
pending: Ref<boolean>
refresh: () => Promise<void>
error: Ref<any>
}
Params
- key: データフェッチをリクエスト間で適切に重複排除できるようにするための一意のキー
- handler: 値を返す非同期関数
-
オプション:
-
lazy: ナビゲーションをブロックする代わりに、ルートのロード後に非同期関数を解決するかどうか(デフォルトは
false
) -
default: 非同期関数が解決する前にデータのデフォルト値を設定するファクトリ関数 - 特に
lazy: true
オプションで役立ちます -
server: サーバー上のデータをフェッチするかどうか(デフォルトは
true
) -
transform: 解決後に
handler
関数の結果を変更するために使用できる関数 -
pick:
handler
関数の結果からこの配列内の指定されたキーのみを選択します - watch: リアクティブソースを監視して自動更新します
-
initialCache:
false
に設定すると、初期フェッチのペイロードキャッシュをスキップします(デフォルトはtrue
)
-
lazy: ナビゲーションをブロックする代わりに、ルートのロード後に非同期関数を解決するかどうか(デフォルトは
内部的には、lazy: false
は <Suspense>
を使用して、データがフェッチされる前にルートのロードをブロックします。より迅速なユーザーエクスペリエンスのために、lazy: true
を使用し、代わりに読み込み状態を実装することを検討してください。
戻り値
- data: 渡された非同期関数の結果
- pending: データがまだフェッチされているかどうかを示すブール値
- refresh: ハンドラー関数によって返されたデータを更新するために使用できる関数
- error: データのフェッチに失敗した場合のエラーオブジェクト
デフォルトでは、Nuxt は 更新
が終了するまで待機してから再度実行できます。パラメータとして true
を渡すと、その待機はスキップされます。
例
const { data, pending, error, refresh } = await useAsyncData(
'mountains',
() => $fetch('https://api.nuxtjs.dev/mountains')
)
useCookie
useError
useFetch
useHead
useHydration
useLazyAsyncData
useLazyFetch
useNuxtApp
useRequestHeaders
useRoute
useRouter
useState
<NuxtPage>
<NuxtLayout>
<NuxtLink>
<NuxtErrorBoundary>
$fetch
abortNavigation
addRouteMiddleware
clearError
defineNuxtComponent
defineNuxtRouteMiddleware
definePageMeta
navigateTo
refreshNuxtData
throwError
ライフサイクルフック
アプリフック(ランタイム)
利用可能なすべてのフックについては、アプリのソースコード
を確認してください。
フック | 引数 | 説明 |
---|---|---|
app:created |
vueApp |
vueApp の初期インスタンス作成時 |
app:beforeMount |
vueApp |
app:created と同じ |
app:mounted |
vueApp |
Vue アプリが初期化され、ブラウザにマウントされた時 |
app:rendered |
- | SSR レンダリングが行われた時 |
app:suspense:resolve |
appComponent |
Suspense 解決イベント |
page:start |
pageComponent |
Suspense 解決イベント |
page:finish |
pageComponent |
Suspense 解決イベント |
meta:register |
metaRenderers |
(内部) |
vue:setup |
- | (内部) |
Nuxt フック(ビルドタイム)
利用可能なすべてのフックについては、スキーマのソースコード
を確認してください。
Kit ユーティリティ
ユーティリティ
モジュール
import { parse, relative } from 'pathe'
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate, ModuleContainer } from '@nuxt/schema'
import { logger } from '../logger'
import { chainFn } from '../internal/task'
import { addTemplate } from '../template'
import { addServerMiddleware } from '../server'
import { isNuxt2 } from '../compatibility'
import { addPluginTemplate } from '../plugin'
import { useNuxt } from '../context'
import { installModule } from './install'
const MODULE_CONTAINER_KEY = '__module_container__'
export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
if (nuxt[MODULE_CONTAINER_KEY]) {
return nuxt[MODULE_CONTAINER_KEY]
}
async function requireModule (moduleOpts) {
let src, inlineOptions
if (typeof moduleOpts === 'string') {
src = moduleOpts
} else if (Array.isArray(moduleOpts)) {
[src, inlineOptions] = moduleOpts
} else if (typeof moduleOpts === 'object') {
if (moduleOpts.src || moduleOpts.handler) {
src = moduleOpts.src || moduleOpts.handler
inlineOptions = moduleOpts.options
} else {
src = moduleOpts
}
} else {
src = moduleOpts
}
await installModule(src, inlineOptions)
}
nuxt[MODULE_CONTAINER_KEY] = <ModuleContainer>{
nuxt,
options: nuxt.options,
ready () { return Promise.resolve() },
addVendor () {},
requireModule,
addModule: requireModule,
addServerMiddleware,
addTemplate (template: string | NuxtTemplate) {
if (typeof template === 'string') {
template = { src: template }
}
if (template.write === undefined) {
template.write = true
}
return addTemplate(template)
},
addPlugin (pluginTemplate: NuxtPluginTemplate): NuxtPluginTemplate {
return addPluginTemplate(pluginTemplate)
},
addLayout (tmpl: NuxtTemplate, name: string) {
const { filename, src } = addTemplate(tmpl)
const layoutName = name || parse(src).name
const layout = nuxt.options.layouts[layoutName]
if (layout) {
logger.warn(`Duplicate layout registration, "${layoutName}" has been registered as "${layout}"`)
}
nuxt.options.layouts[layoutName] = `./${filename}`
if (name === 'error') {
this.addErrorLayout(filename)
}
},
addErrorLayout (dst: string) {
const relativeBuildDir = relative(nuxt.options.rootDir, nuxt.options.buildDir)
nuxt.options.ErrorPage = `~/${relativeBuildDir}/${dst}`
},
extendBuild (fn) {
// @ts-ignore
nuxt.options.build.extend = chainFn(nuxt.options.build.extend, fn)
if (!isNuxt2(nuxt)) {
console.warn('[kit] [compat] Using `extendBuild` in Nuxt 3 has no effect. Instead call extendWebpackConfig and extendViteConfig.')
}
},
extendRoutes (fn) {
if (isNuxt2(nuxt)) {
nuxt.options.router.extendRoutes = chainFn(nuxt.options.router.extendRoutes, fn)
} else {
nuxt.hook('pages:extend', async (pages, ...args) => {
const maybeRoutes = await fn(pages, ...args)
if (maybeRoutes) {
console.warn('[kit] [compat] Using `extendRoutes` in Nuxt 3 needs to directly modify first argument instead of returning updated routes. Skipping extended routes.')
}
})
}
}
}
return nuxt[MODULE_CONTAINER_KEY]
}
import { promises as fsp } from 'node:fs'
import defu from 'defu'
import { applyDefaults } from 'untyped'
import { dirname } from 'pathe'
import type { Nuxt, NuxtTemplate, NuxtModule, ModuleOptions, ModuleDefinition } from '@nuxt/schema'
import { logger } from '../logger'
import { useNuxt, nuxtCtx, tryUseNuxt } from '../context'
import { isNuxt2, checkNuxtCompatibility } from '../compatibility'
import { templateUtils, compileTemplate } from '../internal/template'
/**
* Define a Nuxt module, automatically merging defaults with user provided options, installing
* any hooks that are provided, and calling an optional setup function for full control.
*/
export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: ModuleDefinition<OptionsT>): NuxtModule<OptionsT> {
// Legacy format. TODO: Remove in RC
if (typeof definition === 'function') {
// @ts-ignore
definition = definition(useNuxt())
logger.warn('Module definition as function is deprecated and will be removed in the future versions', definition)
}
// Normalize definition and meta
if (!definition.meta) { definition.meta = {} }
if (!definition.meta.configKey) {
// @ts-ignore TODO: Remove non-meta fallbacks in RC
definition.meta.name = definition.meta.name || definition.name
// @ts-ignore
definition.meta.configKey = definition.meta.configKey || definition.configKey || definition.meta.name
}
// Resolves module options from inline options, [configKey] in nuxt.config, defaults and schema
function getOptions (inlineOptions?: OptionsT, nuxt: Nuxt = useNuxt()) {
const configKey = definition.meta.configKey || definition.meta.name
const _defaults = definition.defaults instanceof Function ? definition.defaults(nuxt) : definition.defaults
let _options = defu(inlineOptions, nuxt.options[configKey], _defaults) as OptionsT
if (definition.schema) {
_options = applyDefaults(definition.schema, _options) as OptionsT
}
return Promise.resolve(_options)
}
// Module format is always a simple function
async function normalizedModule (inlineOptions: OptionsT, nuxt: Nuxt) {
if (!nuxt) {
nuxt = tryUseNuxt() || this.nuxt /* invoked by nuxt 2 */
}
// Avoid duplicate installs
const uniqueKey = definition.meta.name || definition.meta.configKey
if (uniqueKey) {
nuxt.options._requiredModules = nuxt.options._requiredModules || {}
if (nuxt.options._requiredModules[uniqueKey]) {
// TODO: Notify user if inline options is provided since will be ignored!
return
}
nuxt.options._requiredModules[uniqueKey] = true
}
// Check compatibility constraints
if (definition.meta.compatibility) {
const issues = await checkNuxtCompatibility(definition.meta.compatibility, nuxt)
if (issues.length) {
logger.warn(`Module \`${definition.meta.name}\` is disabled due to incompatibility issues:\n${issues.toString()}`)
return
}
}
// Prepare
nuxt2Shims(nuxt)
// Resolve module and options
const _options = await getOptions(inlineOptions, nuxt)
// Register hooks
if (definition.hooks) {
nuxt.hooks.addHooks(definition.hooks)
}
// Call setup
await definition.setup?.call(null, _options, nuxt)
}
// Define getters for options and meta
normalizedModule.getMeta = () => Promise.resolve(definition.meta)
normalizedModule.getOptions = getOptions
return normalizedModule as NuxtModule<OptionsT>
}
// -- Nuxt 2 compatibility shims --
const NUXT2_SHIMS_KEY = '__nuxt2_shims_key__'
function nuxt2Shims (nuxt: Nuxt) {
// Avoid duplicate install and only apply to Nuxt2
if (!isNuxt2(nuxt) || nuxt[NUXT2_SHIMS_KEY]) { return }
nuxt[NUXT2_SHIMS_KEY] = true
// Allow using nuxt.hooks
// @ts-ignore Nuxt 2 extends hookable
nuxt.hooks = nuxt
// Allow using useNuxt()
if (!nuxtCtx.use()) {
nuxtCtx.set(nuxt)
nuxt.hook('close', () => nuxtCtx.unset())
}
// Support virtual templates with getContents() by writing them to .nuxt directory
let virtualTemplates: NuxtTemplate[]
nuxt.hook('builder:prepared', (_builder, buildOptions) => {
virtualTemplates = buildOptions.templates.filter(t => t.getContents)
for (const template of virtualTemplates) {
buildOptions.templates.splice(buildOptions.templates.indexOf(template), 1)
}
})
nuxt.hook('build:templates', async (templates) => {
const context = {
nuxt,
utils: templateUtils,
app: {
dir: nuxt.options.srcDir,
extensions: nuxt.options.extensions,
plugins: nuxt.options.plugins,
templates: [
...templates.templatesFiles,
...virtualTemplates
],
templateVars: templates.templateVars
}
}
for await (const template of virtualTemplates) {
const contents = await compileTemplate({ ...template, src: '' }, context)
await fsp.mkdir(dirname(template.dst), { recursive: true })
await fsp.writeFile(template.dst, contents)
}
})
}
import type { Nuxt, NuxtModule } from '@nuxt/schema'
import { useNuxt } from '../context'
import { resolveModule, requireModule, importModule } from '../internal/cjs'
import { resolveAlias } from '../resolve'
import { useModuleContainer } from './container'
/** Installs a module on a Nuxt instance. */
export async function installModule (moduleToInstall: string | NuxtModule, _inlineOptions?: any, _nuxt?: Nuxt) {
const nuxt = useNuxt()
const { nuxtModule, inlineOptions } = await normalizeModule(moduleToInstall, _inlineOptions)
// Call module
await nuxtModule.call(useModuleContainer(), inlineOptions, nuxt)
nuxt.options._installedModules = nuxt.options._installedModules || []
nuxt.options._installedModules.push({
meta: await nuxtModule.getMeta?.(),
entryPath: typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined
})
}
// --- Internal ---
async function normalizeModule (nuxtModule: string | NuxtModule, inlineOptions?: any) {
const nuxt = useNuxt()
// Detect if `installModule` used with older signuture (nuxt, nuxtModule)
// TODO: Remove in RC
// @ts-ignore
if (nuxtModule?._version || nuxtModule?.version || nuxtModule?.constructor?.version || '') {
[nuxtModule, inlineOptions] = [inlineOptions, {}]
console.warn(new Error('`installModule` is being called with old signature!'))
}
// Import if input is string
if (typeof nuxtModule === 'string') {
const _src = resolveModule(resolveAlias(nuxtModule), { paths: nuxt.options.modulesDir })
// TODO: also check with type: 'module' in closest `package.json`
const isESM = _src.endsWith('.mjs')
nuxtModule = isESM ? await importModule(_src) : requireModule(_src)
}
// Throw error if input is not a function
if (typeof nuxtModule !== 'function') {
throw new TypeError('Nuxt module should be a function: ' + nuxtModule)
}
return { nuxtModule, inlineOptions } as { nuxtModule: NuxtModule<any>, inlineOptions: undefined | Record<string, any> }
}
installModule(module, inlineOptions)
プログラムの使用法
import { resolve } from 'pathe'
import { applyDefaults } from 'untyped'
import { loadConfig, LoadConfigOptions } from 'c12'
import type { NuxtOptions, NuxtConfig } from '@nuxt/schema'
import { NuxtConfigSchema } from '@nuxt/schema'
export interface LoadNuxtConfigOptions extends LoadConfigOptions<NuxtConfig> {}
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
const { config: nuxtConfig, configFile, layers, cwd } = await loadConfig({
name: 'nuxt',
configFile: 'nuxt.config',
rcFile: '.nuxtrc',
dotenv: true,
globalRc: true,
...opts
})
// Fill config
nuxtConfig.rootDir = nuxtConfig.rootDir || cwd
nuxtConfig._nuxtConfigFile = configFile
nuxtConfig._nuxtConfigFiles = [configFile]
// Resolve `rootDir` & `srcDir` of layers
for (const layer of layers) {
layer.config.rootDir = layer.config.rootDir ?? layer.cwd
layer.config.srcDir = resolve(layer.config.rootDir, layer.config.srcDir)
}
// Filter layers
nuxtConfig._layers = layers.filter(layer => layer.configFile && !layer.configFile.endsWith('.nuxtrc'))
// Resolve and apply defaults
return applyDefaults(NuxtConfigSchema, nuxtConfig) as NuxtOptions
}
import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
import type { Nuxt } from '@nuxt/schema'
import { importModule, tryImportModule, RequireModuleOptions } from '../internal/cjs'
import type { LoadNuxtConfigOptions } from './config'
export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
/** Load nuxt with development mode */
dev?: boolean
/** Use lazy initialization of nuxt if set to false */
ready?: boolean
/** @deprecated Use cwd option */
rootDir?: LoadNuxtConfigOptions['cwd']
/** @deprecated use overrides option */
config?: LoadNuxtConfigOptions['overrides']
}
export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
// Backward compatibility
opts.cwd = opts.cwd || opts.rootDir
opts.overrides = opts.overrides || opts.config || {}
const resolveOpts: RequireModuleOptions = { paths: opts.cwd }
// Apply dev as config override
opts.overrides.dev = !!opts.dev
const nearestNuxtPkg = await Promise.all(['nuxt3', 'nuxt', 'nuxt-edge']
.map(pkg => resolvePackageJSON(pkg, { url: opts.cwd }).catch(() => null)))
.then(r => r.filter(Boolean).sort((a, b) => b.length - a.length)[0])
if (!nearestNuxtPkg) {
throw new Error(`Cannot find any nuxt version from ${opts.cwd}`)
}
const pkg = await readPackageJSON(nearestNuxtPkg)
const majorVersion = parseInt((pkg.version || '').split('.')[0])
// Nuxt 3
if (majorVersion === 3) {
const { loadNuxt } = await importModule((pkg as any)._name || pkg.name, resolveOpts)
const nuxt = await loadNuxt(opts)
return nuxt
}
// Nuxt 2
const { loadNuxt } = await tryImportModule('nuxt-edge', resolveOpts) || await importModule('nuxt', resolveOpts)
const nuxt = await loadNuxt({
rootDir: opts.cwd,
for: opts.dev ? 'dev' : 'build',
configOverrides: opts.overrides,
ready: opts.ready,
envConfig: opts.dotenv // TODO: Backward format convertion
})
return nuxt as Nuxt
}
export async function buildNuxt (nuxt: Nuxt): Promise<any> {
const resolveOpts: RequireModuleOptions = { paths: nuxt.options.rootDir }
// Nuxt 3
if (nuxt.options._majorVersion === 3) {
const { build } = await tryImportModule('nuxt3', resolveOpts) || await importModule('nuxt', resolveOpts)
return build(nuxt)
}
// Nuxt 2
const { build } = await tryImportModule('nuxt-edge', resolveOpts) || await importModule('nuxt', resolveOpts)
return build(nuxt)
}
loadNuxt(loadOptions)
buildNuxt(nuxt)
loadNuxtConfig(loadOptions)
互換性
import satisfies from 'semver/functions/satisfies.js' // npm/node-semver#381
import type { Nuxt, NuxtCompatibility, NuxtCompatibilityIssues } from '@nuxt/schema'
import { useNuxt } from './context'
/**
* Check version constraints and return incompatibility issues as an array
*/
export async function checkNuxtCompatibility (constraints: NuxtCompatibility, nuxt: Nuxt = useNuxt()): Promise<NuxtCompatibilityIssues> {
const issues: NuxtCompatibilityIssues = []
// Nuxt version check
if (constraints.nuxt) {
const nuxtVersion = getNuxtVersion(nuxt)
const nuxtSemanticVersion = nuxtVersion.split('-').shift()
if (!satisfies(nuxtSemanticVersion, constraints.nuxt)) {
issues.push({
name: 'nuxt',
message: `Nuxt version \`${constraints.nuxt}\` is required but currently using \`${nuxtVersion}\``
})
}
}
// Bridge compatibility check
if (isNuxt2(nuxt)) {
const bridgeRequirement = constraints?.bridge
const hasBridge = !!(nuxt.options as any).bridge
if (bridgeRequirement === true && !hasBridge) {
issues.push({
name: 'bridge',
message: 'Nuxt bridge is required'
})
} else if (bridgeRequirement === false && hasBridge) {
issues.push({
name: 'bridge',
message: 'Nuxt bridge is not supported'
})
}
}
// Allow extending compatibility checks
await nuxt.callHook('kit:compatibility', constraints, issues)
// Issues formatter
issues.toString = () =>
issues.map(issue => ` - [${issue.name}] ${issue.message}`).join('\n')
return issues
}
/**
* Check version constraints and throw a detailed error if has any, otherwise returns true
*/
export async function assertNuxtCompatibility (constraints: NuxtCompatibility, nuxt: Nuxt = useNuxt()): Promise<true> {
const issues = await checkNuxtCompatibility(constraints, nuxt)
if (issues.length) {
throw new Error('Nuxt compatibility issues found:\n' + issues.toString())
}
return true
}
/**
* Check version constraints and return true if passed, otherwise returns false
*/
export async function hasNuxtCompatibility (constraints: NuxtCompatibility, nuxt: Nuxt = useNuxt()): Promise<boolean> {
const issues = await checkNuxtCompatibility(constraints, nuxt)
return !issues.length
}
/**
* Check if current nuxt instance is version 2 legacy
*/
export function isNuxt2 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('2.')
}
/**
* Check if current nuxt instance is version 3
*/
export function isNuxt3 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('3.')
}
/**
* Get nuxt version
*/
export function getNuxtVersion (nuxt: Nuxt | any = useNuxt() /* TODO: LegacyNuxt */) {
const version = (nuxt?._version || nuxt?.version || nuxt?.constructor?.version || '').replace(/^v/g, '')
if (!version) {
throw new Error('Cannot determine nuxt version! Is currect instance passed?')
}
return version
}
checkNuxtCompatibility(constraints)
assertNuxtCompatibility(constraints)
hasNuxtCompatibility(constraints)
isNuxt2()
isNuxt3()
getNuxtVersion()
コンポーネント
import { pascalCase, kebabCase } from 'scule'
import type { ComponentsDir, Component } from '@nuxt/schema'
import { genDynamicImport } from 'knitwork'
import { useNuxt } from './context'
import { assertNuxtCompatibility } from './compatibility'
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export async function addComponentsDir (dir: ComponentsDir) {
const nuxt = useNuxt()
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
nuxt.hook('components:dirs', (dirs) => { dirs.push(dir) })
}
export type AddComponentOptions = { name: string, filePath: string } & Partial<Exclude<Component,
'shortPath' | 'async' | 'level' | 'import' | 'asyncImport'
>>
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export async function addComponent (opts: AddComponentOptions) {
const nuxt = useNuxt()
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
// Apply defaults
const component: Component = {
export: opts.export || 'default',
chunkName: 'components/' + kebabCase(opts.name),
global: opts.global ?? false,
kebabName: kebabCase(opts.name || ''),
pascalName: pascalCase(opts.name || ''),
prefetch: false,
preload: false,
mode: 'all',
// Nuxt 2 support
shortPath: opts.filePath,
async: false,
level: 0,
asyncImport: `${genDynamicImport(opts.filePath)}.then(r => r['${opts.export || 'default'}'])`,
import: `require(${JSON.stringify(opts.filePath)})['${opts.export || 'default'}']`,
...opts
}
nuxt.hook('components:extend', (components: Component[]) => {
const existingComponent = components.find(c => c.pascalName === component.pascalName || c.kebabName === component.kebabName)
if (existingComponent) {
const name = existingComponent.pascalName || existingComponent.kebabName
console.warn(`Overriding ${name} component.`)
Object.assign(existingComponent, component)
} else {
components.push(component)
}
})
}
addComponentsDir(dir)
addComponent(componentObject)
コンテキスト
import { getContext } from 'unctx'
import type { Nuxt } from '@nuxt/schema'
/** Direct access to the Nuxt context - see https://github.com/unjs/unctx. */
export const nuxtCtx = getContext<Nuxt>('nuxt')
// TODO: Use use/tryUse from unctx. https://github.com/unjs/unctx/issues/6
/**
* Get access to Nuxt instance.
*
* Throws an error if Nuxt instance is unavailable.
*
* @example
* ```js
* const nuxt = useNuxt()
* ```
*/
export function useNuxt (): Nuxt {
const instance = nuxtCtx.use()
if (!instance) {
throw new Error('Nuxt instance is unavailable!')
}
return instance
}
/**
* Get access to Nuxt instance.
*
* Returns null if Nuxt instance is unavailable.
*
* @example
* ```js
* const nuxt = tryUseNuxt()
* if (nuxt) {
* // Do something
* }
* ```
*/
export function tryUseNuxt (): Nuxt | null {
return nuxtCtx.use()
}
useNuxt()
プラグイン
import { normalize } from 'pathe'
import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema'
import { useNuxt } from './context'
import { addTemplate } from './template'
/**
* Normalize a nuxt plugin object
*/
export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
// Normalize src
if (typeof plugin === 'string') {
plugin = { src: plugin }
} else {
plugin = { ...plugin }
}
if (!plugin.src) {
throw new Error('Invalid plugin. src option is required: ' + JSON.stringify(plugin))
}
// Normalize full path to plugin
plugin.src = normalize(plugin.src)
// Normalize mode
if (plugin.ssr) {
plugin.mode = 'server'
}
if (!plugin.mode) {
const [, mode = 'all'] = plugin.src.match(/\.(server|client)(\.\w+)*$/) || []
plugin.mode = mode as 'all' | 'client' | 'server'
}
return plugin
}
/**
* Registers a nuxt plugin and to the plugins array.
*
* Note: You can use mode or .client and .server modifiers with fileName option
* to use plugin only in client or server side.
*
* Note: By default plugin is prepended to the plugins array. You can use second argument to append (push) instead.
*
* @example
* ```js
* addPlugin({
* src: path.resolve(__dirname, 'templates/foo.js'),
* filename: 'foo.server.js' // [optional] only include in server bundle
* })
* ```
*/
export interface AddPluginOptions { append?: boolean }
export function addPlugin (_plugin: NuxtPlugin | string, opts: AddPluginOptions = {}) {
const nuxt = useNuxt()
// Normalize plugin
const plugin = normalizePlugin(_plugin)
// Remove any existing plugin with the same src
nuxt.options.plugins = nuxt.options.plugins.filter(p => normalizePlugin(p).src !== plugin.src)
// Prepend to array by default to be before user provided plugins since is usually used by modules
nuxt.options.plugins[opts.append ? 'push' : 'unshift'](plugin)
return plugin
}
/**
* Adds a template and registers as a nuxt plugin.
*/
export function addPluginTemplate (plugin: NuxtPluginTemplate | string, opts: AddPluginOptions = {}): NuxtPlugin {
const normalizedPlugin: NuxtPlugin = typeof plugin === 'string'
? { src: plugin }
// Update plugin src to template destination
: { ...plugin, src: addTemplate(plugin).dst }
return addPlugin(normalizedPlugin, opts)
}
addPlugin(pluginOptions, { append? })
addPluginTemplate(pluginOptions, { append? })
テンプレート
import { existsSync } from 'node:fs'
import { basename, parse, resolve } from 'pathe'
import hash from 'hash-sum'
import type { NuxtTemplate } from '@nuxt/schema'
import { useNuxt } from './context'
/**
* Renders given template using lodash template during build into the project buildDir
*/
export function addTemplate (_template: NuxtTemplate | string) {
const nuxt = useNuxt()
// Noprmalize template
const template = normalizeTemplate(_template)
// Remove any existing template with the same filename
nuxt.options.build.templates = nuxt.options.build.templates
.filter(p => normalizeTemplate(p).filename !== template.filename)
// Add to templates array
nuxt.options.build.templates.push(template)
return template
}
/**
* Normalize a nuxt template object
*/
export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplate {
if (!template) {
throw new Error('Invalid template: ' + JSON.stringify(template))
}
// Normalize
if (typeof template === 'string') {
template = { src: template }
} else {
template = { ...template }
}
// Use src if provided
if (template.src) {
if (!existsSync(template.src)) {
throw new Error('Template not found: ' + template.src)
}
if (!template.filename) {
const srcPath = parse(template.src)
template.filename = template.fileName ||
`${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`
}
}
if (!template.src && !template.getContents) {
throw new Error('Invalid template. Either getContents or src options should be provided: ' + JSON.stringify(template))
}
if (!template.filename) {
throw new Error('Invalid template. Either filename should be provided: ' + JSON.stringify(template))
}
// Always write declaration files
if (template.filename.endsWith('.d.ts')) {
template.write = true
}
// Resolve dst
if (!template.dst) {
const nuxt = useNuxt()
template.dst = resolve(nuxt.options.buildDir, template.filename)
}
return template
}
addTemplate(templateOptions)
サーバー
import type { Middleware } from 'h3'
import { useNuxt } from './context'
export interface ServerMiddleware {
route?: string,
handler: Middleware | string
}
/** Adds a new server middleware to the end of the server middleware array. */
export function addServerMiddleware (middleware: ServerMiddleware) {
useNuxt().options.serverMiddleware.push(middleware)
}
addServerMiddleware(serverMiddleware)
Resolving
import { promises as fsp, existsSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { basename, dirname, resolve, join, normalize, isAbsolute } from 'pathe'
import { globby } from 'globby'
import { tryUseNuxt, useNuxt } from './context'
import { tryResolveModule } from './internal/cjs'
import { isIgnored } from './ignore'
export interface ResolvePathOptions {
/** Base for resolving paths from. Default is Nuxt rootDir. */
cwd?: string
/** An object of aliases. Default is Nuxt configured aliases. */
alias?: Record<string, string>
/** The file extensions to try. Default is Nuxt configured extensions. */
extensions?: string[]
}
/**
* Resolve full path to a file or directory respecting Nuxt alias and extensions options
*
* If path could not be resolved, normalized input path will be returned
*/
export async function resolvePath (path: string, opts: ResolvePathOptions = {}): Promise<string> {
// Always normalize input
const _path = path
path = normalize(path)
// Fast return if the path exists
if (isAbsolute(path) && existsSync(path)) {
return path
}
// Use current nuxt options
const nuxt = useNuxt()
const cwd = opts.cwd || (nuxt ? nuxt.options.rootDir : process.cwd())
const extensions = opts.extensions || (nuxt ? nuxt.options.extensions : ['.ts', '.mjs', '.cjs', '.json'])
const modulesDir = nuxt ? nuxt.options.modulesDir : []
// Resolve aliases
path = resolveAlias(path)
// Resolve relative to cwd
if (!isAbsolute(path)) {
path = resolve(cwd, path)
}
// Check if resolvedPath is a file
let isDirectory = false
if (existsSync(path)) {
isDirectory = (await fsp.lstat(path)).isDirectory()
if (!isDirectory) {
return path
}
}
// Check possible extensions
for (const ext of extensions) {
// path.[ext]
const pathWithExt = path + ext
if (existsSync(pathWithExt)) {
return pathWithExt
}
// path/index.[ext]
const pathWithIndex = join(path, 'index' + ext)
if (isDirectory && existsSync(pathWithIndex)) {
return pathWithIndex
}
}
// Try to resolve as module id
const resolveModulePath = tryResolveModule(_path, { paths: [cwd, ...modulesDir] })
if (resolveModulePath) {
return resolveModulePath
}
// Return normalized input
return path
}
/**
* Try to resolve first existing file in paths
*/
export async function findPath (paths: string|string[], opts?: ResolvePathOptions, pathType: 'file' | 'dir' = 'file'): Promise<string|null> {
if (!Array.isArray(paths)) {
paths = [paths]
}
for (const path of paths) {
const rPath = await resolvePath(path, opts)
if (await existsSensitive(rPath)) {
const isDirectory = (await fsp.lstat(rPath)).isDirectory()
if (!pathType || (pathType === 'file' && !isDirectory) || (pathType === 'dir' && isDirectory)) {
return rPath
}
}
}
return null
}
/**
* Resolve path aliases respecting Nuxt alias options
*/
export function resolveAlias (path: string, alias?: Record<string, string>): string {
if (!alias) {
alias = tryUseNuxt()?.options.alias || {}
}
for (const key in alias) {
if (key === '@' && !path.startsWith('@/')) { continue } // Don't resolve @foo/bar
if (path.startsWith(key)) {
path = alias[key] + path.slice(key.length)
}
}
return path
}
export interface Resolver {
resolve(...path): string
resolvePath(path: string, opts?: ResolvePathOptions): Promise<string>
}
/**
* Create a relative resolver
*/
export function createResolver (base: string | URL): Resolver {
if (!base) {
throw new Error('`base` argument is missing for createResolver(base)!')
}
base = base.toString()
if (base.startsWith('file://')) {
base = dirname(fileURLToPath(base))
}
return {
resolve: (...path) => resolve(base as string, ...path),
resolvePath: (path, opts) => resolvePath(path, { cwd: base as string, ...opts })
}
}
// --- Internal ---
async function existsSensitive (path: string) {
if (!existsSync(path)) { return false }
const dirFiles = await fsp.readdir(dirname(path))
return dirFiles.includes(basename(path))
}
export async function resolveFiles (path: string, pattern: string | string[]) {
const files = await globby(pattern, { cwd: path, followSymbolicLinks: true })
return files.filter(p => !isIgnored(p)).map(p => resolve(path, p))
}
resolvePath (path, resolveOptions?)
resolveAlias (path, aliases?)
findPath (paths, resolveOptions?)
createResolver (base)
ビルダー
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
import { useNuxt } from './context'
export interface ExtendConfigOptions {
/**
* Install plugin on dev
*
* @default true
*/
dev?: boolean
/**
* Install plugin on build
*
* @default true
*/
build?: boolean
}
export interface ExtendWebpackConfigOptions extends ExtendConfigOptions {
/**
* Install plugin on server side
*
* @default true
*/
server?: boolean
/**
* Install plugin on client side
*
* @default true
*/
client?: boolean
/**
* Install plugin on modern build
*
* @default true
* @deprecated Nuxt 2 only
*/
modern?: boolean
}
export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
/**
* Extend Webpack config
*
* The fallback function might be called multiple times
* when applying to both client and server builds.
*/
export function extendWebpackConfig (
fn: ((config: WebpackConfig)=> void),
options: ExtendWebpackConfigOptions = {}
) {
const nuxt = useNuxt()
if (options.dev === false && nuxt.options.dev) {
return
}
if (options.build === false && nuxt.options.build) {
return
}
nuxt.hook('webpack:config', (configs: WebpackConfig[]) => {
if (options.server !== false) {
const config = configs.find(i => i.name === 'server')
if (config) {
fn(config)
}
}
if (options.client !== false) {
const config = configs.find(i => i.name === 'client')
if (config) {
fn(config)
}
}
// Nuxt 2 backwards compatibility
if (options.modern !== false) {
const config = configs.find(i => i.name === 'modern')
if (config) {
fn(config)
}
}
})
}
/**
* Extend Vite config
*/
export function extendViteConfig (
fn: ((config: ViteConfig) => void),
options: ExtendViteConfigOptions = {}
) {
const nuxt = useNuxt()
if (options.dev === false && nuxt.options.dev) {
return
}
if (options.build === false && nuxt.options.build) {
return
}
nuxt.hook('vite:extend', ({ config }) => fn(config))
}
/**
* Append Webpack plugin to the config.
*/
export function addWebpackPlugin (plugin: WebpackPluginInstance, options?: ExtendWebpackConfigOptions) {
extendWebpackConfig((config) => {
config.plugins = config.plugins || []
config.plugins.push(plugin)
}, options)
}
/**
* Append Vite plugin to the config.
*/
export function addVitePlugin (plugin: VitePlugin, options?: ExtendViteConfigOptions) {
extendViteConfig((config) => {
config.plugins = config.plugins || []
config.plugins.push(plugin)
}, options)
}
extendWebpackConfig(callback, options?)
extendViteConfig(callback, options?)
addWebpackPlugin(webpackPlugin, options?)
addVitePlugin(vitePlugin, options?)
nuxi add
Nuxtアプリケーションにエンティティを足場として組み込むことができます。
npx nuxi add [--cwd] [--force] <TEMPLATE> <NAME>
Option | デフォルト | 説明 |
---|---|---|
TEMPLATE |
- | 生成されるファイルのテンプレートを指定します。 |
NAME |
- | 生成されるファイルの名前を指定します。 |
--cwd |
. |
対象アプリケーションのディレクトリです。 |
--force |
false |
ファイルがすでに存在する場合、強制的に上書きします。 |
例
npx nuxi add component TheHeader
add
コマンドは、新しい要素を生成します。
-
component:
npx nuxi add component TheHeader
-
composable:
npx nuxi add composable foo
-
レイアウト:
npx nuxi add layout custom
-
プラグイン:
npx nuxi add plugin analytics
-
ページ:
npx nuxi add page about または npx nuxi add page "category/[id]"
-
ミドルウェア:
npx nuxi add middleware auth
-
api:
npx nuxi add api hello (CLIで/server/api以下にファイルが生成されます)
nuxi analyze
``
nuxi build
``
nuxi dev
``
nuxi info
``
nuxi init
``
nuxi preview
``
nuxi typecheck
``
nuxi upgrade
``
例
Hello World
最小限の Nuxt 3 アプリケーションは、app.vue と nuxt.config.js のファイルだけで済みます。
<script setup>
const version = 2 + 1
</script>
<template>
<div class="hello">
Hello Nuxt {{ version }}!
</div>
</template>
<style scoped>
.hello {
font-family: Arial, Helvetica, sans-serif;
font-size: 3rem;
padding: 10rem;
}
</style>
エラーハンドリング
この例では、pages、プラグイン、コンポーネント、ミドルウェアという異なるコンテキストでのエラー処理方法を示しています。
<script setup lang="ts">
import { throwError } from '#app'
const route = useRoute()
if ('setup' in route.query) {
throw new Error('error in setup')
}
if ('mounted' in route.query) {
onMounted(() => {
throw new Error('error in mounted')
})
}
function triggerError () {
throw new Error('manually triggered error')
}
</script>
<template>
<NuxtExampleLayout example="app/error-handling">
<template #nav>
<nav class="flex align-center gap-4 p-4">
<NuxtLink to="/" class="n-link-base">
Home
</NuxtLink>
<NuxtLink to="/other" class="n-link-base">
Other
</NuxtLink>
<NuxtLink to="/404" class="n-link-base">
404
</NuxtLink>
<NuxtLink to="/?middleware" class="n-link-base">
Middleware
</NuxtLink>
<button class="n-link-base" @click="throwError">
Trigger fatal error
</button>
<button class="n-link-base" @click="triggerError">
Trigger non-fatal error
</button>
</nav>
</template>
<template #footer>
<div class="text-center p-4 op-50">
Current route: <code>{{ route.path }}</code>
</div>
</template>
</NuxtExampleLayout>
</template>
<template>
<div class="relative font-sans" n="green6">
<div class="container max-w-200 mx-auto py-10 px-4">
<h1>{{ error.message }}</h1>
There was an error 😱
<br>
<button @click="handleError">
Clear error
</button>
<br>
<NuxtLink to="/404">
Trigger another error
</NuxtLink>
<br>
<NuxtLink to="/">
Navigate home
</NuxtLink>
</div>
</div>
</template>
<script setup lang="ts">
defineProps({
error: Object
})
const handleError = () => clearError({ redirect: '/' })
</script>
<script setup>
const hasIssue = ref(true)
const fixIssue = (error) => {
hasIssue.value = false
error.value = null
}
</script>
<template>
<NuxtErrorBoundary>
<throw-error v-if="hasIssue" />
<div v-else>
Component is working ^_^
</div>
<template #error="{ error }">
Component failed to Render -_-
<button @click="fixIssue(error)">
(fix the issue)
</button>
</template>
</NuxtErrorBoundary>
</template>
<script setup>
throw new Error('Deliberate error by <ThrowError>')
</script>
<template>
<div>Should never see this</div>
</template>
export default defineNuxtRouteMiddleware((to) => {
if ('middleware' in to.query) {
return throwError('error in middleware')
}
})
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vue:error', (..._args) => {
console.log('vue:error')
// if (process.client) {
// console.log(..._args)
// }
})
nuxtApp.hook('app:error', (..._args) => {
console.log('app:error')
// if (process.client) {
// console.log(..._args)
// }
})
nuxtApp.vueApp.config.errorHandler = (..._args) => {
console.log('global error handler')
// if (process.client) {
// console.log(..._args)
// }
}
})
import { useQuery, defineMiddleware } from 'h3'
export default defineMiddleware((req, res, next) => {
if ('api' in useQuery(req)) {
throw new Error('Server middleware error')
}
next()
})
プラグイン
この例では、plugins/ ディレクトリを使用して、プラグインを自動登録する方法を示します。
<template>
<NuxtExampleLayout example="app/plugins">
<div>{{ $myPlugin() }}</div>
</NuxtExampleLayout>
</template>
export default defineNuxtPlugin((/* nuxtApp */) => {
return {
provide: {
myPlugin: () => 'String generated from my auto-imported plugin!'
}
}
})
テレポート
Vue 3 には、<Teleport> コンポーネントがあり、Vue アプリケーションの外、DOM の別の場所にコンテンツをレンダリングします。
この例では、クライアントサイドおよびサーバーサイドのレンダリングで、<Teleport> を使用する方法を示します。
<template>
<NuxtExampleLayout example="app/teleport">
<div>
<!-- SSR Teleport -->
<Teleport to="body">
SSR Teleport
</Teleport>
<!-- Client Teleport -->
<ClientOnly>
<Teleport to="body">
<div>
Hello from a client-side teleport!
</div>
</Teleport>
</ClientOnly>
<!-- Modal Example -->
<MyModal />
</div>
</NuxtExampleLayout>
</template>
<script>
export default {
data () {
return {
open: false
}
}
}
</script>
<template>
<NButton @click="open = true">
Open Modal
</NButton>
<Teleport to="body">
<NCard v-if="open" class="modal p4">
<p>Hello from the modal!</p>
<NButton @click="open = false">
Close
</NButton>
</NCard>
</Teleport>
</template>
<style scoped>
.modal {
position: fixed;
z-index: 999;
top: 20%;
left: 50%;
width: 300px;
margin-left: -150px;
}
</style>
コンポーネント
components/ ディレクトリにあるコンポーネントは自動的にインポートされ、テンプレートで自動的に使用することができます。他のディレクトリを設定して、コンポーネントの自動インポートをサポートすることができます。
<template>
<NuxtExampleLayout example="auto-imports/components">
<h1 class="text-xl opacity-50">
Auto Imported Components
</h1>
<div>
<!-- Auto Imported Components -->
<HelloWorld class="text-2xl" />
<Nuxt3 class="text-2xl" />
<ParentFolderHello class="mt-6" />
<NuxtWithPrefix class="mt-6" />
</div>
</NuxtExampleLayout>
</template>
<template>
<div>
This is HelloWorld component!
</div>
</template>
<template>
<b style="color: #00C58E">
From Nuxt 3
</b>
</template>
<template>
<NCard class="flex flex-col gap-1 p-4">
Components in sub folders
<code class="op50">`components/parent-folder/hello.vue`</code>
can be auto imported with folder name as the prefix:
<code class="text-lime6"><ParentFolderHello/></code>
</NCard>
</template>
<template>
<NCard class="flex flex-col gap-1 p-4">
<code>nuxt.config.ts</code> can specify other directories like
<code class="op50">`other-components-folder/`</code>
to import components from and specify prefixes.
</NCard>
</template>
コンポーザブル
この例は、composables/ ディレクトリを使用して composables を自動インポートする方法を示しています。コンポーネントファイルがデフォルトのエクスポートを提供する場合、コンポーザブルの名前はファイルの名前にマップされます。名前付きエクスポートはそのまま使用できます。
<template>
<NuxtExampleLayout example="auto-imports/composables">
<p>Named export <code>useA</code> : {{ a }}</p>
<p>Named export <code>useB</code> : {{ b }}</p>
<p>Named export <code>useC</code> : {{ c }}</p>
<p>Named export <code>useD</code> : {{ d }}</p>
<p>Default export <code>useFoo</code> : {{ foo }}</p>
</NuxtExampleLayout>
</template>
<script setup>
const a = useA()
const b = useB()
const c = useC()
const d = useD()
const foo = useFoo()
</script>
import { useState } from '#app'
export function useA () {
return 'a'
}
function useB () {
return 'b'
}
function _useC () {
return 'c'
}
export const useD = () => {
return 'd'
}
export { useB, _useC as useC }
export default function () {
return useState('foo', () => 'bar')
}
useAsyncData
この例は、useAsyncData を使用して API エンドポイントからデータをフェッチする方法を示しています。
<script setup>
const showMountain = ref(false)
const refreshing = ref(false)
const refreshAll = async () => {
refreshing.value = true
try {
await refreshNuxtData()
} finally {
refreshing.value = false
}
}
</script>
<template>
<NuxtExampleLayout example="composables/use-async-data">
<div>
<div class="flex justify-center gap-2">
<NButton @click="showMountain = !showMountain">
{{ showMountain ? 'Hide' : 'Show' }} Mountain
</NButton>
<NButton :disabled="refreshing" @click="refreshAll">
Refetch All Data
</NButton>
</div>
<div class="flex justify-center gap-2">
<CounterExample />
</div>
<div class="flex justify-center gap-2">
<MountainExample v-if="showMountain" />
</div>
</div>
</NuxtExampleLayout>
</template>
<script setup>
const ctr = ref(0)
const { data, pending, refresh } = await useAsyncData('/api/hello', () => $fetch(`/api/hello/${ctr.value}`), { watch: [ctr] })
</script>
<template>
<div>
{{ data }}
<div class="flex justify-center gap-2">
<NButton :disabled="pending" @click="ctr++">
+
</NButton>
<NButton :disabled="pending" @click="refresh">
⟳
</NButton>
</div>
</div>
</template>
<script setup>
const { data: mountain } = await useFetch(
'https://api.nuxtjs.dev/mountains/mount-everest'
)
</script>
<template>
<pre class="text-sm text-left overflow-auto">{{ mountain }}</pre>
</template>
export default req => `Hello world (${req.url.substr(1)}) (Generated at ${new Date().toGMTString()})`
useCookie
この例は、useCookie API を使用して、クライアントとサーバーの両方が使用できる少量のデータを永続化する方法を示しています。
<script setup lang="ts">
const user = useCookie<{ name: string }>('user')
const logins = useCookie<number>('logins')
const name = ref('')
const login = () => {
logins.value = (logins.value || 0) + 1
user.value = { name: name.value }
}
const logout = () => {
user.value = null
}
</script>
<template>
<NuxtExampleLayout class="h-50" example="composables/use-cookie">
<template v-if="user">
<h1 class="text-3xl mb-3">
Welcome, {{ user.name }}! 👋
</h1>
<div>
<NTip n="green6" icon="carbon:idea" class="inline-flex">
You have logged in <b>{{ logins }} times</b>!
</NTip>
</div>
<div class="mt-3">
<NButton n="red" icon="carbon:logout" @click="logout">
Log out
</NButton>
</div>
</template>
<template v-else>
<h1 class="text-3xl mb-3">
Login
</h1>
<NTextInput v-model="name" n="lg" class="w-100 m-auto" placeholder="Enter your name..." @keypress.enter="login()" />
<div class="mt-3">
<NButton icon="carbon:user" :disabled="!name" @click="login">
Log in
</NButton>
</div>
</template>
</NuxtExampleLayout>
</template>
useFetch
この例は、useFetch を使用して API エンドポイントからデータをフェッチする方法を示しています。
<script setup>
const count = ref(1)
const { data } = await useFetch(() => `/api/hello/${count.value}`, { params: { token: 123 } })
</script>
<template>
<NuxtExampleLayout example="composables/use-fetch">
<div>
Fetch result:
<pre class="text-left"><code>{{ data }}</code></pre>
<NButton @click="count++">
+
</NButton>
</div>
</NuxtExampleLayout>
</template>
import { useQuery } from 'h3'
import { parseURL } from 'ufo'
export default req => ({
path: '/api/hello' + parseURL(req.url).pathname,
query: useQuery(req)
})
useHead
この例は、useHead および Nuxt の組み込みコンポーネントを使用して、メタデータをページの先頭にバインドする方法を示しています。
<template>
<NuxtExampleLayout example="composables/use-head">
<div
class="bg-gray-400/10 border-2 border-dashed border-gray-400/50 rounded-xl py-8 px-2 op-80"
>
There are renderless <code><Html></code>, <code><Meta></code>, <code><Title></code> components
<br>that can magically bind the meta using Vue template.
</div>
<Html :lang="String(dynamic)">
<Head>
<Title>Luck number: {{ dynamic }}</Title>
<Meta name="description" :content="`My page's ${dynamic} description`" />
<Link rel="preload" href="/test.txt" as="script" />
</Head>
</Html>
<div class="mt-5">
<NButton @click="dynamic = Math.round(Math.random() * 100)">
Click me and see the dynamic title
</NButton>
</div>
</NuxtExampleLayout>
</template>
<script>
export default {
setup () {
useHead({
titleTemplate: '%s - useHead example',
bodyAttrs: {
class: 'test'
}
})
return { dynamic: ref(49) }
},
head: {
title: 'Another title'
}
}
</script>
useState
useState は、SSR に適した ref の代替品です。その値はサーバー側のレンダリング後に保持され、一意のキーを使用してすべてのコンポーネント間で共有されます。
<script setup>
const counter = useState('counter', () => Math.round(Math.random() * 1000))
</script>
<template>
<NuxtExampleLayout example="composables/use-state">
<div>Counter: {{ counter }}</div>
<div>
<NButton class="font-mono" @click="counter++">
+
</NButton>
<NButton class="font-mono" @click="counter--">
-
</NButton>
</div>
</NuxtExampleLayout>
</template>
レイアウト
この例は、デフォルトレイアウトとカスタムレイアウトを定義する方法を示しています。
<template>
<div class="p-4">
Custom layout defined with <code>definePageMeta</code>
<br>
<NuxtLink to="/">
Back to home
</NuxtLink>
</div>
</template>
<script>
definePageMeta({
layout: 'custom'
})
</script>
<template>
<div>
<p>Content inside <code>default</code> layout</p>
<br>
<NuxtLink to="/">
Back to home
</NuxtLink>
</div>
</template>
<template>
<div class="p-4">
Custom layout defined dynamically with the <code>NuxtLayout</code> component
<br>
<NuxtLayout :name="layout">
Default slot
<br>
<button class="border p-1 rounded" @click="layout ? layout = null : layout = 'custom'">
Switch layout
</button>
<template #header>
Header slot
</template>
</NuxtLayout>
<br>
<NuxtLink to="/">
Back to home
</NuxtLink>
</div>
</template>
<script>
definePageMeta({
layout: false
})
export default {
data: () => ({
layout: 'custom'
})
}
</script>
<template>
<NuxtExampleLayout example="routing/layouts">
<template #nav>
<nav class="flex align-center gap-4 p-4">
<NuxtLink to="/default">
Default layout
</NuxtLink>
<NuxtLink to="/custom">
Custom layout
</NuxtLink>
<NuxtLink to="/dynamic">
Dynamic layout
</NuxtLink>
</nav>
</template>
</NuxtExampleLayout>
</template>
<template>
<div>
<strong>Custom layout</strong>
Header slot:
<slot name="header">
Default slot content for <code>Custom</code> layout
</slot>
<slot />
</div>
</template>
<script>
export default {
created () {
console.log('layout created')
}
}
</script>
<template>
<div>
Default layout
<slot />
</div>
</template>
<template>
<div>
Other layout
<slot />
</div>
</template>
ミドルウェア
この例は、middleware/ ディレクトリまたはプラグインを使用してルートミドルウェアを追加する方法と、それらをグローバルにまたはページごとに使用する方法を示しています。
<script setup lang="ts">
const route = useRoute()
</script>
<template>
<NuxtExampleLayout example="routing/middleware">
<NuxtPage />
<template #nav>
<nav class="flex align-center gap-4 p-4">
<NuxtLink to="/" class="n-link-base">
Home
</NuxtLink>
<NuxtLink to="/forbidden" class="n-link-base">
Forbidden
</NuxtLink>
<NuxtLink to="/redirect" class="n-link-base">
Redirect
</NuxtLink>
</nav>
</template>
<template #footer>
<div class="text-center p-4 op-50">
Current route: <code>{{ route.path }}</code>
</div>
</template>
</NuxtExampleLayout>
</template>
<template>
<div>
Forbidden
</div>
</template>
<script setup>
definePageMeta({
// This is an example of inline middleware
middleware: () => {
console.log('Strictly forbidden.')
return false
}
})
</script>
<template>
<div>
Home
</div>
</template>
<template>
<div>
You should never see this page
</div>
</template>
<script setup>
definePageMeta({
middleware: 'redirect-me'
})
</script>
<template>
<div>
You've landed on a page that wasn't in the menu!
</div>
</template>
<script setup>
definePageMeta({
middleware: 'named-test'
})
</script>
export default defineNuxtRouteMiddleware(() => {
console.log('running global middleware')
})
export default defineNuxtRouteMiddleware((to) => {
const { $config } = useNuxtApp()
if ($config) {
console.log('Accessed runtime config within middleware.')
}
console.log('Heading to', to.path, 'but I think we should go somewhere else...')
return '/secret'
})
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('this global middleware was added in a plugin')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('this named middleware was added in a plugin')
})
})
<NuxtLink>
この例は、<NuxtLink> を使用するさまざまな方法を示しています。
<template>
<NuxtExampleLayout example="routing/nuxt-link" class="example">
<NuxtPage />
</NuxtExampleLayout>
</template>
<style>
.example a {
font-family: Arial, Helvetica, sans-serif;
padding: 1rem 10rem;
display: block;
}
</style>
<template>
<NuxtLink to="/">
Index page
</NuxtLink>
</template>
<template>
<div>
<NuxtLink to="/about">
About page
</NuxtLink>
<NuxtLink to="https://nuxtjs.org">
Nuxt website
</NuxtLink>
<NuxtLink to="https://twitter.com/nuxt_js" target="_blank">
Nuxt Twitter with a blank target
</NuxtLink>
<NuxtLink to="https://discord.nuxtjs.org" target="_blank" rel="noopener">
Nuxt Discord with a blank target and custom rel value
</NuxtLink>
<NuxtLink to="https://github.com/nuxt" no-rel>
Nuxt GitHub without rel attribute
</NuxtLink>
<MyNuxtLink to="https://nuxtjs.org">
Nuxt website with a custom link component with no default rel attribute
</MyNuxtLink>
<MyNuxtLink to="/">
Index page with a custom link component with a custom active class
</MyNuxtLink>
<NuxtLink>Link without href and to</NuxtLink>
</div>
</template>
export default defineNuxtLink({
componentName: 'MyNuxtLink',
externalRelAttribute: '',
activeClass: 'active',
exactActiveClass: 'exact-active'
})
Pages
この例は、pages/ ディレクトリを使用してアプリケーションルートを作成する方法を示しています。
<script setup lang="ts">
const route = useRoute()
</script>
<template>
<NuxtExampleLayout example="routing/pages">
<NuxtPage />
<template #nav>
<nav class="flex align-center gap-4 p-4">
<NuxtLink to="/" class="n-link-base">
Home
</NuxtLink>
<NuxtLink to="/about" class="n-link-base">
About
</NuxtLink>
<NuxtLink to="/parent" class="n-link-base">
Parent (index)
</NuxtLink>
<NuxtLink to="/parent/b" class="n-link-base">
Parent (b)
</NuxtLink>
<button class="n-link-base" @click="$router.push(`/parent/reload-${(Math.random() * 100).toFixed()}`)">
Keyed child
</button>
<button class="n-link-base" @click="$router.push(`/parent/static-${(Math.random() * 100).toFixed()}`)">
Non-keyed child
</button>
</nav>
</template>
<template #footer>
<div class="text-center p-4 op-50">
Current route: <code>{{ route.path }}</code>
</div>
</template>
</NuxtExampleLayout>
</template>
<template>
<div>
test-{{ $route.params.id }}
</div>
</template>
<template>
<div>
Parent/b
</div>
</template>
<template>
<div>
Parent/index
</div>
</template>
<template>
<div>
Child reloaded: {{ reloads }}
</div>
</template>
<script setup>
const reloads = useState('reload', () => 0)
onMounted(() => { reloads.value++ })
</script>
<template>
<div>
Child reloaded: {{ reloads }}
</div>
</template>
<script setup>
const reloads = useState('static', () => 0)
onMounted(() => { reloads.value++ })
definePageMeta({
key: 'static'
})
</script>
<template>
<div>
About
</div>
</template>
<template>
<div>
Home
</div>
</template>
<template>
<div>
Parent
<NuxtPage />
</div>
</template>
ユニバーサルルーター
この例は、pages/ および vue-router に依存しない Nuxt ユニバーサルルーティングユーティリティを示しています。
<script setup lang="ts">
const route = useRoute()
const timer = useState('timer', () => 0)
</script>
<template>
<NuxtExampleLayout example="routing/universal-router">
A page...
<br>
<template v-if="timer">
Processing navigation in 0.{{ timer }}s
</template>
<template #nav>
<nav class="flex align-center gap-4 p-4">
<NuxtLink to="/" class="n-link-base">
Home
</NuxtLink>
<NuxtLink to="/forbidden" class="n-link-base">
Forbidden
</NuxtLink>
<NuxtLink to="/redirect" class="n-link-base">
Redirect
</NuxtLink>
</nav>
</template>
<template #footer>
<div class="text-center p-4 op-50">
Current route: <code>{{ route.path }}</code>
</div>
</template>
</NuxtExampleLayout>
</template>
export default defineNuxtPlugin(() => {
const timer = useState('timer', () => 0)
if (process.client) {
addRouteMiddleware(async () => {
console.log('Starting timer...')
timer.value = 5
do {
await new Promise(resolve => setTimeout(resolve, 100))
timer.value--
} while (timer.value)
console.log('...and navigating')
})
}
addRouteMiddleware((to) => {
if (to.path === '/forbidden') {
return false
}
})
addRouteMiddleware((to) => {
const { $config } = useNuxtApp()
if ($config) {
console.log('Accessed runtime config within middleware.')
}
if (to.path !== '/redirect') { return }
console.log('Heading to', to.path, 'but I think we should go somewhere else...')
return '/secret'
})
})
サーバールーター
この例は、server/api ディレクトリ内にサーバールートを作成する方法を示しています。
<template>
<NuxtExampleLayout example="server/routes">
<p>Fetched from <pre>/api/mountain</pre>: {{ mountain }}</p>
</NuxtExampleLayout>
</template>
<script setup>
const { data: mountain } = await useFetch('/api/mountain')
</script>
export default defineEventHandler(() => {
return {
title: 'Mount Everest',
description: "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. The China–Nepal border runs across its summit point",
height: '8,848 m',
countries: [
'China',
'Nepal'
],
continent: 'Asia',
image: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg',
dir: '/mountains',
path: '/mountains/mount-everest',
slug: 'mount-everest',
updatedAt: '2020-12-11T15:40:35.000Z'
}
})
設定の拡張
この例は、nuxt.config.ts の extends キーを使用して base/ ディレクトリを Nuxt アプリケーションのベースとして使用し、そのコンポーネント、コンポーザブル、または config を使用して、必要に応じてそれらをオーバーライド(再定義)する方法を示しています。
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
extends: [
'./ui',
'./base'
],
publicRuntimeConfig: {
theme: {
primaryColor: 'user_primary'
}
},
modules: [
'@nuxt/ui'
]
})
<template>
<BaseButton class="fancy-button">
<slot />
</BaseButton>
</template>
<style scoped>
button {
appearance: none;
background-color: #FAFBFC;
border: 1px solid rgba(27, 31, 35, 0.15);
border-radius: 6px;
box-shadow: rgba(27, 31, 35, 0.04) 0 1px 0, rgba(255, 255, 255, 0.25) 0 1px 0 inset;
color: #24292E;
}
</style>
<script setup>
const themeConfig = useRuntimeConfig().theme
const foo = useFoo()
const bar = getBar()
</script>
<template>
<NuxtExampleLayout example="advanced/config-extends">
theme runtimeConfig
<pre>{{ JSON.stringify(themeConfig, null, 2) }}</pre>
<BaseButton>Base Button</BaseButton>
<FancyButton>Fancy Button</FancyButton>
<UIButton>UI Button</UIButton>
<br>
{{ foo }} {{ bar }}
<br>
{{ $myPlugin() }}
</NuxtExampleLayout>
</template>
<style scoped>
pre {
text-align: left;
}
</style>
export default () => 'hello'
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
autoImports: {
dirs: ['utils']
},
publicRuntimeConfig: {
theme: {
primaryColor: 'base_primary',
secondaryColor: 'base_secondary'
}
}
})
<template>
<button role="button">
<slot />
</button>
</template>
<template>
<BaseButton>
<slot />
</BaseButton>
</template>
import { useState } from '#app'
export const useFoo = () => useState('foo', () => 'foo')
export default defineNuxtRouteMiddleware(() => {
console.log('Hello from extended middleware !')
})
<template>
<div>
Hello from extended page !
</div>
</template>
<script setup>
definePageMeta({
middleware: 'foo'
})
</script>
export default defineNuxtPlugin((/* nuxtApp */) => {
return {
provide: {
myPlugin: () => 'String generated from my auto-imported plugin!'
}
}
})
export default () => 'base'
export const getBar = () => 'bar'
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
components: [
{ path: './components', prefix: 'UI' }
]
})
<script setup>
defineProps({
color: {
type: String,
default: 'black'
}
})
</script>
<template>
<button class="ui-button" :style="{ color }">
<slot />
</button>
</template>
モジュール拡張 pages
この例では、モジュール内で extendPages を使用して新しいテストページを定義します。
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: [
'~/modules/pages',
'@nuxt/ui'
]
})
<template>
<div>
Go to <NuxtLink to="/test">
Test Page
</NuxtLink>
</div>
</template>
<template>
<NuxtExampleLayout example="advanced/module-extend-pages">
<slot />
</NuxtExampleLayout>
</template>
import { defineNuxtModule, extendPages } from '@nuxt/kit'
import { resolve } from 'pathe'
export default defineNuxtModule({
setup () {
extendPages((pages) => {
// Add /test page
pages.push({
name: 'Test',
path: '/test',
file: resolve(__dirname, './pages/test.vue')
})
})
}
})
<template>
<div>
<p>
Go to <NuxtLink to="/">
Homepage
</NuxtLink>
</p>
<p>Test page added by module</p>
</div>
</template>
テスト
<script setup>
</script>
<template>
<div class="hello">
Hello Nuxt!
</div>
</template>
<style scoped>
.hello {
font-family: Arial, Helvetica, sans-serif;
font-size: 3rem;
padding: 10rem;
}
</style>
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { setup, $fetch, isDev } from '@nuxt/test-utils'
describe('example', async () => {
await setup({
rootDir: fileURLToPath(new URL('..', import.meta.url)),
server: true
})
it('Renders Hello Nuxt', async () => {
expect(await $fetch('/')).toMatch('Hello Nuxt!')
})
if (isDev()) {
it('[dev] ensure vite client script is added', async () => {
expect(await $fetch('/')).toMatch('/_nuxt/@vite/client"')
})
}
})
リアクティビティトランスフォーム
この例は、Nuxt3 でのリアクティビティトランスフォーム(コンパイラーマクロによってコードを変換する仕組み)のサポートを示しています。
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: [
'@nuxt/ui'
],
experimental: {
reactivityTransform: true
}
// builder: 'webpack'
})
<script setup lang="ts">
let count = $ref(0)
function inc () {
count++
}
function dec () {
count--
}
</script>
<template>
<NuxtExampleLayout example="experimental/reactivity-transform">
<div>
<Label :count="count" />
<div class="flex gap-1 justify-center">
<NButton @click="inc()">
Inc
</NButton>
<NButton @click="dec()">
Dec
</NButton>
</div>
</div>
</NuxtExampleLayout>
</template>
<script setup lang="ts">
const { count } = defineProps<{
count: number,
}>()
const doubled = $computed(() => count * 2)
</script>
<template>
<div class="pb2">
Count <b>{{ count }}</b><br>
Doubled <b>{{ doubled }}</b>
</div>
</template>
Wasm
この例は、Nuxt3 での WebAssembly のサーバー側サポートを示しています。
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
nitro: {
experiments: {
wasm: true
}
},
modules: [
'@nuxt/ui'
]
})
<script setup>
const a = ref(100)
const b = ref(250)
const { data } = await useAsyncData('sum',
() => $fetch('/api/sum', { params: { a: a.value, b: b.value } })
)
</script>
<template>
<NuxtExampleLayout example="experimental/wasm">
<p>
<code>a = 100</code>
</p>
<p>
<code>b = 250</code>
</p>
<p>
Computation performed server-side with WASM :
<br>
<code>{{ a }} + {{ b }} = {{ data.sum }}</code>
</p>
</NuxtExampleLayout>
</template>
import { useQuery, lazyHandle } from 'h3'
export default lazyHandle(async () => {
const { exports: { sum } } = await loadWasmInstance(
// @ts-ignore
() => import('~/server/wasm/sum.wasm')
)
return (req) => {
const { a = 0, b = 0 } = useQuery(req)
return { sum: sum(a, b) }
}
})
async function loadWasmInstance (importFn, imports = {}) {
const init = await importFn().then(m => m.default || m)
const { instance } = await init(imports)
return instance
}
asm`sum
jname
;; https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format
;; https://webassembly.github.io/wabt/demo/wat2wasm/
(module
(func (export "sum") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add))
Vite-node
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: [
'@nuxt/ui'
],
experimental: {
viteNode: true
}
})
<script setup lang="ts">
const count = ref(0)
function inc () {
count.value++
}
function dec () {
count.value--
}
</script>
<template>
<NuxtExampleLayout example="experimental/vite-node">
<div>
{{ count }}
<div class="flex gap-1 justify-center">
<NButton @click="inc()">
Inc
</NButton>
<NButton @click="dec()">
Dec
</NButton>
</div>
</div>
</NuxtExampleLayout>
</template>
Locale
この例は、サーバー側とクライアント側の両方で、アプリケーションの Locale を処理するために構成可能な Locale を定義する方法を示しています。
<script setup lang="ts">
// see ../compoables/locale.ts for the implementation
const locales = useLocales()
const locale = useLocale()
const date = useLocaleDate(new Date('2016-10-26') /* NUXT_BIRTHDAY */)
</script>
<template>
<NuxtExampleLayout example="other/locale">
<h1 class="text-xl opacity-50">
Nuxt birthday
</h1>
<p class="text-4xl">
{{ date }}
</p>
<div class="mt-4" />
<label for="locale-chooser">Preview a different locale</label>
<select id="locale-chooser" v-model="locale" class="m-auto w-50 border n-border-base rounded p-1">
<option v-for="l of locales" :key="l" :value="l">
{{ l }}
</option>
</select>
</NuxtExampleLayout>
</template>
import type { Ref } from 'vue'
export const useLocale = () => useState<string>('locale', () => useDefaultLocale().value)
export const useDefaultLocale = (fallback = 'en-US') => {
const locale = ref(fallback)
if (process.server) {
// Learn more about the nuxtApp interface on https://v3.nuxtjs.org/docs/usage/nuxt-app#nuxtapp-interface-advanced
const nuxtApp = useNuxtApp()
const reqLocale = nuxtApp.ssrContext?.req.headers['accept-language']?.split(',')[0]
if (reqLocale) {
locale.value = reqLocale
}
} else if (process.client) {
const navLang = navigator.language
if (navLang) {
locale.value = navLang
}
}
return locale
}
export const useLocales = () => {
const locale = useLocale()
const locales = ref([
'en-US',
'en-GB',
'ko-KR',
'zh-CN',
'ar-EG',
'fa-IR',
'ja-JP-u-ca-japanese'
])
if (!locales.value.includes(locale.value)) {
locales.value.unshift(locale.value)
}
return locales
}
// Using Intl.DateTimeFormat for language-sensitive date and time formatting
// Learn more: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
export const useLocaleDate = (date: Ref<Date> | Date, locale = useLocale()) => {
return computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: 'full' }).format(unref(date)))
}