web-performance-optimization について
Web 開発者向けのパフォーマンス最適化スキルです。読み込み、キャッシュ、圧縮、監視の手法を扱い、Core Web Vitals とユーザー体験の改善に役立ちます。
主な機能
- 01ルートのコード分割と遅延読み込み
- 02画像の最適化とキャッシュ
- 03gzip と Brotli による圧縮
- 04Core Web Vitals の監視
- 05パフォーマンス指標の目標
活用シーン
- 01ページ読み込みの遅さの調査
- 02大きなフロントエンドバンドルの縮小
- 03モバイル性能の改善
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill web-performance-optimizationインストール後、Claude Code で/web-performance-optimizationを入力して読み込まれたか確認します。
同梱ファイルreferences/compression-monitoring.mdreferences/typescript-advanced.md
詳細
Web パフォーマンス最適化
概要
遅延読み込み、コード分割、キャッシュ、圧縮、モニタリングなどのパフォーマンス最適化戦略を実装して、Core Web Vitals とユーザーエクスペリエンスを改善します。
使用する場面
- ページの読み込みに時間がかかる
- Largest Contentful Paint(LCP)の値が高い
- バンドルサイズが大きい
- Cumulative Layout Shift(CLS)が頻繁に発生する
- モバイルでパフォーマンスの問題がある
コード分割(React)
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
Webpack バンドルの最適化
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
画像の最適化
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img
src="image.jpg"
srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
loading="lazy"
decoding="async"
alt="Description"
>
</picture>
Service Worker キャッシュ
// sw.js
const CACHE_NAME = 'app-v1';
const ASSETS = ['/', '/index.html', '/main.js', '/styles.css'];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request).then(response => {
return caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
Core Web Vitals のモニタリング
// Track LCP, CLS, INP (Note: INP replaced FID as of March 2024)
// sendToAnalytics is a placeholder function that sends metrics to your analytics endpoint
// Expected signature: sendToAnalytics({ metric: string, value: number }) => void
// Example implementation:
function sendToAnalytics({ metric, value }) {
// Replace with your analytics implementation (e.g., Google Analytics, Segment)
fetch('/api/analytics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ metric, value, timestamp: Date.now() })
});
}
// Largest Contentful Paint (LCP)
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`LCP: ${entry.startTime}ms`);
sendToAnalytics({ metric: 'LCP', value: entry.startTime });
}
}).observe({ type: 'largest-contentful-paint', buffered: true });
// Cumulative Layout Shift (CLS)
new PerformanceObserver((list) => {
let cls = 0;
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) cls += entry.value;
}
sendToAnalytics({ metric: 'CLS', value: cls });
}).observe({ type: 'layout-shift', buffered: true });
// Interaction to Next Paint (INP) - replaces FID
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// INP measures responsiveness - duration of slowest interaction
const inp = entry.processingEnd - entry.processingStart;
console.log(`INP: ${inp}ms`);
sendToAnalytics({ metric: 'INP', value: inp });
}
}).observe({ type: 'event', buffered: true }); // 'event' captures interaction events
パフォーマンス目標
| 指標 | 良好 | 改善が必要 |
|---|---|---|
| LCP | <2.5s | 2.5-4s |
| INP | <200ms | 200-500ms |
| CLS | <0.1 | 0.1-0.25 |
| TTI | <3.8s | 3.8-7.3s |
注: INP(Interaction to Next Paint)は、2024年3月にCore Web VitalとしてFID(First Input Delay)に置き換わりました。INPは、入力遅延だけでなく操作にかかる時間全体を測定するため、ページの応答性をより包括的に評価できます。
圧縮(Nginx)
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
gzip_comp_level 6;
ベストプラクティス
- コード分割でバンドルサイズを最小限に抑える
- 適切な形式を使用して画像を最適化する
- 戦略的に遅延読み込みを実装する
- HTTP キャッシュヘッダーを使用する
- gzip/brotli 圧縮を有効にする
- Core Web Vitals を継続的にモニタリングする
- service worker を実装する
- 重要度の低い JavaScript の読み込みを遅らせる
- クリティカルレンダリングパスを最適化する
- 実際のデバイスとネットワークでテストする
最適化チェックリスト
- ルートごとにコード分割を有効にする
- ファーストビューより下のコンポーネントを遅延読み込みする
- 画像を最適化して圧縮する
- service worker によるキャッシュを実装する
- gzip/brotli 圧縮を有効にする
- Core Web Vitals をモニタリングする
- レンダリングをブロックするリソースを最小限に抑える
追加設定
以下については、references/compression-monitoring.md を参照してください。
- Webpack 圧縮プラグインのセットアップ
- Apache .htaccess 圧縮設定
- TTFB モニタリングの実装
- 測定のための Puppeteer 自動化
以下については、references/typescript-advanced.md を参照してください。
- TypeScript の lazyLoad ユーティリティ
- TypeScript の画像コンポーネント
- オフライン時のフォールバックに対応した高度な service worker
- TerserPlugin の設定
- PerformanceMetrics インターフェースの全体
ツール
- Lighthouse / PageSpeed Insights
- WebPageTest
- Chrome DevTools の Performance タブ
- web-vitals npm パッケージ
リソース
コメント
Markdown 対応