nuxt logo

ドキュメント翻訳(非公式)

components

components/ ディレクトリは、すべての Vue コンポーネントを配置する場所です。

Nuxt は、このディレクトリ内のコンポーネント(および使用しているモジュールによって登録されるコンポーネント)を自動的にインポートします。

Directory Structure
-| components/
---| AppHeader.vue
---| AppFooter.vue
app.vue
<template>
  <div>
    <AppHeader />
    <NuxtPage />
    <AppFooter />
  </div>
</template>

コンポーネント名

ネストされたディレクトリにコンポーネントがある場合、例えば:

Directory Structure
-| components/
---| base/
-----| foo/
-------| Button.vue

... コンポーネントの名前は、そのパスディレクトリとファイル名に基づき、重複するセグメントは削除されます。したがって、コンポーネントの名前は次のようになります:

<BaseFooButton />

明確にするために、コンポーネントのファイル名がその名前と一致することをお勧めします。したがって、上記の例では、Button.vueBaseFooButton.vue にリネームすることができます。

コンポーネントをパスではなく名前に基づいて自動インポートしたい場合は、設定オブジェクトの拡張形式を使用して pathPrefix オプションを false に設定する必要があります:

nuxt.config.ts
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 プロパティに渡す必要があります。

例えば:

pages/index.vue
<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 バンドルサイズの最適化に役立ちます。

pages/index.vue
<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

コンポーネントがビューポートに表示されるときにハイドレーションを行います。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-visible />
  </div>
</template>
こちらも参照 IntersectionObserver のオプションについて詳しく読む

内部では、Vue の組み込みの hydrateOnVisible 戦略 を使用しています。

hydrate-on-idle

ブラウザがアイドル状態のときにコンポーネントをハイドレーションします。これは、コンポーネントをできるだけ早くロードする必要があるが、重要なレンダリングパスをブロックしない場合に適しています。

最大タイムアウトとして機能する数値を渡すこともできます。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-idle />
  </div>
</template>

内部では、Vue の組み込みの hydrateOnIdle 戦略 を使用しています。

hydrate-on-interaction

指定されたインタラクション(例:クリック、マウスオーバー)後にコンポーネントをハイドレーションします。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-interaction="mouseover" />
  </div>
</template>

イベントやイベントのリストを渡さない場合、デフォルトでは pointerenterfocus でハイドレーションされます。

内部では、Vue の組み込みの hydrateOnInteraction 戦略 を使用しています。

hydrate-on-media-query

ウィンドウがメディアクエリに一致するときにコンポーネントをハイドレーションします。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
  </div>
</template>

内部では、Vue の組み込みの hydrateOnMediaQuery 戦略 を使用しています。

hydrate-after

指定された遅延(ミリ秒単位)後にコンポーネントをハイドレーションします。

pages/index.vue
<template>
  <div>
    <LazyMyComponent :hydrate-after="2000" />
  </div>
</template>

hydrate-when

ブール条件に基づいてコンポーネントをハイドレーションします。

pages/index.vue
<template>
  <div>
    <LazyMyComponent :hydrate-when="isReady" />
  </div>
</template>
<script setup lang="ts">
const isReady = ref(false)
function myFunction() {
  // カスタムハイドレーション戦略をトリガー...
  isReady.value = true
}
</script>

hydrate-never

コンポーネントをハイドレーションしません。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-never />
  </div>
</template>

ハイドレーションイベントのリスニング

すべての遅延ハイドレーションコンポーネントは、ハイドレーションされたときに @hydrated イベントを発行します。

pages/index.vue
<template>
  <div>
    <LazyMyComponent hydrate-on-visible @hydrated="onHydrate" />
  </div>
</template>

<script setup lang="ts">
function onHydrate() {
  console.log("コンポーネントがハイドレーションされました!")
}
</script>

注意点とベストプラクティス

遅延ハイドレーションはパフォーマンスの向上を提供できますが、正しく使用することが重要です:

  1. ビューポート内のコンテンツを優先する: 重要な、折りたたみ上のコンテンツには遅延ハイドレーションを避けてください。すぐに必要でないコンテンツに最適です。

  2. 条件付きレンダリング: 遅延コンポーネントで v-if="false" を使用する場合、遅延ハイドレーションは必要ないかもしれません。通常の遅延コンポーネントを使用するだけで済みます。

  3. 共有状態: 複数のコンポーネント間で共有される状態(v-model)に注意してください。1つのコンポーネントでモデルを更新すると、そのモデルにバインドされたすべてのコンポーネントでハイドレーションがトリガーされる可能性があります。

  4. 各戦略の意図された使用ケースを使用する: 各戦略は特定の目的に最適化されています。

    • hydrate-when は、常にハイドレーションが必要でないかもしれないコンポーネントに最適です。
    • hydrate-after は、特定の時間を待つことができるコンポーネントに適しています。
    • hydrate-on-idle は、ブラウザがアイドル状態のときにハイドレーションできるコンポーネントに適しています。
  5. インタラクティブなコンポーネントに hydrate-never を使用しない: ユーザーのインタラクションが必要なコンポーネントは、ハイドレーションしない設定にしてはいけません。

直接インポート

Nuxt の自動インポート機能をバイパスしたい、またはする必要がある場合、#components からコンポーネントを明示的にインポートすることもできます。

