viteでのアセット最適化2025

2025.11.28 09:00
2025.11.28 09:56
viteでのアセット最適化2025

静的サイトやWordPressテーマの開発で、画像の最適化は必須の作業です。しかし、手動で画像を圧縮するのは手間がかかりますし、ディレクトリ構造が複雑になると管理も大変になります。

今回は、Viteのビルドプロセスに組み込んで、画像を階層構造を保ったまま自動で最適化する方法を紹介します。

やりたいこと

  • ビルド時に画像を一括で最適化したい
  • 元の画像形式(JPEG/PNG/WebP/GIF/SVG)を保持したい
  • ディレクトリの階層構造を維持したい
  • assets/img_src/assets/img/ のように、プロジェクト内で完結させたい

必要なパッケージのインストール

まず、画像処理に必要なパッケージをインストールします。

npm install -D sharp glob
  • sharp: 高速な画像処理ライブラリ
  • glob: ファイルパターンマッチング用

Viteの設定

vite.config.js にカスタムプラグインを作成します。

import { defineConfig } from 'vite'
import path from 'path'
import fs from 'fs/promises'
import sharp from 'sharp'
import { glob } from 'glob'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const imageOptimizePlugin = () => {
  return {
    name: 'vite-image-optimize',
    async closeBundle() {
      console.log('\n📸 画像を最適化中...\n')
      
      // assets/img_src 配下の全画像を取得
      const images = await glob('assets/img_src/**/*.{jpg,jpeg,png,gif,webp,svg}', {
        nodir: true
      })
      
      console.log(`見つかった画像: ${images.length}件\n`)
      
      for (const imgPath of images) {
        try {
          // 相対パスを取得
          const relativePath = path.relative('assets/img_src', imgPath)
          // 出力先のパス
          const outputPath = path.join('assets/img', relativePath)
          const ext = path.extname(imgPath).toLowerCase()
          
          // 出力先ディレクトリを作成
          await fs.mkdir(path.dirname(outputPath), { recursive: true })
          
          // SVGはそのままコピー
          if (ext === '.svg') {
            await fs.copyFile(imgPath, outputPath)
            console.log(`✓ コピー: ${relativePath}`)
            continue
          }
          
          // sharpで最適化
          let pipeline = sharp(imgPath)
          
          switch (ext) {
            case '.jpg':
            case '.jpeg':
              pipeline = pipeline.jpeg({ 
                quality: 85,
                progressive: true 
              })
              break
            case '.png':
              pipeline = pipeline.png({ 
                quality: 85,
                compressionLevel: 9 
              })
              break
            case '.webp':
              pipeline = pipeline.webp({ 
                quality: 85 
              })
              break
            case '.gif':
              pipeline = pipeline.gif()
              break
          }
          
          await pipeline.toFile(outputPath)
          console.log(`✓ 最適化: ${relativePath}`)
          
        } catch (error) {
          console.error(`✗ エラー: ${imgPath}`, error.message)
        }
      }
      
      console.log('\n✨ 画像の最適化が完了しました\n')
    }
  }
}

export default defineConfig({
  plugins: [
    imageOptimizePlugin()
  ],
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: path.resolve(__dirname, 'assets/js/src/main.js'),
      output: {
        entryFileNames: 'js/[name].js',
        chunkFileNames: 'js/[name].js',
        assetFileNames: (assetInfo) => {
          if (assetInfo.name.endsWith('.css')) {
            return 'css/[name][extname]'
          }
          return 'assets/[name][extname]'
        }
      }
    }
  }
})

ディレクトリ構造

このプラグインを使うと、以下のように階層構造を保ったまま画像が最適化されます。

入力(最適化前)

assets/
└── img_src/
    ├── base/
    │   ├── hero.jpg
    │   └── logo.png
    ├── pages/
    │   ├── about.jpg
    │   └── contact.jpg
    └── icon/
        └── favicon.svg

出力(最適化後)

assets/
└── img/
    ├── base/
    │   ├── hero.jpg      ← 最適化済み
    │   └── logo.png      ← 最適化済み
    ├── pages/
    │   ├── about.jpg     ← 最適化済み
    │   └── contact.jpg   ← 最適化済み
    └── icon/
        └── favicon.svg   ← そのままコピー

ビルドの実行

npm run build

実行すると、以下のようなログが表示されます:

📸 画像を最適化中...

見つかった画像: 15件

✓ 最適化: base/hero.jpg
✓ 最適化: base/logo.png
✓ 最適化: pages/about.jpg
✓ 最適化: pages/contact.jpg
✓ コピー: icon/favicon.svg

✨ 画像の最適化が完了しました

カスタマイズ

画質の調整

ファイルサイズと画質のバランスを調整したい場合は、quality の値を変更します:

// より高品質(ファイルサイズ大)
quality: 90

// よりファイルサイズ重視
quality: 75

入力・出力パスの変更

プロジェクトの構成に合わせてパスを変更できます:

// 入力
const images = await glob('img_src/**/*.{jpg,jpeg,png,gif,webp,svg}', {
  nodir: true
})

// 出力
const outputPath = path.join('dist/img', relativePath)

補足:PostCSSの設定

CSS内で画像を参照する場合は、postcss.config.js も設定しておくと便利です:

export default {
  plugins: {
    autoprefixer: {},
    ...(process.env.NODE_ENV === 'production' ? {
      cssnano: {
        preset: ['default', {
          discardComments: {
            removeAll: true,
          },
        }]
      }
    } : {})
  }
}
npm install -D autoprefixer cssnano postcss

この処理の呼び方

この作業は以下のような用語で呼ばれます:

  • アセット最適化 (Asset Optimization) – 画像、CSS、JSなどのリソースを最適化すること
  • 画像最適化 / 画像圧縮 (Image Optimization / Image Compression) – 今回の具体的な処理内容
  • ビルドプロセス / ビルドパイプライン (Build Process / Build Pipeline) – ビルド時に実行される一連の処理
  • ビルドプラグイン (Build Plugin) – Viteのカスタム機能

まとめ

Viteのカスタムプラグインを使うことで、以下のことが実現できました:

  • ビルド時に画像を自動で最適化
  • 元の画像形式を保持
  • ディレクトリの階層構造を維持
  • プロジェクト内で完結する設定

この方法を使うことで、手動での画像圧縮作業から解放され、
開発に集中できるようになりました。

今回は以上です!