Advanced

Optimisation des images avec Claude Code

Découvrez optimisation des images avec Claude Code. Conseils pratiques et exemples de code inclus.

画像最適化パイプラインで表示速度を劇的に改善

画像はWebページの転送量の大部分を占めます。Claude Codeを使って、自動的に画像を最適化するパイプラインを構築すれば、手作業の負担なくパフォーマンスを改善できます。

画像変換スクリプトの構築

> sharpを使った画像最適化スクリプトを作成して。
> WebP・AVIF変換、リサイズ、メタデータ削除を自動化して。
// scripts/optimize-images.ts
import sharp from 'sharp';
import { globSync } from 'glob';
import { mkdirSync, existsSync } from 'fs';
import path from 'path';

interface ImageConfig {
  widths: number[];
  formats: ('webp' | 'avif' | 'jpg')[];
  quality: Record<string, number>;
}

const config: ImageConfig = {
  widths: [320, 640, 960, 1280, 1920],
  formats: ['avif', 'webp', 'jpg'],
  quality: { avif: 50, webp: 75, jpg: 80 },
};

async function optimizeImage(inputPath: string, outputDir: string) {
  const name = path.basename(inputPath, path.extname(inputPath));
  
  for (const format of config.formats) {
    for (const width of config.widths) {
      const outputPath = path.join(outputDir, `${name}-${width}w.${format}`);
      
      await sharp(inputPath)
        .resize(width, undefined, { withoutEnlargement: true })
        .toFormat(format, { quality: config.quality[format] })
        .toFile(outputPath);
    }
  }
}

async function main() {
  const images = globSync('src/assets/images/**/*.{jpg,jpeg,png}');
  const outputDir = 'public/images/optimized';
  
  if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });

  for (const img of images) {
    console.log(`最適化中: ${img}`);
    await optimizeImage(img, outputDir);
  }
  
  console.log(`✅ ${images.length}枚の画像を最適化しました`);
}

main();

レスポンシブ画像コンポーネント

// components/OptimizedImage.tsx
interface OptimizedImageProps {
  src: string;
  alt: string;
  widths?: number[];
  sizes?: string;
  className?: string;
  loading?: 'lazy' | 'eager';
  priority?: boolean;
}

export function OptimizedImage({
  src,
  alt,
  widths = [320, 640, 960, 1280],
  sizes = '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw',
  className,
  loading = 'lazy',
  priority = false,
}: OptimizedImageProps) {
  const baseName = src.replace(/\.(jpg|jpeg|png)$/, '');

  const avifSrcSet = widths
    .map(w => `/images/optimized/${baseName}-${w}w.avif ${w}w`)
    .join(', ');

  const webpSrcSet = widths
    .map(w => `/images/optimized/${baseName}-${w}w.webp ${w}w`)
    .join(', ');

  const jpgSrcSet = widths
    .map(w => `/images/optimized/${baseName}-${w}w.jpg ${w}w`)
    .join(', ');

  return (
    <picture>
      <source type="image/avif" srcSet={avifSrcSet} sizes={sizes} />
      <source type="image/webp" srcSet={webpSrcSet} sizes={sizes} />
      <img
        src={`/images/optimized/${baseName}-960w.jpg`}
        srcSet={jpgSrcSet}
        sizes={sizes}
        alt={alt}
        loading={priority ? 'eager' : loading}
        decoding={priority ? 'sync' : 'async'}
        fetchPriority={priority ? 'high' : undefined}
        className={className}
      />
    </picture>
  );
}

遅延読み込みとプレースホルダー

// ぼかしプレースホルダー付き遅延読み込み
function BlurImage({ src, alt, blurDataUrl }: {
  src: string;
  alt: string;
  blurDataUrl: string;
}) {
  const [loaded, setLoaded] = useState(false);

  return (
    <div className="relative overflow-hidden">
      {/* ぼかしプレースホルダー */}
      <img
        src={blurDataUrl}
        alt=""
        aria-hidden="true"
        className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-300
          ${loaded ? 'opacity-0' : 'opacity-100'}`}
        style={{ filter: 'blur(20px)', transform: 'scale(1.1)' }}
      />
      {/* 本画像 */}
      <img
        src={src}
        alt={alt}
        loading="lazy"
        onLoad={() => setLoaded(true)}
        className={`h-full w-full object-cover transition-opacity duration-300
          ${loaded ? 'opacity-100' : 'opacity-0'}`}
      />
    </div>
  );
}

ぼかしプレースホルダーの生成

// scripts/generate-blur.ts
import sharp from 'sharp';

async function generateBlurDataUrl(imagePath: string): Promise<string> {
  const buffer = await sharp(imagePath)
    .resize(10, 10, { fit: 'inside' })
    .blur()
    .toBuffer();
  
  return `data:image/jpeg;base64,${buffer.toString('base64')}`;
}

ビルドパイプラインへの組み込み

{
  "scripts": {
    "images": "tsx scripts/optimize-images.ts",
    "images:blur": "tsx scripts/generate-blur.ts",
    "prebuild": "npm run images && npm run images:blur",
    "build": "astro build"
  }
}

Summary

画像最適化はフォント最適化と並んで、表示速度改善に最も効果的な施策です。Claude Codeを使えば、sharp を使った変換スクリプトからレスポンシブ画像コンポーネントまで一貫して構築できます。バンドル分析と組み合わせて、総合的なパフォーマンス改善を進めましょう。画像最適化の詳細はweb.dev Images Guideを参照してください。

#Claude Code #image optimization #WebP #AVIF #performance