はじめに
Astro と Vite を使った開発で、ビルド時に生成されるファイルについて「なぜこんなにたくさんの JS ファイルが作られるの?」「ファイル名にくっついてるハッシュ値って何?」と疑問に思ったことはありませんか?
この記事では、そんな疑問を一緒に解決していきましょう!
以下について詳しく見ていきます。
- Astro が内部で使っている Vite とは何か
- ビルド時にどんなファイルが生成されるのか
- JS ファイルが複数作られる理由とその仕組み
- ファイル名のハッシュ値が果たしている役割
公式ドキュメントをベースに、分かりやすく解説していきますので、ぜひ最後まで読んでみてください。
目次
概要
Astro のビルドシステムってどんなもの?
まず、Astro がどうやってファイルをビルドしているのかを見てみましょう。Astro は内部で Vite というツールを使用していて、以下のような特徴があります。
- SSG with selective hydration(二段階ビルド): 静的コンテンツの生成と JavaScript バンドルの最適化
- コード分割: 自動的なコード分割による効率的な読み込み
- アセット最適化: 画像、CSS、JavaScript の最適化
- ハッシュベースキャッシュ: ファイル名にハッシュ値を付与したキャッシュ戦略
ビルド出力の構造
実際にビルドを実行すると、どのような構造でファイルが生成されるのでしょうか?以下は典型的なビルド出力の例です。
※ 実際のプロジェクトでは、設定やページ構成によって構造が異なります。また、ハッシュ値(a1b2c3d4
のような部分)は実際異なります。
ファイル/ディレクトリ | 種別 | 説明 |
---|---|---|
dist/index.html |
HTML | メインページ |
dist/about/index.html |
HTML | 静的ページ |
dist/_astro/client.a1b2c3d4.js |
JavaScript | クライアントサイド JS(ハッシュ付き) |
dist/_astro/page.e5f6g7h8.js |
JavaScript | ページ固有 JS |
dist/_astro/styles.i9j0k1l2.css |
CSS | 最適化された CSS |
dist/assets/hero.webp |
画像 | 最適化された画像 |
コード分割の仕組み
自動でコード分割してくれる仕組み
Astro の優れた機能の一つが、コードを自動的に分割してくれることです。
どういうことかというと、各ページ(.astro
ファイル)ごとに独立したチャンクを作成します。そして、React や Vue などのフレームワークライブラリは別のチャンクに分離してくれます。
さらに、import()
を使った動的読み込みの部分は、独立したチャンクとして分割されるので、必要なときにだけ読み込まれます。
---
// 動的インポートによる遅延読み込み
const { Chart } = await import('./Chart.jsx');
---
チャンク生成例
ファイル名 | 種別 | サイズ | 説明 |
---|---|---|---|
client.a1b2c3d4.js |
メインコード | 15KB | クライアントサイドの基本機能 |
react-vendor.e5f6g7h8.js |
ベンダー | 120KB | React 関連ライブラリ |
chart.i9j0k1l2.js |
動的インポート | 45KB | チャート機能(必要時に読み込み) |
page-about.m3n4o5p6.js |
ページ固有 | 8KB | about ページ専用コード |
ハッシュ値を使ったキャッシュ戦略について
なぜハッシュ値が生成されるの?
ファイル名についているa1b2c3d4
のような文字列、これがハッシュ値です。これはブラウザのキャッシュを効率的に活用するための仕組みです。ファイルの内容が変わったときだけハッシュ値が変わるので、変更されていないファイルはキャッシュから読み込まれ、変更されたファイルだけ新しく取得されます。
内部的には以下のような処理が行われています。
// Viteの内部的なハッシュ生成
function generateHash(content) {
const hash = crypto
.createHash("sha256")
.update(content)
.digest("hex")
.substring(0, 8);
return hash;
}
// ファイル内容が変更された場合のみハッシュが変更される
// 変更前: client.a1b2c3d4.js
// 変更後: client.x9y8z7w6.js
キャッシュ戦略の効果
ファイル種別 | キャッシュ期間 | 効果 |
---|---|---|
HTML | 短期(数分〜数時間) | 最新コンテンツの配信 |
JS/CSS(ハッシュ付き) | 長期(1 年) | 不要な再ダウンロード防止 |
画像・フォント | 中期(1 週間〜1 ヶ月) | バランスの取れたキャッシュ |
実際のビルド例
仮に以下1.プロジェクト構成だった場合、ビルドされると2のようなファイルが生成されます。_astro/
のファイルには、ハッシュ値が付与されているのも確認できるかと思います。
styles/global.css
にCSSスタイルを集約している場合は、以下2のような生成結果になりますが、各 .astro ファイル内に scoped でスタイルを記載している場合や Vite の設定で CSS が分割設定されている場合は、複数の .css ファイルが生成されることもあります。
また、.jsファイルに関しては、基本的に標準ではバンドルすることはできず、エントリファイル(src/
でJavaScriptを使用している箇所)や Astro プロジェクト内で使用している React・Svelte などの他フレームワークに応じて対応した .js ファイルが生成される仕組みです。
1. プロジェクト構成
src/
├── pages/
│ ├── index.astro # トップページ
│ ├── about.astro # About ページ
│ └── blog/
│ └── [slug].astro # 動的ルーティング
├── components/
│ ├── Header.astro
│ └── Chart.jsx # React コンポーネント
└── styles/
└── global.css
2. ビルド実行とファイル生成
$ npm run build
# ビルド過程の出力(簡略化)
14:32:18 [build] Building for production...
14:32:19 [build] ✓ Completed in 847ms.
dist/
├── index.html # 2.3 KB
├── about/index.html # 1.8 KB
├── blog/
│ ├── post-1/index.html # 3.1 KB
│ └── post-2/index.html # 2.9 KB
└── _astro/
├── client.DwN2Y8tU.js # 12.4 KB (gzipped: 4.2 KB)
├── react.BxM8K3Ls.js # 42.1 KB (gzipped: 13.8 KB)
├── chart.A7sF9dG2.js # 15.6 KB (gzipped: 5.1 KB)
├── header.C4nR7wQ9.js # 3.2 KB (gzipped: 1.1 KB)
└── styles.E8hY2nK7.css # 4.7 KB (gzipped: 1.8 KB)
ビルドプロセスの詳細を深掘り
Tree Shaking という概念
ツリーシェイキング(tree shaking)とは、ビルド時に使われていないコード(未参照のエクスポートや副作用のない処理など)を自動で削除して、バンドルを小さくする最適化フローのことです。ビルド時に作動します。
// calculate.js
export const add = (a, b) => a + b; // ← 使う
export const mul = (a, b) => a * b; // ← 使わないので削除対象
export const sub = (a, b) => a - b; // ← 使わないので削除対象
// index.js
console.log(add(1, 2));
astro.config.mjs の設定項目
これまでのハッシュ値やコード分割など、バンドル・ビルドの設定は、astro.config.mjs で行えます。以下見ていただければ分かる通り、Vite 内部では、さまざまなツールが組み合わさって、最適なバンドル・ビルドを提供しています。その為、各ツールの設定インターフェースが提供されています。
export default defineConfig({
vite: {
// esbuild設定の例
esbuild: {
drop: ["console", "debugger"], // console.logを削除
legalComments: "none", // コメント削除
},
build: {
cssCodeSplit: false,
treeShaking: false,
// Rollup設定の例
rollupOptions: {
external: ["some-large-lib"], // 外部化
},
},
},
});
ビルドエラーの基本的な対処
モジュール解決エラー
# エラー例: "Cannot resolve module"
Error: Cannot resolve module 'some-package'
解決法
package.json
の依存関係を確認npm install
またはnpm ci
を実行- TypeScript の場合は
tsconfig.json
のmoduleResolution
設定を確認
おわりに
いかがでしたか?Astro と Vite のビルドシステムについて、少し理解が深まったでしょうか。
最初は「なぜこんなにファイルがたくさん作られるの?」という疑問から始まったかもしれませんが、実はそれぞれにちゃんと意味があることが分かったのではないでしょうか。
二段階ビルドやコード分割、ハッシュベースキャッシュなど、これらの仕組みを理解することで、より効率的な Web アプリケーション開発ができるようになります。
開発時は高速な HMR でサクサク作業でき、本番では最適化されたファイルで高速な配信ができる。そんな理想的な開発環境を、Astro と Vite が実現してくれています。HMR 様様です。大感謝。。。
読んでいただきありがとうございました。本記事が少しでも参考になれば幸いです。
参考記事
- Getting Started | Docs – Astro の基本的な使い方とセットアップ方法
- 設定リファレンス – astro.config.mjs の詳細な設定オプション
- ビルドとデプロイ – 開発からデプロイまでの流れ
- Integration API – Astro インテグレーションの開発方法
- ビルドオプション – Vite のビルド設定の詳細
- パフォーマンス最適化 – パフォーマンス問題の特定と修正方法
- 本番環境ビルド – 本番用ビルドの作成方法
- 依存関係の最適化 – 依存関係の事前バンドル設定
- 設定オプション – Rollup の詳細な設定オプション
- よくある質問 – Tree Shaking やバンドル最適化に関する FAQ