📗

プロダクト開発で使えるNuxt Auto-imports設定

2024/03/03に公開1

はじめに

皆さん、プロダクト開発で Nuxt を利用していますか?
Nuxt はバージョン 3 系に上がり優秀な機能がどんどんと追加されており、v3.10 の現在もその進化は留まるところを知りません。

Nuxt の機能の 1 つに Auto-imports という機能があり非常に優秀な機能です。
ただ、プロダクト・チーム開発においては混乱の元になりかねない危険性をはらんでおり、共通認識の確認やルールの設定が必要になります。

本記事では Auto-imports 機能を実際のプロダクトで利用する場合を想定して設定パターンを整理してみたいと思います。
後述で述べるもの私がプロダクト/個人開発時に検証したものばかりです。ドキュメントに記載されている例は下記記事で紹介しておりますので、合わせてご覧ください。

https://zenn.dev/comm_vue_nuxt/articles/nuxt3-extend-auto-imports

import 漏れ、import の順番、import 文はチーム開発で揉めがち...

レビュー時の import 漏れ指摘、import の順番、import の alias 設定、import 禁止箇所からの import 利用、未使用モジュールの import、etc...、import 文は問題の中心にいることが多いです。

これらの解決先として、ESLint や Prettier を用いた機械的なチェックが一般的かと思います。ESLint プラグイン・ルールでよく見かけるのは以下のようなものです。


私はprettier-plugin-organize-imports Prettier プラグインを利用し Prettier 側でフォーマットしています。本プラグインについての詳細は下記記事を参照ください。

https://zenn.dev/wakamsha/articles/prettier-plugin-organize-imports


複数人が参加するプロダクト開発では「どのライブラリを利用し、どのようなルールを運用するのか」が委ねられていることで逆に認識の相違による問題が容易に起こりえます。

Nuxt Auto-imports 機能が提供するのは「そもそも import 文を書かずに利用する」という新たな選択肢です。

未拡張の Auto-imports で可能なこと

ドキュメントのAuto-importsを確認してみます。未拡張の Auto-imports 機能で提供するのは下記の 2 つです。

  1. Built-in Auto-imports

    Vue, Nuxt composable が import 不要で利用できる機能です。Vue で利用するrefdefinePropscomputed、Nuxt で利用するuseFetchuseRouteuseHeadなどが import 不要で利用できます。

  2. Directory-based Auto-imports

    Directory-based Auto-imports 機能により下記 3 つのディレクトリが自動スキャンされます。

    • components/
    • composables/
    • utils/

このままでも十分良いが...

Nuxt 公式が提供する Auto-imports のおかげでかなり楽に開発を進めることができます。ありがたい限りです。

ただ、実際のプロダクト開発を行う場合は(汎用的な機能であるが故に)このままでは使い勝手が良くないケースもあります。そこで実際のプロダクト開発を想定して設定していきます。

事前情報

下記ではcomponents optionsimports optionsと分割しています。理由はそれぞれのオプションで出力されるファイルが異なるからです。

components Optionsで設定したコンポーネントは.nuxt/components.d.tsにエクスポートされ、imports Optionsで設定した内容は.nuxt/imports.d.tsにエクスポートされます。

そのため、誤って composable を.nuxt/components.d.tsにエクスポートした場合、Auto-imports は成功するが上手く動作しないという状況が発生します。


どのファイルにエクスポートされるかが非常に重要になってきますので頭の片隅に入れておいてもらえると助かります。Auto-imports 機能の構造については下記記事で深掘りしています、合わせてご覧ください。

https://zenn.dev/comm_vue_nuxt/articles/seeking-the-depths-of-nuxt-auto-imports-feature

components Options

Q. components/ をネストして利用したい

components ディレクトリがネストしている場合を考えます。例えば、以下のようなケースです。

| components/
--| ui/
----| Button.vue
----| Select/
------| index.vue
--| prod1/
----| AdminPage/
------| Table.vue
------| TableHeader.vue
--| comA.server.vue

components/uiディレクトリの切り方はshadcn-vueなどに見られる傾向です。プロダクトの場合は独自の共通コンポーネントを作成しているケースなどで見られるかと思います。

