Nuxt 3 における dependencies と devDependencies の使い分け問題

この記事は Goodpatch Advent Calendar 2024 18日目の記事です。

Nuxtをどこにインストールするか悩んでいる人

はじめに

Goodpatch の運営する ReDesigner で、フロントエンドの開発を行っております、かずです。

npm や yarn 等を用いた パッケージ管理をしているプロジェクトにおいて、dependencies と devDependencies については、様々な運用方法があると思います。

本記事では、Nuxt 3 における dependencies と devDependencies の使い分けについてまとめます。

この記事が解決するかもしれない疑問

結局 Nuxt 3 を使うとき、パッケージは dependencies と devDependencies どっちにおけばいいの?

結論

どちらでもよい。あなたが管理しやすかったり、効率的だと思うほうを選択すればよい。 パッケージが node_modules にインストールされていれば正しく動作します。

何を当たり前のことを?

さて、この記事を見た方は、「え、何を当たり前のことを記事にしてるの?」と思うかもしれません。

「そんなもん迷いっこないだろ。dependencies に決まってるだろ」という方もいるでしょうし、 「いや、Nuxt は デフォルトで devDependencies だろ。そういう設計なんだから」という方もいるかもしれません。

実はちょっとめんどくさいことに、 Nuxt 公式でもこのあたりの方針が変化しており、Nuxt 3 に触れたタイミングによって認識が異なってしまう可能性があるのです。

以下、このあたりの背景を説明します。

dependencies と devDependencies の違い

まずは、dependencies と devDependencies の違いについて確認しておきます。

package.json において、dependencies と devDependencies はどちらも必要なパッケージの一覧です。 npm install を実行すればどちらも node_modules にインストールされます。

では、 dependencies と devDependencies の違いはなんでしょうか?

npm の公式では以下のような記述があります。

"dependencies": Packages required by your application in production. "devDependencies": Packages that are only needed for local development and testing.

"dependencies":本番環境でアプリケーションが動作するために必要なパッケージ "devDependencies":ローカル開発やテストのためにのみ必要なパッケージ

docs.npmjs.com

そして、npm や yarn には --production オプションがあり、以下のコマンドを実行することで package.json の dependencies のみをインストールすることができます。

# npm
$ npm install -- production

# yarn
$ yarn install --production
$ NODE_ENV=production yarn install

要するに、開発時にのみ必要なパッケージを実行環境に入れないようにするための機能なのです。

Nuxt 2 の依存関係指定と問題点

Nuxt 3の理解を深めるために、 Nuxt 2 について説明する必要があります。

Nuxt 2 ではビルドを実行すると、基本的にソースコードと同じディレクトリに出力されたバンドルが配置されます。そして、そのまま同じディレクトリで実行される仕様でした。 この仕様だと、package.json は 開発時、ビルド時、実行時で常に同じ物が使われることになります。

Nuxt 2のディレクトリ構造とpackage.json

そのため、デプロイ時に実行環境で不要な余計なパッケージをインストールしないように、dependencies と devDependencies をわけておく必要がありました。これは、package.json の定義と一致しています。

しかし、ビルド出力とソースコードが同一ディレクトリにまざり、実行環境に必要なファイルとそうでないファイルが混在してしまうという問題がありました。実行環境で本当に必要なファイルがわかりにくく、不要なデータを本番環境に持ち込んでしまうことも多かったと思います。

Nuxt 2 実行環境での要不要

Nuxt 3 での改善

Nuxt 3 ではビルド時に .output ディレクトリに、依存関係を含めた 実行に必要な全てのファイルを含めて出力するようになりました。実行環境専用の package.json が生成され、これを元に Node.js が起動されます。

Nuxt 3 のビルド出力と、実行環境に必要なファイル

シンプルでわかりやすく、バンドルサイズも最小限となり、デプロイの時間を減らすことができました。

Nuxt 3 に 本番環境に必要なパッケージ は存在しない

素晴しい改善ですが、ここで dependencies に関する疑問が発生します。

dependencies は 本番環境でアプリケーションが動作するために必要なパッケージ でした。

しかし前述の通り、 Nuxt 3では 全てデプロイ用のバンドルとしてビルドされるため、開発時に使用している package.json は使用されず、 本番環境で動作するために必要なパッケージ存在しない のです。

そのためか、初期の Nuxt公式は次のように謳っていました。

This means that Nuxt 3 and everything you use in your project are now devDependencies. Nitro will take care of code-splitting and bundling everything into a portable and compact .output directory ready to be deployed to any hosting provider .

github.com

また、プロジェクトテンプレート生成のコードでも以下のように devDependencies となっていました。

{
  "name": "nuxt-app",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "devDependencies": {
    "nuxt": "^3.9.0",
    "vue": "^3.3.13",
    "vue-router": "^4.2.5"
  }
}

github.com

このころに Nuxt 3 に触れた方は、「Nuxt 3 は devDependencies に入れるって方針なんだな」 と覚えたと思います。

