-
Notifications
You must be signed in to change notification settings - Fork 0
Build System
Makoto Horikawa edited this page Jul 9, 2025
·
1 revision
GitHub Power Scouterのビルドシステム(Vite)の詳細解説です。初心者からベテランまで、ビルドシステムの理解と最適化について包括的に説明します。
flowchart TD
A[Source Code] --> B{Development Mode?}
B -->|Yes| C[Vite Dev Server]
B -->|No| D[Production Build]
C --> E[ES Modules]
C --> F[HMR]
C --> G[Fast Refresh]
D --> H[Bundle Analysis]
D --> I[Code Splitting]
D --> J[Asset Optimization]
H --> K[Rollup]
I --> K
J --> K
K --> L[Optimized Assets]
L --> M[Static Files]
| 特徴 | Webpack | Parcel | Vite | 優位性 |
|---|---|---|---|---|
| 開発サーバー起動 | 10-30秒 | 5-15秒 | 0.5-2秒 | ✅ 10-50倍高速 |
| HMR | 1-5秒 | 1-3秒 | 0.1-0.5秒 | ✅ 瞬時更新 |
| 学習コスト | 高 | 中 | 低 | ✅ 簡単設定 |
| エコシステム | 最大 | 中 | 成長中 |
graph LR
subgraph "Development"
A[Source Files] --> B[esbuild Transform]
B --> C[ES Modules]
C --> D[Browser]
end
subgraph "Production"
E[Source Files] --> F[Rollup Bundle]
F --> G[Optimized Bundle]
G --> H[Static Assets]
end
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
export default defineConfig({
// プラグイン設定
plugins: [
react({
// React Fast Refresh設定
fastRefresh: true,
// JSX運用ランタイム設定
jsxRuntime: 'automatic',
}),
],
// 開発サーバー設定
server: {
port: 5173,
host: true, // ネットワークアクセス許可
open: true, // ブラウザ自動オープン
cors: true, // CORS設定
proxy: {
// APIプロキシ設定(開発時のCORS回避)
'/api': {
target: 'https://api.github.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// ビルド設定
build: {
// 出力ディレクトリ
outDir: 'dist',
// アセットディレクトリ
assetsDir: 'assets',
// ソースマップ生成
sourcemap: process.env.NODE_ENV !== 'production',
// minify設定
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // console.log削除
drop_debugger: true, // debugger文削除
},
},
// チャンク分割設定
rollupOptions: {
output: {
manualChunks: {
// ベンダーチャンク
vendor: ['react', 'react-dom'],
// ユーティリティチャンク
utils: ['./src/utils/index.ts'],
},
// アセット命名規則
assetFileNames: (assetInfo) => {
const info = assetInfo.name.split('.')
const ext = info[info.length - 1]
// 画像ファイル
if (/\.(png|jpe?g|svg|gif|tiff|bmp|ico)$/i.test(assetInfo.name)) {
return `images/[name]-[hash][extname]`
}
// フォントファイル
if (/\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name)) {
return `fonts/[name]-[hash][extname]`
}
return `${ext}/[name]-[hash][extname]`
},
// JSファイル命名規則
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
},
},
// ファイルサイズ警告しきい値
chunkSizeWarningLimit: 1000, // 1MB
},
// ベースパス設定(デプロイ先に応じて変更)
base: process.env.NODE_ENV === 'production'
? '/programming-skill-scouter/' // GitHub Pages
: './',
// 解決設定
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@components': resolve(__dirname, 'src/components'),
'@types': resolve(__dirname, 'src/types'),
'@utils': resolve(__dirname, 'src/utils'),
},
},
// CSS設定
css: {
modules: {
localsConvention: 'camelCase',
},
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
},
// 環境変数設定
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
},
}){
"scripts": {
// 開発サーバー起動
"dev": "vite",
"dev:host": "vite --host", // ネットワークアクセス有効
"dev:debug": "vite --debug", // デバッグモード
// ビルド関連
"build": "tsc -b && vite build",
"build:analyze": "npm run build && npx vite-bundle-analyzer dist",
"build:preview": "npm run build && npm run preview",
// 型チェック
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch",
// リント・フォーマット
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext ts,tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
// プレビュー
"preview": "vite preview",
"preview:host": "vite preview --host",
// デプロイ
"predeploy": "npm run build",
"deploy": "gh-pages -d dist",
// クリーンアップ
"clean": "rm -rf dist node_modules/.vite",
"fresh": "npm run clean && npm install"
}
}# 開発環境での作業フロー
npm run dev # 開発サーバー起動
npm run type-check # 型チェック
npm run lint # リント実行
npm run format # コードフォーマット
# ビルド・デプロイフロー
npm run build # プロダクションビルド
npm run build:analyze # バンドル分析
npm run preview # ビルド結果プレビュー
npm run deploy # デプロイgraph TD
A[Application Bundle] --> B[Entry Chunk]
A --> C[Vendor Chunk]
A --> D[Utils Chunk]
A --> E[Dynamic Chunks]
B --> F[App.tsx]
B --> G[main.tsx]
C --> H[react]
C --> I[react-dom]
D --> J[API utilities]
D --> K[Helper functions]
E --> L[Lazy Components]
E --> M[Route Components]
// コンポーネントの遅延読み込み
import { lazy, Suspense } from 'react'
// 動的インポート
const ResumeModal = lazy(() => import('./ResumeModal'))
const HeavyComponent = lazy(() =>
import('./HeavyComponent').then(module => ({
default: module.HeavyComponent
}))
)
// 使用例
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<ResumeModal />
</Suspense>
)
}// ❌ 悪い例: ライブラリ全体をインポート
import * as utils from 'lodash'
// ✅ 良い例: 必要な関数のみインポート
import { debounce, throttle } from 'lodash'
// ✅ さらに良い例: 個別モジュールインポート
import debounce from 'lodash/debounce'
import throttle from 'lodash/throttle'# vite-bundle-analyzerでの分析
npm install --save-dev vite-bundle-analyzer
npx vite-bundle-analyzer dist
# rollup-plugin-visualizerでの分析
npm install --save-dev rollup-plugin-visualizer
npm run buildpie title Bundle Size Distribution
"React & ReactDOM" : 45
"Application Code" : 30
"Third-party Libraries" : 15
"CSS & Assets" : 10
| メトリクス | 目標値 | 現在値 | 状態 |
|---|---|---|---|
| Total Bundle Size | < 1MB | ~220KB | ✅ |
| Initial JS | < 500KB | ~205KB | ✅ |
| CSS | < 100KB | ~7KB | ✅ |
| Vendor Chunk | < 200KB | ~12KB | ✅ |
| Gzip Compression | < 70% | ~65% | ✅ |
// HMRのカスタマイズ
if (import.meta.hot) {
// モジュール受け入れ設定
import.meta.hot.accept()
// データ保持設定
import.meta.hot.accept(['./UserData'], (newModule) => {
// 状態を保持しながらモジュール更新
})
// 破棄時のクリーンアップ
import.meta.hot.dispose(() => {
// クリーンアップ処理
})
}// 開発時のみ有効なデバッグ機能
if (import.meta.env.DEV) {
// React DevTools
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.__REACT_DEVTOOLS_GLOBAL_HOOK__ || {};
// パフォーマンス測定
console.time('Component Render')
// API レスポンス時間測定
const startTime = performance.now()
}// vite.config.ts での画像最適化
import { defineConfig } from 'vite'
export default defineConfig({
build: {
rollupOptions: {
external: [
// 外部依存として扱うライブラリ
],
output: {
// 画像の最適化
assetFileNames: (assetInfo) => {
if (/\.(png|jpe?g|svg|gif)$/i.test(assetInfo.name)) {
return 'images/[name]-[hash][extname]'
}
return 'assets/[name]-[hash][extname]'
}
}
}
},
// アセット処理設定
assetsInclude: ['**/*.md'], // Markdownファイルをアセットとして処理
})// Gzip/Brotli圧縮
import { defineConfig } from 'vite'
import { compression } from 'vite-plugin-compression'
export default defineConfig({
plugins: [
compression({
algorithm: 'gzip',
threshold: 1024, // 1KB以上のファイルを圧縮
}),
compression({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 1024,
}),
],
})// plugins/github-cache.ts
import type { Plugin } from 'vite'
export function githubCachePlugin(): Plugin {
return {
name: 'github-cache',
configureServer(server) {
server.middlewares.use('/api/github', (req, res, next) => {
// GitHub APIレスポンスのキャッシュ処理
const cacheKey = req.url
// キャッシュから取得を試行
const cached = getCache(cacheKey)
if (cached) {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(cached))
return
}
next()
})
},
}
}// plugins/build-info.ts
import type { Plugin } from 'vite'
export function buildInfoPlugin(): Plugin {
return {
name: 'build-info',
generateBundle() {
// ビルド情報をJSONファイルとして出力
const buildInfo = {
timestamp: new Date().toISOString(),
version: process.env.npm_package_version,
environment: process.env.NODE_ENV,
}
this.emitFile({
type: 'asset',
fileName: 'build-info.json',
source: JSON.stringify(buildInfo, null, 2),
})
},
}
}// plugins/build-timer.ts
import type { Plugin } from 'vite'
export function buildTimerPlugin(): Plugin {
let startTime: number
return {
name: 'build-timer',
buildStart() {
startTime = Date.now()
console.log('🚀 Build started...')
},
generateBundle() {
const duration = Date.now() - startTime
console.log(`✅ Build completed in ${duration}ms`)
},
}
}# size-limit での監視
npm install --save-dev size-limit @size-limit/preset-big-lib
# package.json
{
"size-limit": [
{
"path": "dist/assets/*.js",
"limit": "500 KB"
},
{
"path": "dist/assets/*.css",
"limit": "50 KB"
}
]
}# 並列処理の有効化
export VITE_BUILD_THREADS=$(nproc)
# キャッシュのクリア
rm -rf node_modules/.vite
npm run build# Node.jsのメモリ制限を増加
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build// 正しい相対パス設定
export default defineConfig({
base: process.env.NODE_ENV === 'production'
? '/your-repo-name/'
: './',
})# .github/workflows/build.yml
name: Build and Test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Build
run: npm run build
- name: Size check
run: npx size-limit
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-files
path: dist/timeline
title Build Tool Evolution
Current : Vite 7.0
: esbuild
: Rollup
Q2 2024 : Vite 8.0
: Rollup 5.0
: Lightning CSS
Q4 2024 : Native ESM
: Import Maps
: Web Assembly
2025 : Rust-based tools
: Even faster builds
: Zero-config setup
次のステップ: リント・フォーマットガイドでコード品質管理を学びましょう。