ただ、このままだとコンポーネント名が以下のようになります。Ui~の方は良いですが Prod1~の方は非常に見にくい状態になってます。

<UiButton />
<UiSelect />
<Prod1AdminPageTable />
<Prod1AdminPageTableHeader />


そこでpathPrefixprefixオプションを利用します。

export default defineNuxtConfig({
  components: [
    // eg: /ui/Select/index.vue -> <UiForm />
    {
      path: "components/ui",
      prefix: "Ui",
    },
    // eg: /components/prod1/**/Table.vue -> <Table />
    {
      path: "components/prod1",
      pathPrefix: false,
    },
  ],
});

Q. Auto-imports されるコンポーネントを限定したい

コンポーネントカタログとしてStorybookではなくHistoireを利用した場合、Histoire のファイルの拡張子*.story.vueのようになります。Histoire で利用するファイルはcomponents.d.tsに含めない方が好ましいと思います。

そこで、extensionsignoreオプションを用いて限定することが可能です。

export default defineNuxtConfig({
  components: [
    {
      path: "components/**",
      // case: *.stories.ts, types.ts などが同一ディレクトリに含まれる場合
      extensions: [".vue"],
      // case: Storybook ではなく Histoire を利用している場合
      ignore: ["**/*.story.vue"],
    },
  ],
});

Q. components/ ではなく features/ 直下にコンポーネントを配置したい

Nuxt が推奨するディレクトリ構造ではなく features ディレクトリ構造を用いるケースです。
チーム内で Next.js/React.js プロダクトを併用している場合は今までの慣れから採用されるケースが多いと思います。

この場合は以下のような設定になるかと思います。ほとんど前項と同じです。

export default defineNuxtConfig({
  components: [
    {
      path: "features/components/ui",
      extensions: [".vue"],
      ignore: ["**/*.story.vue"],
      prefix: "Ui",
    },
  ],
});

imports Options

Q. Auto Imports をオフにしたい

Auto-imports のメリットは import 内容を隠蔽できることです。しかし、現場によっては 「Auto-imports ではなく全ての import を明示したい」 という現場もあることでしょう。

チームの開発規模・人数が大きくレビュアーが全容を把握していない場合やコンポーネントの数が数百を超えており import を明示的にしないことが逆に開発を妨げ毒になりえるケースです。

ライブラリの移行作業と新規開発を並行で行なっている現場も削除予定のものを誤って import しないように明示したいとなるでしょう。その場合は以下の設定でオフにすることが可能です。

export default defineNuxtConfig({
  imports: {
    autoImport: false,
  },
});

Q. schemas/ や types/を Auto Import したい

zod や valibot でスキーマを定義している場合、型定義を types/に切り出しているケースです。
OpenAPI Generator を用いて IF・型定義を generate しているケースも該当すると思います。

この場合はdirsオプションを用いてディレクトリ単位で設定します。

export default defineNuxtConfig({
  imports: {
    dirs: ["schemas/**", "types/**", "{components,layouts}/**/types.ts"],
  },
});

zod や valibot を利用する場合はpresetsオプションを用いてそれ自体も Auto-imports すると便利です。

export default defineNuxtConfig({
  imports: {
    dirs: ["schemas/**", "types/**", "{components,layouts}/**/types.ts"],
    presets: [
      {
        from: "zod",
        imports: ["z", "string", "number", "object", "date"],
      },
    ],
  },
});

Q. Pinia でも Auto Import したい

Nuxt アプリケーション利用者であれば状態管理として Pinia を利用するケースは多いでしょう。この場合もimports.dirsオプションを利用すれば良いでしょうか?

Pinia 用の Nuxt module ではモジュール側で Auto-imports のオプションが提供されています。こちらを利用すれば問題ありません。

https://pinia.vuejs.org/ssr/nuxt.html#Auto-imports

export default defineNuxtConfig({
  modules: ["@pinia/nuxt"],

  // @pinia/nuxt@v0.5.0 以上
  pinia: {
    storesDirs: ["./stores/**"],
  },

  // @pinia/nuxt@v0.5.0 より前のバージョン
  imports: {
    dirs: ["stores"],
  },
  pinia: {
    autoImports: ["defineStore", "storeToRefs"],
  },
});