最近の Nuxt 公式

しかし、昨今では Nuxt公式でこの方針が変ったようで、以下のように テンプレートが変更されています。

devDependencies から dependencies に書き換わっている

github.com

以前はあったはずの 「npm i -D でインストールします」みたいな記載もドキュメント等から消えているようです。

(どこかで方針に対する議論があったと思うのですが、私がその履歴を発見できていないです。誰か知ってたら教えてください)

結局どうすれば?

では、結局どうすればいいのでしょうか?

Nuxt コアチームメンバーからの回答は以下です。

It makes no difference - totally up to you.

それは何の違いもない - 完全にあなた次第です。 (ChatGPT翻訳)

github.com

It's more a flavor about how you want to split your dependencies as in the final bundle, only the used dependencies are traced and included. So all can be in dev dependencies.

I personally (and Daniel too) use dependencies to have the project dependencies and use devDependencies for everything related to development (testing, linting, typescript, bundling)

それは最終的なバンドルで使用される依存関係のみが追跡されて含まれるため、依存関係をどのように分割するかという好みの問題です。だから、すべてをdevDependenciesに入れることもできます。

個人的には(ダニエルも同様に)、プロジェクトの依存関係にはdependenciesを使用し、開発関連のすべて(テスト、リント、TypeScript、バンドル)にはdevDependenciesを使用します。 (ChatGPT翻訳)

github.com

つまり、結論にも書いたように、

どっちでもいい。あなたが管理しやすかったり、効率的だと思うほうを選択すればよい。 どこにおいてもパッケージが node_modules にインストールされていればビルドは成功するし、正しく動作します。

となります。

Nuxt 3における運用の選択肢: 3つの考え方

とはいえ、考え方のパターンはあるのでそちらをまとめておきます。

例示のため、以下のような状況を想定します。

  • ソースコードから、CI/CDを経由して、本番環境にデプロイしている
  • CI/CD 環境ではビルド前に単体テストやLintを実行している。
  • フォーマットはローカル環境でのみ実行している。

1. 実行環境に直接必要かどうかでわける

最終的にパッケージが直接実行環境で使われるか否かで考えます。初期のNuxt公式の考え方と同じ方法です。

Nuxt 3 の場合は基本的に全て devDependencies に入れることになります。

実行環境に必要かどうかで分ける

メリット

  • シンプル。考えなくていい。

デメリット

  • CI/CD 時に不要なパッケージがインストールされる。
  • ほとんどの場合 devDependencies しか使わないので、依存関係の分割機能を活用できない。
  • 各パッケージのインストール手順に単純に従うと、誤って dependencies に入ってしまうことがある。

2. 最終的なコードに影響があるかないかで分ける

実行環境で使われるコードを生成する nuxtvue と、Lint や フォーマッター、テストなどの直接影響しないパッケージで分離する方法です。

つまり、実行環境で直接ライブラリが利用されなくとも、最終的に実行環境のコードに影響するものと、そうでないものとを分けるということになります。

最終的なコードに影響があるかどうかで分ける

メリット

  • 最終的なコードに影響があるかないかで分離できる。
  • 定義に最も近いため、理解している人にとっては誤解がない。

デメリット

  • CI/CD 時に不要なパッケージがインストールされる。
  • どちらに配置するかの判断についてをチームで共通認識を持つ必要がある。

3. CI/CD 効率で分ける

CI/CD 環境で必要なもののみをインストールできるように dependencies に置くというパターンも考えられます。npm install --production で、必要最小限のパッケージだけインストールできます。

CD/CD 効率で分ける

メリット

  • CI/CDが効率化でき、実行時間が多少減る。

デメリット

  • どちらに配置するかの判断についてチームで共通認識を持つ必要がある。
  • どのパッケージがCIで利用されているかを管理する必要がある。

現在のチームでの選択

我々のチームの Nuxt 3 リポジトリでは、現状全て devDependencies で統一されています。

現状不自由や CI/CD パフォーマンスへの影響はないので、特に問題がない限りこのままにすると思います。

まとめ

Q: 結局 Nuxt 3 を使うとき、パッケージは dependencies と devDependencies どっちにおけばいいの?

A: どちらでもよい。あなたが管理しやすかったり、効率的だと思うほうを選択すればよい。 パッケージが node_modules にインストールされていれば正しく動作します。

最後に

パッケージマネージャやフレームワークの挙動を正しく理解し、各々にとって一番良い状態を考えていきましょう〜。

それでは👋

お約束

Goodpatchではデザイン好きなエンジニアの仲間を募集しています。 少しでもご興味を持たれた方は、ぜひ一度カジュアルにお話ししましょう!

蛇足

2024/12/17 現在、GPT-4o に尋ねたところ、回答は大枠正しいかったのですが Nuxt 3 に関してはハルシネーションがあったので、この記事には一定の価値があります。

chatgpt.com