pages/index.vue
<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 ディレクトリのみがスキャンされます。他のディレクトリを追加したり、このディレクトリのサブフォルダ内でコンポーネントがスキャンされる方法を変更したい場合は、設定に追加のディレクトリを追加できます:

nuxt.config.ts
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 キーを使用できます:

nuxt.config.ts
export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
      extensions: ['.vue'], // [!code ++]
    }
  ]
})

クライアントコンポーネント

コンポーネントがクライアントサイドでのみレンダリングされることを意図している場合、コンポーネントに .client サフィックスを追加できます。

Directory Structure
| components/
--| Comments.client.vue
pages/example.vue
<template>
  <div>
    <!-- このコンポーネントはクライアントサイドでのみレンダリングされます -->
    <Comments />
  </div>
</template>

この機能は Nuxt の自動インポートと #components インポートでのみ機能します。これらのコンポーネントを実際のパスから明示的にインポートしても、クライアント専用コンポーネントにはなりません。

.client コンポーネントはマウント後にのみレンダリングされます。onMounted() を使用してレンダリングされたテンプレートにアクセスするには、onMounted() フックのコールバック内で await nextTick() を追加します。

こちらも参照 api > components > client-only

サーバーコンポーネント

サーバーコンポーネントは、クライアントサイドアプリ内で個々のコンポーネントをサーバーレンダリングすることを可能にします。静的サイトを生成している場合でも、Nuxt 内でサーバーコンポーネントを使用することが可能です。これにより、動的コンポーネント、サーバーレンダリングされた HTML、さらには静的なマークアップのチャンクを組み合わせた複雑なサイトを構築することが可能になります。

サーバーコンポーネントは単独で使用することも、クライアントコンポーネントと組み合わせて使用することもできます。

Daniel Roe の Nuxt サーバーコンポーネントに関するガイドを読む。

スタンドアロンのサーバーコンポーネント

スタンドアロンのサーバーコンポーネントは常にサーバーでレンダリングされ、アイランドコンポーネントとも呼ばれます。

そのプロップが更新されると、ネットワークリクエストが発生し、レンダリングされた HTML がその場で更新されます。

サーバーコンポーネントは現在実験的であり、使用するには nuxt.config で 'component islands' 機能を有効にする必要があります:

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

これで、.server サフィックスを持つサーバー専用コンポーネントを登録し、アプリケーション内のどこでも自動的に使用できます。

Directory Structure
-| components/
---| HighlightedMarkdown.server.vue
pages/example.vue
<template>
  <div>
    <!--
      これにより、マークダウンの解析とハイライトライブラリがクライアントバンドルに含まれないことを意味し、
      自動的にサーバーでレンダリングされます。
     -->
    <HighlightedMarkdown markdown="# Headline" />
  </div>
</template>

サーバー専用コンポーネントは、<NuxtIsland> を内部で使用しており、lazy プロップと #fallback スロットがそれに渡されます。

サーバーコンポーネント(およびアイランド)は単一のルート要素を持たなければなりません。(HTML コメントも要素と見なされます。)

プロップは URL クエリパラメータを介してサーバーコンポーネントに渡されるため、URL の可能な長さによって制限されるため、プロップを介してサーバーコンポーネントに膨大なデータを渡さないように注意してください。

他のアイランド内にアイランドをネストする際には注意が必要です。各アイランドは追加のオーバーヘッドを追加します。

サーバー専用コンポーネントとアイランドコンポーネントのほとんどの機能、スロットやクライアントコンポーネントなどは、単一ファイルコンポーネントでのみ利用可能です。

サーバーコンポーネント内のクライアントコンポーネント

この機能を使用するには、設定内で experimental.componentIslands.selectiveClient を true にする必要があります。

nuxt-client 属性を設定することで、コンポーネントを部分的にハイドレーションし、クライアントサイドでロードされるようにすることができます。

components/ServerWithClient.vue
<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つの '半分' であり、サーバーとクライアントサイドでのコンポーネントの別々の実装のための高度なユースケースで使用できます。

Directory Structure
-| components/
---| Comments.client.vue
---| Comments.server.vue
pages/example.vue
<template>
  <div>
    <!-- このコンポーネントはサーバーで Comments.server をレンダリングし、ブラウザでマウントされた後に Comments.client をレンダリングします -->
    <Comments />
  </div>
</template>

組み込みの Nuxt コンポーネント

Nuxt が提供するコンポーネントには、<ClientOnly><DevOnly> などがあります。API ドキュメントでそれらについて詳しく読むことができます。

こちらも参照 api

ライブラリアーサー

自動ツリーシェイキングとコンポーネント登録を備えた Vue コンポーネントライブラリを作成するのは非常に簡単です。✨

@nuxt/kit から提供される addComponentsDir メソッドを使用して、Nuxt モジュール内でコンポーネントディレクトリを登録できます。

次のようなディレクトリ構造を想像してください:

Directory Structure
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.ts
-| pages/
---| index.vue
-| nuxt.config.ts

その後、awesome-ui/nuxt.tsaddComponentsDir フックを使用できます:

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 ファイルにインポートできます:

nuxt.config.ts
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 をサポートします。

サンプルコードの編集とプレビューexamples > features > auto-imports