ただし、バージョンごとにオプションが異なるため下記の記事を参考にしてください。

Q. VeeValidate でも Auto Import したい

Pinia のようにモジュール側が提供しているケースとして VeeValidate があります。こちらもドキュメントの指示に従うだけで Auto-imports 設定が完了します。

https://vee-validate.logaretm.com/v4/integrations/nuxt/

export default defineNuxtConfig({
  modules: ["@vee-validate/nuxt"],
  veeValidate: {
    autoImport: true,
  },
});

Other case

Q. 特定ディレクトリ直下の components、types をそれぞれインポートしたい

ディレクトリ構成によっては以下のようなケースが考えられると思います。

| components/
--| ui/
----| Card/
------| index.vue
------| index.stories.ts
------| types.ts
------| recipe.ts

この場合は components、imports でそれぞれ設定する必要があります。

export default defineNuxtConfig({
  components: [
    {
      path: "components/**",
      extensions: [".vue"],
      ignore: ["**/*.story.vue"],
    },
  ],
  imports: {
    dirs: ["{components,layouts}/**/*.ts"],
  },
});

Q. consola、 h3 などの unjs stack を Auto Import して利用したい

Nuxt 内部では Logger のconsaola、minimal http FW のh3など多くの UnJS ライブラリが使われています。

この場合は zod、valibot 同様にimports.presetsを利用することで満たせます。

export default defineNuxtConfig({
  imports: {
    presets: [
      {
        from: "consola",
        imports: ["consola"],
      },
    ],
  },
});

また、unjs/unimportには Exports Auto Scan が可能です。これはローカルにインストールされたパッケージを自動でキャンする機能です。presets の import 対象を限定しない場合はこちらの方がシンプルです。

https://github.com/unjs/unimport#exports-auto-scan

余談: Exports Auto Scan は 下記イシューで Anthony Fu 氏に教えてもらいました。非常にありがたい 🙏

https://github.com/unjs/unimport/issues/313

おわりに

本記事は Nuxt Auto-imports 機能をプロダクト開発に当てはめて書いてみました。今回書いた内容はあくまで 1 例だと思います。

  • 「こんなケースでも利用できた」

  • 「こんなケースではどうすればいいのか」

などあればコメントまでどんどんお待ちしております 🙌

本記事が Auto-imports を使ってみるきっかけになれば嬉しいですし、ドキュメントと合わせた集合知になれば幸いです。

また、Nuxt Layer での利用ケース、monorepo 構成の場合については検証途中のため別記事で執筆できたらと思います。

関連

公式 Docs / GitHub

記事

https://zenn.dev/comm_vue_nuxt/articles/seeking-the-depths-of-nuxt-auto-imports-feature

https://zenn.dev/gangannikki/articles/nuxt3-extend-auto-imports

https://zenn.dev/gangannikki/articles/pinia-autoimports

https://zenn.dev/gangannikki/articles/pinia-stores-dirs-autoimports

Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion

あいや - aiya000あいや - aiya000

いつのまにかpathPrefixglobalプロパティになりましたかね?
こちらだと、pathPrefix: falseの代わりにglobal: trueを指定することで、うまくいきました
https://nuxt.com/docs/api/nuxt-config#components

また、componentsに指定していないコンポーネントもauto-importしたい場合は、最後に(最後に?)文字列として加えてあげる必要があるっぽそうな気がします

export default defineNuxtConfig({
  components: [
    // .prefixは削除された? undocumentedなだけ? こちらでは未調査
    // {
    //  path: "components/ui",
    //  prefix: "Ui",
    // },
    // eg: /components/prod1/**/Table.vue -> <Table />
    {
      path: "components/prod1",
      global: true, // `pathPrefix: false` と同様っぽい
    },
  ],
});

返信先↓

そこでpathPrefix、prefixオプションを利用します。

export default defineNuxtConfig({
  components: [
    // eg: /ui/Select/index.vue -> <UiForm />
    {
      path: "components/ui",
      prefix: "Ui",
    },
    // eg: /components/prod1/**/Table.vue -> <Table />
    {
      path: "components/prod1",
      pathPrefix: false,
    },
  ],
});