components
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, },
],
})
これにより、Nuxt 2 で使用されているのと同じ戦略でコンポーネントが登録されます。たとえば、~/components/Some/MyComponent.vue は <SomeMyComponent> ではなく <MyComponent> として使用できます。
動的コンポーネント
Vue の <component :is="someComputedComponent"> 構文を使用したい場合は、Vue が提供する resolveComponent ヘルパーを使用するか、#components からコンポーネントを直接インポートして is prop に渡す必要があります。
例:
<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>
遅延 (または Lazy) ハイドレーション
Lazy コンポーネントは、アプリのチャンクサイズを制御するのに最適ですが、条件付きでレンダリングされない限り、積極的に読み込まれるため、常にランタイムパフォーマンスを向上させるわけではありません。実際のアプリケーションでは、一部のページには多くのコンテンツと多くのコンポーネントが含まれる場合がありますが、ほとんどの場合、ページが読み込まれた直後にそれらすべてがインタラクティブである必要はありません。それらすべてを積極的に読み込むと、パフォーマンスに悪影響を与える可能性があります。
アプリを最適化するために、一部のコンポーネントのハイドレーションを、それらが可視になるまで、またはブラウザがより重要なタスクを完了するまで遅延させたい場合があります。
Nuxt は、遅延 (または Lazy) ハイドレーションを使用してこれをサポートし、コンポーネントがいつインタラクティブになるかを制御できます。
ハイドレーション戦略
Nuxt は、さまざまな組み込みハイドレーション戦略を提供します。Lazy コンポーネントごとに 1 つの戦略のみを使用できます。
hydrate-never を持つコンポーネントの prop を変更すると、ハイドレートされます)v-bind を介して prop のオブジェクトを展開するのではなく)。また、#components からの直接インポートでは機能しません。hydrate-on-visible
コンポーネントがビューポートで可視になったときにハイドレートします。
<template>
<div>
<LazyMyComponent hydrate-on-visible />
</div>
</template>
hydrateOnVisible 戦略.hydrate-on-idle
ブラウザがアイドル状態のときにコンポーネントをハイドレートします。これは、コンポーネントをできるだけ早く読み込む必要があるが、クリティカルレンダリングパスをブロックしない場合に適しています。
最大タイムアウトとして機能する数値を渡すこともできます。
<template>
<div>
<LazyMyComponent hydrate-on-idle />
</div>
</template>
hydrateOnIdle 戦略.hydrate-on-interaction
指定されたインタラクション (例: クリック、マウスオーバー) の後にコンポーネントをハイドレートします。
<template>
<div>
<LazyMyComponent hydrate-on-interaction="mouseover" />
</div>
</template>
イベントまたはイベントのリストを渡さない場合、デフォルトでは pointerenter、click、focus でハイドレートされます。
hydrateOnInteraction 戦略.hydrate-on-media-query
ウィンドウがメディアクエリに一致したときにコンポーネントをハイドレートします。
<template>
<div>
<LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
</div>
</template>
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 () {
// trigger custom hydration strategy...
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('Component has been hydrated!')
}
</script>
注意点とベストプラクティス
遅延ハイドレーションはパフォーマンス上の利点をもたらしますが、正しく使用することが不可欠です
- ビューポート内のコンテンツを優先する: 重要な、ファーストビューのコンテンツには遅延ハイドレーションを使用しないでください。すぐに必要とされないコンテンツに最適です。
- 条件付きレンダリング: Lazy コンポーネントで
v-if="false"を使用する場合、遅延ハイドレーションは必要ないかもしれません。通常の Lazy コンポーネントを使用するだけで十分です。 - 共有状態: 複数のコンポーネント間での共有状態 (
v-model) に注意してください。1 つのコンポーネントでモデルを更新すると、そのモデルにバインドされているすべてのコンポーネントでハイドレーションがトリガーされる可能性があります。 - 各戦略の意図されたユースケースを使用する: 各戦略は特定の目的に最適化されています。
hydrate-whenは、常にハイドレートする必要がない可能性があるコンポーネントに最適です。hydrate-afterは、特定の期間待機できるコンポーネント用です。hydrate-on-idleは、ブラウザがアイドル状態のときにハイドレートできるコンポーネント用です。
- インタラクティブなコンポーネントで
hydrate-neverを避ける: コンポーネントがユーザーインタラクションを必要とする場合、決してハイドレートしないように設定すべきではありません。
直接インポート
Nuxt の自動インポート機能をバイパスしたい場合、またはバイパスする必要がある場合は、#components からコンポーネントを明示的にインポートすることもできます。
<script setup lang="ts">
import { LazyMountainsList, NuxtLink } 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' },
// It's important that this comes last if you have overrides you wish to apply
// to sub-directories of `~/components`.
//
// ~/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',
})
},
})
<template>
<div>
<!-- the component uses the name we specified and is auto-imported -->
<MyAutoImportedComponent />
</div>
</template>
コンポーネント拡張子
デフォルトでは、nuxt.config.ts の extensions キーで指定された拡張子を持つすべてのファイルがコンポーネントとして扱われます。コンポーネントとして登録されるファイル拡張子を制限する必要がある場合は、コンポーネントディレクトリ宣言とその extensions キーの拡張形式を使用できます。
export default defineNuxtConfig({
components: [
{
path: '~/components',
extensions: ['.vue'], },
],
})
クライアントコンポーネント
コンポーネントがクライアント側でのみレンダリングされることを意図している場合、コンポーネントに .client サフィックスを追加できます。
| components/
--| Comments.client.vue
<template>
<div>
<!-- this component will only be rendered on client side -->
<Comments />
</div>
</template>
#components インポートでのみ機能します。これらのコンポーネントを実際のパスから明示的にインポートしても、クライアントのみのコンポーネントには変換されません。.client コンポーネントはマウントされた後にのみレンダリングされます。onMounted() を使用してレンダリングされたテンプレートにアクセスするには、onMounted() フックのコールバックに await nextTick() を追加します。サーバーコンポーネント
サーバーコンポーネントを使用すると、クライアントサイドアプリ内で個々のコンポーネントをサーバーサイドレンダリングできます。静的サイトを生成している場合でも、Nuxt 内でサーバーコンポーネントを使用できます。これにより、動的コンポーネント、サーバーレンダリングされた HTML、さらには静的マークアップチャンクを組み合わせた複雑なサイトを構築できます。
サーバーコンポーネントは単独で使用することも、クライアントコンポーネントと組み合わせて使用することもできます。
スタンドアロンサーバーコンポーネント
スタンドアロンサーバーコンポーネントは、常にサーバーでレンダリングされます。これはアイランドコンポーネントとも呼ばれます。
そのプロップが更新されると、ネットワークリクエストが発生し、レンダリングされた HTML がその場で更新されます。
サーバーコンポーネントは現在実験段階であり、使用するには nuxt.config で「コンポーネントアイランド」機能を有効にする必要があります
export default defineNuxtConfig({
experimental: {
componentIslands: true,
},
})
これで、.server サフィックスを使用してサーバー専用コンポーネントを登録し、アプリケーションのどこでも自動的に使用できます。
-| components/
---| HighlightedMarkdown.server.vue
<template>
<div>
<!--
this will automatically be rendered on the server, meaning your markdown parsing + highlighting
libraries are not included in your client bundle.
-->
<HighlightedMarkdown markdown="# Headline" />
</div>
</template>
サーバー専用コンポーネントは、内部的に <NuxtIsland> を使用しているため、lazy プロップと #fallback スロットの両方が渡されます。
サーバーコンポーネント内のクライアントコンポーネント
experimental.componentIslands.selectiveClient が true である必要があります。クライアントサイドで読み込みたいコンポーネントに nuxt-client 属性を設定することで、コンポーネントを部分的にハイドレートできます。
<template>
<div>
<HighlightedMarkdown markdown="# Headline" />
<!-- Counter will be loaded and hydrated client-side -->
<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>
<!-- this component will render Comments.server on the server then Comments.client once mounted in the browser -->
<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 { addComponentsDir, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup () {
const resolver = createResolver(import.meta.url)
// Add ./components dir to the list
addComponentsDir({
path: resolver.resolve('./components'),
prefix: 'awesome',
})
},
})
それだけです!これで、プロジェクト内で、UI ライブラリを Nuxt モジュールとして nuxt.config ファイルにインポートできます。
export default defineNuxtConfig({
modules: ['awesome-ui/nuxt'],
})
...そして、モジュールコンポーネント ( awesome- プレフィックス付き) を app/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 もサポートします。