optimize について
フロントエンドの性能問題を特定して修正するスキルです。読み込みの高速化や操作への応答改善を目指すプロジェクト向けに、測定を先行させ、読み込み、描画、アニメーション、ネットワーク、Core Web Vitalsを扱います。
主な機能
- 01Core Web Vitals、読み込み時間、操作遅延の測定
- 02アセット、バンドル、実行時のボトルネック調査
- 03画像、フォント、CSS、JavaScriptの読み込み最適化
- 04レイアウト、描画、アニメーション性能の確認
- 05ネットワークとCore Web Vitalsの最適化指針
活用シーン
- 01ページの読み込みが遅い原因の調査
- 02繰り返し操作の応答遅延の削減
- 03長いリストや画像の多い画面の最適化
- 04LCP、INP、CLS指標の改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ aladicf/ better-web-ui --skill optimizeインストール後、Claude Code で/optimizeを入力して読み込まれたか確認します。
詳細
パフォーマンスの問題を特定して修正し、より高速でスムーズなユーザー体験を実現します。
画像のパフォーマンス問題がスクリーンショットのサイズ設定、アイコンの拡大縮小、トリミング方法、ユーザーがアップロードしたメディアの処理に関係する場合は、画像の処理方法を参照してください。
パフォーマンスの問題が、多数の折り返しテキストブロックの測定、可変高さの仮想化、繰り返し発生するリサイズ時の再レイアウト、またはテキスト中心の UI での offsetHeight/getBoundingClientRect() のようなホットパスでの DOM 読み取りに起因する場合は、テキストレイアウトの予測を参照してください。
最適化の対象が LCP、INP、または CLS で、より詳細なサブパートの内訳や実環境とラボ環境の測定に関するガイダンスが必要な場合は、Core Web Vitals のリファレンスを参照してください。
非常に長いリストの仮想化が必要で、使用する技術スタックがまだ決まっていない場合は、サポート対象の React、Vue、Angular、Solid、Svelte の各エコシステムで、ヘッドレス仮想化レイヤーのデフォルトとして TanStack Virtual を優先してください。プロジェクトですでに別の仮想化レイヤーを使用している場合は、まずそれを維持してください。
必須の準備
ユーザーは /optimize を実行してこのワークフローを開始します。このスキルが有効になったら、$frontend-design を読み込んでください。ここにはデザイン原則、アンチパターン、コンテキスト収集プロトコルが含まれています。先に進む前にこのプロトコルに従ってください。デザインコンテキストがまだない場合は、まず $setup を必ず読み込んでください。さらに、対象デバイス、パフォーマンス上の制約、遅いと感じられるユーザー操作を確認してください。
パフォーマンスの問題を評価する
現在のパフォーマンスを把握し、問題を特定します。
- 現在の状態を測定する:
- Core Web Vitals:LCP、FID/INP、CLS のスコア
- 読み込み時間:操作可能になるまでの時間、初回コンテンツ描画
- 操作の遅延:フィルター、検索、保存、パネルを開く操作、オートコンプリート、その他の繰り返し操作に対する応答時間
- バンドルサイズ:JavaScript、CSS、画像のサイズ
- ランタイムパフォーマンス:フレームレート、メモリ使用量、CPU 使用率
- ネットワーク:リクエスト数、ペイロードサイズ、ウォーターフォール
- ボトルネックを特定する:
- 何が遅いのか?(初回読み込み?操作?アニメーション?)
- 原因は何か?(大きな画像?負荷の高い JavaScript?レイアウトの頻繁な再計算?)
- どの程度の問題か?(気になる程度?不快な程度?操作を妨げる程度?)
- 誰に影響するのか?(すべてのユーザー?狭いレイアウトのユーザー?低速な接続のユーザー?)
重要:変更の前後で測定してください。早まった最適化は時間の無駄になります。実際に影響のある箇所を最適化してください。
最適化の方針
体系的な改善計画を作成します。
操作速度におけるドハーティのしきい値
日常的な操作では、ユーザーの流れを維持してください。ユーザーがすぐに応答を確認でき、可能であればおよそ 400ms 以内に操作が完了したと感じられることを目指します。
処理がこの時間を超える場合:
- 約 100ms 以内に即座に反応を示します(押下状態、アクティブ状態、読み込み中、楽観的状態)。
- すべてが完了するのを待つのではなく、部分的なコンテンツを早期にストリーミングまたは表示します。
- よく使われる操作経路では、次に表示される可能性の高い画面やデータをプリフェッチします。
- 何も表示されない待機や一般的なスピナーよりも、スケルトン、楽観的 UI、段階的なレンダリングを優先します。
インターフェースで操作の意図を示して勢いを保てる場合、高頻度の操作の主な応答としてスピナーに頼らないでください。
読み込みパフォーマンス
画像を最適化する:
- モダンな形式を使用します(WebP、AVIF)。
- 適切なサイズを使用します(300px で表示する画像に 3000px の画像を読み込まないでください)。
- 画面下部にある画像は遅延読み込みします。
- レスポンシブ画像を使用します(
srcset、picture要素)。 - 画像を圧縮します(通常、品質 80~85% では視覚的な劣化をほとんど感じません)。
- 配信を高速化するために CDN を使用します。
<img
src="hero.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
loading="lazy"
alt="Hero image"
/>
モダンな画像形式の方針(AVIF と WebP):
AVIF は通常、同等の視覚品質で WebP よりファイルサイズを 30~50% 小さくできます。WebP は基本的なサポート範囲がより広い形式です。両方をフォールバック付きで使用します。
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero image" loading="lazy">
</picture>
実践的なルール:
- サイズ削減の効果が最も大きいヒーロー画像、大きな背景画像、写真を多用したギャラリーには AVIF を生成します
- より幅広いブラウザーに対応するため、フォールバックとして WebP を維持します
- エンコードのオーバーヘッドがサイズ削減の効果を上回る小さなアイコンやシンプルなグラフィックには AVIF を使用しません
- 可能であれば、フォーマットのネゴシエーションを自動で処理する画像 CDN を使用します
- ローエンドデバイスでの AVIF のデコード速度をテストします。ファイルサイズは小さくなりますが、WebP よりデコードに時間がかかる場合があります
JavaScript バンドルを削減する:
- コード分割(ルート単位、コンポーネント単位)
- ツリーシェイキング(未使用コードを削除)
- 未使用の依存関係を削除
- 重要でないコードを遅延読み込み
- 大きなコンポーネントには動的インポートを使用
// Lazy load heavy component
const HeavyChart = lazy(() => import('./HeavyChart'));
CSS を最適化する:
- 未使用の CSS を削除
- クリティカル CSS はインライン化し、残りは非同期で読み込む
- CSS ファイルを最小化
- 独立した領域には CSS Containment を使用
フォントを最適化する:
font-display: swapまたはoptionalを使用- フォントをサブセット化する(必要な文字のみ)
- 重要なフォントをプリロードする
- 適切な場合はシステムフォントを使用する
- 読み込むフォントウェイトを制限する
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap; /* Show fallback immediately */
unicode-range: U+0020-007F; /* Basic Latin only */
}
フォントのサブセット化戦略:
サブセット化では、使用されていないグリフを削除してファイルサイズを大幅に削減します。ラテン文字全体を含むフォントは 200KB になることがありますが、同じフォントを Basic Latin にサブセット化すると 20〜30KB になります。
方法:
- 静的サブセット化:言語または文字体系ごとに個別のフォントファイル(ラテン文字、キリル文字、CJK)を生成し、ページで必要なものだけを
unicode-rangeで配信します - 動的サブセット化:Google Fonts のようなサービス、またはリクエストに応じて分割フォントを生成するセルフホスト型のサブセット化ツールを使用します
- クリティカルグリフのサブセット化:ファーストビューのヒーローテキストでは、見出しに使われている文字だけにサブセット化し、そのファイルをプリロードします
/* Latin-only subset for body text */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-latin.woff2') format('woff2');
font-display: swap;
unicode-range: U+0020-007F, U+00A0-00FF;
}
/* Extended subset for other scripts, loaded only when needed */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-cyrillic.woff2') format('woff2');
font-display: swap;
unicode-range: U+0400-04FF;
}
サブセット化ツール:
- 正確な手動サブセット化には
pyftsubset(fonttools) - ビルド時の自動サブセット化には
subfont - HTML/CSS で使用されているグリフのみを抽出するには
glyphhanger
クリティカル CSS の抽出:
ファーストビューのコンテンツに必要な CSS を抽出し、HTML の <head> にインラインで記述します。残りの CSS は非同期で読み込みます。
重要な理由:
- レンダリングをブロックする CSS は、First Contentful Paint の遅延に最も大きく寄与することがよくあります
- クリティカル CSS をインライン化すると、初期表示に必要なネットワークリクエストがなくなります
- 重要でない CSS は、ページが操作可能になった後に読み込めます
実装方法:
critical(npm パッケージ)またはPenthouseを使用して、ファーストビューの CSS を自動的に抽出します- または、ヘッダーのスタイル、ヒーローのレイアウト、初期表示のタイポグラフィ、ファーストビュー内のコンポーネントのスタイルなど、クリティカル CSS を手動で特定します
- クリティカル CSS を
<head>内の<style>タグにインラインで記述します - 完全なスタイルシートを非同期で読み込みます:
<head>
<style>
/* Critical CSS inlined here */
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>
</head>
注意点:
- 1 回の TCP 往復時間の予算内に収めるため、インライン化する CSS は約 14KB(gzip 圧縮後)を超えないようにします
- クリティカル CSS は実際のファーストビューのコンテンツと一致させる必要があります。レイアウトを変更した場合は再生成します
- SPA では、ページ単位ではなくルート単位のクリティカル CSS を検討します
読み込み戦略を最適化する:
- 重要なリソースを優先する(重要でないリソースには async/defer を使用)
- 重要なアセットをプリロードする
- 次に表示される可能性が高いページをプリフェッチする
- オフライン対応とキャッシュには Service Worker を使用する
- 多重化には HTTP/2 または HTTP/3 を使用する
コンポーネント単位のレスポンシブ対応にコンテナークエリを使用する:
コンテナークエリを使うと、コンポーネントはビューポートではなく、自身を囲むコンテナーのサイズに応じて表示を調整できます。これにより、ページ全体に適用するブレークポイントの上書きが減り、コンポーネントの再利用性が高まります。
使用する場面:
- コンポーネントが複数のコンテキスト(サイドバー、メインカラム、モーダル)に表示される場合
- コンポーネントのレイアウトをデバイスの幅ではなく、利用可能なスペースに応じて決める場合
- ページレベルでブレークポイントが増えすぎるのを防ぎたい場合
構文、実践的なパターン、ブラウザーのサポートについては、コンテナークエリのリファレンスを参照してください。 パフォーマンスに関する注意:
- コンテナークエリーによる実行時コストは最小限です。メディアクエリーと同様に、コンポジター上で実行されます
- 両方の寸法が本当に必要な場合を除き、
sizeよりcontainer-type: inline-sizeを優先してください - すべての要素に包含コンテキストを作成するのは避け、コンポーネントがコンテナーをクエリーする必要がある場合にのみ作成してください
レンダリングパフォーマンス
レイアウトのスラッシングを避ける:
// ❌ Bad: Alternating reads and writes (causes reflows)
elements.forEach(el => {
const height = el.offsetHeight; // Read (forces layout)
el.style.height = height * 2; // Write
});
// ✅ Good: Batch reads, then batch writes
const heights = elements.map(el => el.offsetHeight); // All reads
elements.forEach((el, i) => {
el.style.height = heights[i] * 2; // All writes
});
レンダリングを最適化する:
- 独立した領域には CSS の
containプロパティを使用します - DOM の深さを最小限に抑えます(フラットなほど高速です)
- DOM のサイズを小さくします(要素数を減らします)
- 長いリストには
content-visibility: autoを使用します - 非常に長いリストには仮想スクロールを使用します。アーキテクチャがまだ確定していない場合は TanStack Virtual を優先し、プロジェクトに既存の仮想化スタックがある場合は、まずそれを維持します
- テキスト量が多く、高さが可変のリストやカードでは、折り返しが実際のボトルネックである場合、ライブ DOM で高さを繰り返し読み取るよりも、予測的なテキスト計測を優先します
ペイントと合成を減らす:
- アニメーションには
transformとopacityを使用します(GPU アクセラレーション) - レイアウトプロパティ(width、height、top、left)のアニメーションは避けます
- コストの高い処理が予想される場合に限り、
will-changeを控えめに使用します - ペイント領域を最小限に抑えます(小さいほど高速です)
アニメーションのパフォーマンス
GPU アクセラレーション:
/* ✅ GPU-accelerated (fast) */
.animated {
transform: translateX(100px);
opacity: 0.5;
}
/* ❌ CPU-bound (slow) */
.animated {
left: 100px;
width: 300px;
}
60fps を滑らかに維持する:
- 1 フレームあたり 16ms(60fps)を目標にします
- JavaScript アニメーションには
requestAnimationFrameを使用します - スクロールハンドラーにはデバウンスまたはスロットリングを適用します
- 可能な場合は CSS アニメーションを使用します
- アニメーション中に長時間実行される JavaScript を避けます
Intersection Observer:
// Efficiently detect when elements enter viewport
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Element is visible, lazy load or animate
}
});
});
React/フレームワークの最適化
React 固有の最適化:
- コストの高いコンポーネントには
memo()を使用します - コストの高い計算には
useMemo()とuseCallback()を使用します - 長いリストを仮想化します
- ルートをコード分割します
- レンダリング中にインライン関数を作成しないようにします
- React DevTools Profiler を使用します
フレームワークに依存しない最適化:
- 再レンダリングを最小限に抑えます
- コストの高い処理にデバウンスを適用します
- 計算値をメモ化します
- ルートとコンポーネントを遅延読み込みします
ネットワークの最適化
リクエストを減らす:
- 小さなファイルを結合します
- アイコンには SVG スプライトを使用します
- 重要度の高い小さなアセットをインライン化します
- 使用されていないサードパーティスクリプトを削除します
API を最適化する:
- ページネーションを使用します(すべてを一度に読み込まないでください)
- 必要なフィールドのみをリクエストするには GraphQL を使用します
- レスポンスを圧縮します(gzip、brotli)
- HTTP キャッシュヘッダーを使用します
- 静的アセットには CDN を使用します
低速な接続向けに最適化する:
- 接続状況に応じて読み込みを調整します(navigator.connection)
- 楽観的 UI 更新を使用します
- リクエストの優先順位を設定します
- プログレッシブエンハンスメントを採用します
Core Web Vitals の最適化
Largest Contentful Paint(LCP < 2.5s)
- ヒーロー画像を最適化します
- 重要な CSS をインライン化します
- 重要なリソースをプリロードします
- CDN を使用します
- サーバーサイドレンダリングを使用します
First Input Delay(FID < 100ms)/INP(< 200ms)
- 長時間実行されるタスクを分割します
- 重要度の低い JavaScript の実行を遅らせます
- 負荷の高い計算には Web Worker を使用します
- JavaScript の実行時間を短縮します
Cumulative Layout Shift(CLS < 0.1)
- 画像と動画に寸法を設定します
- 既存のコンテンツの上にコンテンツを挿入しないでください
- CSS の
aspect-ratioプロパティを使用します - 広告や埋め込みコンテンツ用のスペースを確保します
- レイアウトシフトを引き起こすアニメーションを避けます
/* Reserve space for image */
.image-container {
aspect-ratio: 16 / 9;
}
パフォーマンスの監視
使用するツール:
- Chrome DevTools(Lighthouse、Performance パネル)
- WebPageTest
- Core Web Vitals(Chrome UX Report)
- バンドルアナライザー(webpack-bundle-analyzer)
- パフォーマンス監視ツール(Sentry、DataDog、New Relic)
主要な指標:
- LCP、FID/INP、CLS(Core Web Vitals)
- インタラクティブになるまでの時間(TTI)
- 初回コンテンツ描画(FCP)
- 合計ブロック時間(TBT)
- バンドルサイズ
- リクエスト数
重要:実際のデバイスと実際のネットワーク状況で測定してください。高速回線を使ったデスクトップ版 Chrome では、実情を反映できません。
絶対に避けること:
- 測定せずに最適化すること(早すぎる最適化)
- パフォーマンスのためにアクセシビリティを犠牲にすること
- 最適化中に機能を壊すこと
- あらゆる場所で
will-changeを使うこと(新しいレイヤーが作られ、メモリを消費します) - ファーストビューのコンテンツを遅延読み込みすること
- 大きな問題を無視して細かな最適化にこだわること(まず最大のボトルネックを最適化してください)
- 性能に制約のあるブラウザーでのパフォーマンスを忘れること(ハードウェアの性能が低く、CPU の余裕が少なく、接続速度も遅い場合がよくあります)
改善を検証する
最適化が機能したことをテストします。
- 改善前後の指標:Lighthouse のスコアを比較します
- 実ユーザーの監視:実際のユーザーに対する改善を追跡します
- さまざまなデバイス:低消費電力のタッチ対応デバイスと、一般的なノート PC/デスクトップのブラウザーでテストします
- 低速な接続:3G に速度を制限して、使用感をテストします
- リグレッションがないこと:引き続き機能が動作することを確認します
- ユーザーの体感:より速く感じられますか?
パフォーマンスは機能のひとつです。表示や応答が速いと、より反応がよく、洗練され、プロフェッショナルな印象になります。体系的に最適化し、徹底して測定し、ユーザーが体感するパフォーマンスを優先してください。