最終更新日: 2023年08月15日

コンポーネント ディレクトリ

components/ ディレクトリは、Vueコンポーネントを配置する場所です。これらのコンポーネントは、ページや他のコンポーネントの内部でインポートして使用することができます(詳細はこちらをご覧ください)。

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

  • ts
| components/
--| TheHeader.vue
--| TheFooter.vue
  • layouts/default.vue
  • vue
<template>
  <div>
    <TheHeader />
    <slot />
    <TheFooter />
  </div>
</template>

カスタム ディレクトリ

デフォルトでは、~/componentsディレクトリのみがスキャンされます。他のディレクトリを追加したり、このディレクトリのサブフォルダ内のコンポーネントに変更したりする場合は、追加のディレクトリを設定する必要があります。

  • layouts/default.vue
  • ts
export default defineNuxtConfig({
  components: [
    { path: '~/components/special-components', prefix: 'Special' },
    '~/components'
  ]
})

コンポーネントの拡張子

デフォルトでは、nuxt.config.tsのextensionsキーで指定された拡張子を持つすべてのファイルがコンポーネントとして扱われます。コンポーネントとして(登録されるべき)ファイルの拡張子を制限する必要がある場合は、componentsディレクトリ宣言の拡張形式とそのextensionsキーを使用することができます。

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

コンポーネント名

もし、ネストされたディレクトリ内にコンポーネントがある場合は、次のようになります:

  • ts
| components/
--| base/
----| foo/
------| Button.vue

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

  • vue
<BaseFooButton />

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

  • ts
export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
+     pathPrefix: false,
    },
  ],
});

これにより、Nuxt2で使用されていたものと同じ方法でコンポーネントが登録できます。例えば、~/components/Some/MyComponent.vue は<SomeMyComponent>ではなく<MyComponent>として使用できるようになります。

動的なコンポーネント

Vueの<component :is="someComputedComponent"> 構文を使用したい場合は、Vueが提供するresolveComponentヘルパーを使用するか、直接#componentsから(コンポーネントを)インポートしてisプロパティに渡す必要があります。

事例:
  • ts
<script setup lang="ts">
import { SomeComponent } from '#components'

const MyButton = resolveComponent('MyButton')
</script>

<template>
  <component :is="clickable ? MyButton : 'div'" />
  <component :is="SomeComponent" />
</template>

代替案としてはお勧めしませんが、すべてのコンポーネントをグローバルに登録することもできます。これにより、すべてのコンポーネントに対して非同期のチャンクが作成され、アプリケーション全体で利用可能になります。

  • ts
 export default defineNuxtConfig({
    components: {
+     global: true,
+     dirs: ['~/components']
    },
 })

また、特定のコンポーネントを選択してグローバルに登録する場合は、~/components/globalディレクトリに配置することもできます。

動的なインポート

コンポーネントを動的にインポートするために(コンポーネントの遅延読み込み)、コンポーネントの名前の前にLazyプレフィックスを追加するだけです。

  • layouts/default.vue
  • vue
<template>
  <div>
    <TheHeader />
    <slot />
    <LazyTheFooter />
  </div>
</template>

Lazy プレフィックスを使用すると、コンポーネントが常に必要ではない場合に特に便利です。Lazy プレフィックスを使うことで、コンポーネントのコードを必要なタイミングまで遅延させることができます。これは JavaScript バンドルのサイズを最適化するのに役立ちます。

  • pages/index.vue
  • ts
<script>
export default {
  data() {
    return {
      show: false
    }
  }
}
</script>

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

直接的なインポート

必要に応じて、Nuxtの自動インポート機能をバイパスするために、#components から明示的にコンポーネントをインポートすることもできます。

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

<ClientOnly> コンポーネント

Nuxtは、意図的にクライアント側のみでコンポーネントをレンダリングするための<ClientOnly>コンポーネントを提供します。クライアント側のみでコンポーネントをインポートする場合は、クライアント側のみのプラグイン内でコンポーネントを登録します。

  • pages/example.vue
  • vue
<template>
  <div>
    <Sidebar />
    <ClientOnly>
      <!-- このコンポーネントはクライアント側でのみレンダリングされます -->
      <Comments />
    </ClientOnly>
  </div>
</template>

<ClientOnly>がクライアント側でマウントされるまで、スロットをフォールバックとして使用します。

  • pages/index.vue
  • vue
<template>
  <div>
    <Sidebar />
    <!-- これにより、"span"要素がサーバー側でレンダリングされます -->
    <ClientOnly fallbackTag="span">
      <!-- このコンポーネントはクライアント側でのみレンダリングされます -->
      <Comments />
      <template #fallback>
        <!-- これはサーバーサイドでレンダリングされます -->
        <p>Loading comments...</p>
      </template>
    </ClientOnly>
  </div>
