components
components/ ディレクトリは、すべての Vue コンポーネントを配置する場所です。
Nuxt は、このディレクトリ内のコンポーネント(および使用しているモジュールによって登録されるコンポーネント)を自動的にインポートします。
-| components/
---| AppHeader.vue
---| AppFooter.vue
<template>
<div>
<AppHeader />
<NuxtPage />
<AppFooter />
</div>
</template>
コンポーネント名
ネストされたディレクトリにコンポーネントがある場合、例えば:
-| components/
---| base/
-----| foo/
-------| Button.vue
... コンポーネントの名前は、そのパスディレクトリとファイル名に基づき、重複するセグメントは削除されます。したがって、コンポーネントの名前は次のようになります:
<BaseFooButton />
明確にするために、コンポーネントのファイル名がその名前と一致することをお勧めします。したがって、上記の例では、Button.vue
を BaseFooButton.vue
にリネームすることができます。
コンポーネントをパスではなく名前に基づいて自動インポートしたい場合は、設定オブジェクトの拡張形式を使用して pathPrefix
オプションを false
に設定する必要があります:
export default defineNuxtConfig({
components: [
{
path: '~/components',
pathPrefix: false, // [!code ++]
},
],
});
これにより、Nuxt 2 で使用されていたのと同じ戦略を使用してコンポーネントが登録されます。例えば、~/components/Some/MyComponent.vue
は <MyComponent>
として使用可能であり、<SomeMyComponent>
ではありません。
動的コンポーネント
Vue の <component :is="someComputedComponent">
構文を使用したい場合は、Vue が提供する resolveComponent
ヘルパーを使用するか、#components
から直接コンポーネントをインポートして is
プロパティに渡す必要があります。
例えば:
<script setup lang="ts">
import { SomeComponent } from '#components'
const MyButton = resolveComponent('MyButton')
</script>
<template>
<component :is="clickable ? MyButton : 'div'" />
<component :is="SomeComponent" />
</template>
動的コンポーネントを処理するために resolveComponent
を使用する場合、コンポーネントの名前以外は挿入しないようにしてください。名前はリテラル文字列であり、変数を含んではいけません。文字列はコンパイルステップで静的に解析されます。
推奨されませんが、すべてのコンポーネントをグローバルに登録することもできます。これにより、すべてのコンポーネントに非同期チャンクが作成され、アプリケーション全体で利用可能になります。
export default defineNuxtConfig({
components: {
+ global: true,
+ dirs: ['~/components']
},
})
また、~/components/global
ディレクトリに配置するか、ファイル名に .global.vue
サフィックスを使用することで、一部のコンポーネントを選択的にグローバル登録することもできます。上記のように、各グローバルコンポーネントは別々のチャンクでレンダリングされるため、この機能を過度に使用しないように注意してください。
global
オプションは、コンポーネントディレクトリごとに設定することもできます。
動的インポート
コンポーネントを動的にインポートする(コンポーネントを遅延ロードする)には、コンポーネントの名前に Lazy
プレフィックスを追加するだけです。これは、コンポーネントが常に必要でない場合に特に便利です。
Lazy
プレフィックスを使用することで、適切なタイミングまでコンポーネントコードのロードを遅らせることができ、JavaScript バンドルサイズの最適化に役立ちます。
<script setup lang="ts">
const show = ref(false)
</script>
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">Show List</button>
</div>
</template>
遅延(または遅延)ハイドレーション
遅延コンポーネントはアプリのチャンクサイズを制御するのに優れていますが、条件付きでレンダリングされない限り、実行時のパフォーマンスを向上させることはありません。実際のアプリケーションでは、多くのコンテンツやコンポーネントが含まれるページがあり、ページがロードされた直後にすべてがインタラクティブである必要はありません。すべてを急いでロードすると、パフォーマンスに悪影響を与える可能性があります。
アプリを最適化するために、コンポーネントが表示されるまで、またはブラウザがより重要なタスクを完了するまで、いくつかのコンポーネントのハイドレーションを遅らせることを検討するかもしれません。
Nuxt は遅延(または遅延)ハイドレーションをサポートしており、コンポーネントがインタラクティブになるタイミングを制御できます。
ハイドレーション戦略
Nuxt は、さまざまな組み込みのハイドレーション戦略を提供します。遅延コンポーネントごとに使用できる戦略は1つだけです。
現在、Nuxt の組み込みの遅延ハイドレーションは単一ファイルコンポーネント(SFC)でのみ機能し、テンプレートでプロップを定義する必要があります(v-bind
を介してプロップのオブジェクトをスプレッドするのではなく)。また、#components
からの直接インポートでは機能しません。
hydrate-on-visible
コンポーネントがビューポートに表示されるときにハイドレーションを行います。
<template>
<div>
<LazyMyComponent hydrate-on-visible />
</div>
</template>
内部では、Vue の組み込みの hydrateOnVisible
戦略 を使用しています。
hydrate-on-idle
ブラウザがアイドル状態のときにコンポーネントをハイドレーションします。これは、コンポーネントをできるだけ早くロードする必要があるが、重要なレンダリングパスをブロックしない場合に適しています。
最大タイムアウトとして機能する数値を渡すこともできます。
<template>
<div>
<LazyMyComponent hydrate-on-idle />
</div>
</template>
内部では、Vue の組み込みの hydrateOnIdle
戦略 を使用しています。
hydrate-on-interaction
指定されたインタラクション(例:クリック、マウスオーバー)後にコンポーネントをハイドレーションします。
<template>
<div>
<LazyMyComponent hydrate-on-interaction="mouseover" />
</div>
</template>
イベントやイベントのリストを渡さない場合、デフォルトでは pointerenter
と focus
でハイドレーションされます。
内部では、Vue の組み込みの hydrateOnInteraction
戦略 を使用しています。
hydrate-on-media-query
ウィンドウがメディアクエリに一致するときにコンポーネントをハイドレーションします。
<template>
<div>
<LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
</div>
</template>
内部では、Vue の組み込みの hydrateOnMediaQuery
戦略 を使用しています。
hydrate-after
指定された遅延(ミリ秒単位)後にコンポーネントをハイドレーションします。
<template>
<div>
<LazyMyComponent :hydrate-after="2000" />
</div>
</template>
hydrate-when
ブール条件に基づいてコンポーネントをハイドレーションします。
<template>
<div>
<LazyMyComponent :hydrate-when="isReady" />
</div>
</template>
<script setup lang="ts">
const isReady = ref(false)
function myFunction() {
// カスタムハイドレーション戦略をトリガー...
isReady.value = true
}
</script>
hydrate-never
コンポーネントをハイドレーションしません。
<template>
<div>
<LazyMyComponent hydrate-never />
</div>
</template>
ハイドレーションイベントのリスニング
すべての遅延ハイドレーションコンポーネントは、ハイドレーションされたときに @hydrated
イベントを発行します。
<template>
<div>
<LazyMyComponent hydrate-on-visible @hydrated="onHydrate" />
</div>
</template>
<script setup lang="ts">
function onHydrate() {
console.log("コンポーネントがハイドレーションされました!")
}
</script>
注意点とベストプラクティス
遅延ハイドレーションはパフォーマンスの向上を提供できますが、正しく使用することが重要です:
-
ビューポート内のコンテンツを優先する: 重要な、折りたたみ上のコンテンツには遅延ハイドレーションを避けてください。すぐに必要でないコンテンツに最適です。
-
条件付きレンダリング: 遅延コンポーネントで
v-if="false"
を使用する場合、遅延ハイドレーションは必要ないかもしれません。通常の遅延コンポーネントを使用するだけで済みます。 -
共有状態: 複数のコンポーネント間で共有される状態(
v-model
)に注意してください。1つのコンポーネントでモデルを更新すると、そのモデルにバインドされたすべてのコンポーネントでハイドレーションがトリガーされる可能性があります。 -
各戦略の意図された使用ケースを使用する: 各戦略は特定の目的に最適化されています。
hydrate-when
は、常にハイドレーションが必要でないかもしれないコンポーネントに最適です。hydrate-after
は、特定の時間を待つことができるコンポーネントに適しています。hydrate-on-idle
は、ブラウザがアイドル状態のときにハイドレーションできるコンポーネントに適しています。
-
インタラクティブなコンポーネントに
hydrate-never
を使用しない: ユーザーのインタラクションが必要なコンポーネントは、ハイドレーションしない設定にしてはいけません。
直接インポート
Nuxt の自動インポート機能をバイパスしたい、またはする必要がある場合、#components
からコンポーネントを明示的にインポートすることもできます。
<script setup lang="ts">
import { NuxtLink, LazyMountainsList } from '#components'
const show = ref(false)
</script>
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">Show List</button>
<NuxtLink to="/">Home</NuxtLink>
</div>
</template>
カスタムディレクトリ
デフォルトでは、~/components
ディレクトリのみがスキャンされます。他のディレクトリを追加したり、このディレクトリのサブフォルダ内でコンポーネントがスキャンされる方法を変更したい場合は、設定に追加のディレクトリを追加できます:
export default defineNuxtConfig({
components: [
// ~/calendar-module/components/event/Update.vue => <EventUpdate />
{ path: '~/calendar-module/components' },
// ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog />
{ path: '~/user-module/components', pathPrefix: false },
// ~/components/special-components/Btn.vue => <SpecialBtn />
{ path: '~/components/special-components', prefix: 'Special' },
// 上書きを適用したいサブディレクトリがある場合は、これを最後に配置することが重要です。
//
// ~/components/Btn.vue => <Btn />
// ~/components/base/Btn.vue => <BaseBtn />
'~/components'
]
})
ネストされたディレクトリは最初に追加する必要があります。順番にスキャンされます。
npm パッケージ
npm パッケージからコンポーネントを自動インポートしたい場合、ローカルモジュールで addComponent
を使用して登録できます。
import { addComponent, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup() {
// import { MyComponent as MyAutoImportedComponent } from 'my-npm-package'
addComponent({
name: 'MyAutoImportedComponent',
export: 'MyComponent',
filePath: 'my-npm-package',
})
},
})
コンポーネント拡張子
デフォルトでは、nuxt.config.ts
の extensions キー で指定された拡張子を持つファイルはコンポーネントとして扱われます。コンポーネントとして登録されるファイル拡張子を制限する必要がある場合は、コンポーネントディレクトリ宣言の拡張形式とその extensions
キーを使用できます:
export default defineNuxtConfig({
components: [
{
path: '~/components',
extensions: ['.vue'], // [!code ++]
}
]
})
クライアントコンポーネント
コンポーネントがクライアントサイドでのみレンダリングされることを意図している場合、コンポーネントに .client
サフィックスを追加できます。
| components/
--| Comments.client.vue
<template>
<div>
<!-- このコンポーネントはクライアントサイドでのみレンダリングされます -->
<Comments />
</div>
</template>
この機能は Nuxt の自動インポートと #components
インポートでのみ機能します。これらのコンポーネントを実際のパスから明示的にインポートしても、クライアント専用コンポーネントにはなりません。
.client
コンポーネントはマウント後にのみレンダリングされます。onMounted()
を使用してレンダリングされたテンプレートにアクセスするには、onMounted()
フックのコールバック内で await nextTick()
を追加します。
サーバーコンポーネント
サーバーコンポーネントは、クライアントサイドアプリ内で個々のコンポーネントをサーバーレンダリングすることを可能にします。静的サイトを生成している場合でも、Nuxt 内でサーバーコンポーネントを使用することが可能です。これにより、動的コンポーネント、サーバーレンダリングされた HTML、さらには静的なマークアップのチャンクを組み合わせた複雑なサイトを構築することが可能になります。
サーバーコンポーネントは単独で使用することも、クライアントコンポーネントと組み合わせて使用することもできます。
Daniel Roe の Nuxt サーバーコンポーネントに関するガイドを読む。
スタンドアロンのサーバーコンポーネント
スタンドアロンのサーバーコンポーネントは常にサーバーでレンダリングされ、アイランドコンポーネントとも呼ばれます。
そのプロップが更新されると、ネットワークリクエストが発生し、レンダリングされた HTML がその場で更新されます。
サーバーコンポーネントは現在実験的であり、使用するには nuxt.config で 'component islands' 機能を有効にする必要があります:
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
これで、.server
サフィックスを持つサーバー専用コンポーネントを登録し、アプリケーション内のどこでも自動的に使用できます。
-| components/
---| HighlightedMarkdown.server.vue
<template>
<div>
<!--
これにより、マークダウンの解析とハイライトライブラリがクライアントバンドルに含まれないことを意味し、
自動的にサーバーでレンダリングされます。
-->
<HighlightedMarkdown markdown="# Headline" />
</div>
</template>
サーバー専用コンポーネントは、<NuxtIsland>
を内部で使用しており、lazy
プロップと #fallback
スロットがそれに渡されます。
サーバーコンポーネント(およびアイランド)は単一のルート要素を持たなければなりません。(HTML コメントも要素と見なされます。)
プロップは URL クエリパラメータを介してサーバーコンポーネントに渡されるため、URL の可能な長さによって制限されるため、プロップを介してサーバーコンポーネントに膨大なデータを渡さないように注意してください。
他のアイランド内にアイランドをネストする際には注意が必要です。各アイランドは追加のオーバーヘッドを追加します。
サーバー専用コンポーネントとアイランドコンポーネントのほとんどの機能、スロットやクライアントコンポーネントなどは、単一ファイルコンポーネントでのみ利用可能です。
サーバーコンポーネント内のクライアントコンポーネント
この機能を使用するには、設定内で experimental.componentIslands.selectiveClient
を true にする必要があります。
nuxt-client
属性を設定することで、コンポーネントを部分的にハイドレーションし、クライアントサイドでロードされるようにすることができます。
<template>
<div>
<HighlightedMarkdown markdown="# Headline" />
<!-- Counter はクライアントサイドでロードされ、ハイドレーションされます -->
<Counter nuxt-client :count="5" />
</div>
</template>
これはサーバーコンポーネント内でのみ機能します。クライアントコンポーネントのスロットは experimental.componentIsland.selectiveClient
を 'deep'
に設定した場合にのみ機能し、サーバーサイドでレンダリングされるため、クライアントサイドではインタラクティブではありません。
サーバーコンポーネントコンテキスト
サーバー専用またはアイランドコンポーネントをレンダリングする際、<NuxtIsland>
は NuxtIslandResponse
を返すフェッチリクエストを行います。(サーバーでレンダリングされる場合は内部リクエスト、クライアントサイドナビゲーションでレンダリングされる場合はネットワークタブで確認できるリクエストです。)
これにより:
- サーバーサイドで
NuxtIslandResponse
を作成するために新しい Vue アプリが作成されます。 - コンポーネントをレンダリングする際に新しい 'アイランドコンテキスト' が作成されます。
- アプリの残りの部分から 'アイランドコンテキスト' にアクセスすることはできず、アイランドコンポーネントからアプリの残りの部分のコンテキストにアクセスすることもできません。言い換えれば、サーバーコンポーネントまたはアイランドはアプリの残りの部分から 隔離 されています。
- プラグインはアイランドをレンダリングする際に再度実行されますが、
env: { islands: false }
が設定されている場合は除きます(オブジェクト構文プラグインで設定できます)。
アイランドコンポーネント内では、nuxtApp.ssrContext.islandContext
を通じてそのアイランドコンテキストにアクセスできます。アイランドコンポーネントがまだ実験的である間、このコンテキストの形式は変更される可能性があります。
スロットはインタラクティブであり、display: contents;
を持つ <div>
内にラップされています。
クライアントコンポーネントとペアリング
この場合、.server
+ .client
コンポーネントはコンポーネントの2つの '半分' であり、サーバーとクライアントサイドでのコンポーネントの別々の実装のための高度なユースケースで使用できます。
-| components/
---| Comments.client.vue
---| Comments.server.vue
<template>
<div>
<!-- このコンポーネントはサーバーで Comments.server をレンダリングし、ブラウザでマウントされた後に Comments.client をレンダリングします -->
<Comments />
</div>
</template>
組み込みの Nuxt コンポーネント
Nuxt が提供するコンポーネントには、<ClientOnly>
や <DevOnly>
などがあります。API ドキュメントでそれらについて詳しく読むことができます。
ライブラリアーサー
自動ツリーシェイキングとコンポーネント登録を備えた Vue コンポーネントライブラリを作成するのは非常に簡単です。✨
@nuxt/kit
から提供される addComponentsDir
メソッドを使用して、Nuxt モジュール内でコンポーネントディレクトリを登録できます。
次のようなディレクトリ構造を想像してください:
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.ts
-| pages/
---| index.vue
-| nuxt.config.ts
その後、awesome-ui/nuxt.ts
で addComponentsDir
フックを使用できます:
import { createResolver, defineNuxtModule, addComponentsDir } from '@nuxt/kit'
export default defineNuxtModule({
setup() {
const resolver = createResolver(import.meta.url)
// ./components ディレクトリをリストに追加
addComponentsDir({
path: resolver.resolve('./components'),
prefix: 'awesome',
})
},
})
これで完了です!プロジェクト内で、UI ライブラリを Nuxt モジュールとして nuxt.config
ファイルにインポートできます:
export default defineNuxtConfig({
modules: ['awesome-ui/nuxt']
})
... そして、pages/index.vue
でモジュールコンポーネント(awesome-
プレフィックス付き)を直接使用できます:
<template>
<div>
My <AwesomeButton>UI button</AwesomeButton>!
<awesome-alert>Here's an alert!</awesome-alert>
</div>
</template>
これにより、使用されている場合にのみコンポーネントが自動的にインポートされ、node_modules/awesome-ui/components/
内のコンポーネントを更新するときに HMR をサポートします。
※このページは Nuxt.js 公式ドキュメントの翻訳ページ(非公式)です。
公式ドキュメントの該当ページはこちら:
https://nuxt.com/docs/3.x/guide/directory-structure/components