</template>

.client コンポーネント

コンポーネントがクライアント側でのみレンダリングされるようにしたい場合、コンポーネントの名前に .client 接尾辞を追加することができます。

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

.server コンポーネント

.serverコンポーネントは、単独で使用することも、.clientコンポーネントと組み合わせて使用することもできます。

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

スタンドアロンのサーバーコンポーネントは常にサーバー上でレンダリングされます。そのプロップスが更新されると、レンダリングされたHTMLをその場で更新するためのネットワークリクエストが発生します。

■ Nuxtドキュメントのための動画

Nuxt サーバー コンポーネント動画

Server componentsは現在実験的な機能であり、これを使用するには、nuxt.configで 'component islands' 機能を有効にする必要があります。

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

現在では、ファイル名に.serverを付与した(サーバーオンリーな)コンポーネントを登録することで、アプリケーション内のどこでも自動的に使用することができます。

  • ts
| components/
--| HighlightedMarkdown.server.vue
  • pages/example.vue
  • vue
<template>
  <div>
    <!--
      このコンポーネントは自動的にサーバー上でレンダリングされます。
      つまり、マークダウンの解析とハイライトライブラリは、
      クライアントのバンドルに含まれていません。
     -->
    <HighlightedMarkdown markdown="# Headline" />
  </div>
</template>

.client コンポーネントとの組み合わせ

この場合、.server + .client コンポーネントはコンポーネントの2つの「ハーフ」であり、サーバーとクライアント側で別々の実装を行う高度な使用例で使用することができます。

  • ts
| components/
--| Comments.client.vue
--| Comments.server.vue
  • pages/example.vue
  • vue
<template>
  <div>
    <!-- 
      このコンポーネントは、Comments.server を
      サーバーサイドでレンダリングし、その後クライアントサイドで
      マウントされると Comments.client をレンダリングします。 
    -->
    <Comments />
  </div>
</template>

<DevOnly> コンポーネント

Nuxtは、開発中にのみコンポーネントをレンダリングするための <DevOnly> コンポーネントを提供します。

このコンテンツは、本番ビルドおよびツリーシェイキングに含まれません。

  • pages/example.vue
  • vue
<template>
  <div>
    <Sidebar />
    <DevOnly>
      <!-- このコンポーネントは開発中にのみレンダリングされます -->
      <LazyDebugBar />

      <!-- もし本番環境で置き換えが必要な場合は -->
      <!-- nuxt preview を使用してこれらをテストすることを確認してください -->
      <template #fallback>
        <div><!-- "flex.justify-between" のための空のdivです --></div>
      </template>
    </DevOnly>
  </div>
</template>

<NuxtClientFallback> コンポーネント

Nuxtは、<NuxtClientFallback> コンポーネントを提供しています。このコンポーネントは、その子要素のいずれかがSSRでエラーを引き起こした場合に、クライアント側でその内容をレンダリングします。もしサーバー上でレンダリングに失敗した場合は、fallbackTagを指定して特定のタグをレンダリングさせることができます。

  • pages/example.vue
  • vue
<template>
  <div>
    <Sidebar />
    <!-- このコンポーネントはクライアント側でレンダリングされます -->
    <NuxtClientFallback fallback-tag="span">
      <Comments />
      <BrokeInSSR />
    </NuxtClientFallback>
  </div>
</template>

ライブラリの作者

Vueコンポーネントライブラリを自動的なツリーシェイキングとコンポーネント登録で作成することはとても簡単です。

Nuxtモジュール内で、ユーザーの設定を必要とせずにディレクトリリストを拡張するために、components:dirsフックを使用することができます。

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

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

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

  • ts
import { defineNuxtModule, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  hooks: {
    'components:dirs': (dirs) => {
      const { resolve } = createResolver(import.meta.url)
      // リストに ./components ディレクトリを追加してください
      dirs.push({
        path: fileURLToPath(resolve('./components')),
        prefix: 'awesome'
      })
    }
  }
})

以上で完了です!これでプロジェクト内で、UIライブラリを nuxt.config ファイルでNuxtモジュールとしてインポートすることができます。

  • nuxt.config.ts
  • ts
export default defineNuxtConfig({
  modules: ['awesome-ui/nuxt']
})

...そして、pages/index.vue でモジュールのコンポーネント(awesome- でプレフィックスされたコンポーネント)を直接使用することができます。

  • vue
<template>
  <div>
    My <AwesomeButton>UI button</AwesomeButton>!
    <awesome-alert>Here's an alert!</awesome-alert>
  </div>
</template>

これにより、コンポーネントが使用されている場合にのみ自動的にインポートされ、また、node_modules/awesome-ui/components/内のコンポーネントを更新する際にHMR(ホットモジュールリローディング)がサポートされます。