パフォーマンス計測 コピペで使える Tips 集【実務向け】
パフォーマンス計測 コピペで使えるコードを一か所にまとめました。「計測できないものは改善できない」を前提に、ブラウザ・Node.js・CI まで幅広くカバーしています。
performance.now() / performance.mark() で処理時間を計測する
performance.now() で任意区間を計測
const start = performance.now();
// 計測したい処理
await heavyTask();
const elapsed = performance.now() - start;
console.log(`${elapsed.toFixed(2)} ms`);
performance.mark() と measure() でタイムラインに残す
DevTools の Performance タブで可視化できるのが利点です。
performance.mark("parse:start");
const data = JSON.parse(largeJson);
performance.mark("parse:end");
performance.measure("parse", "parse:start", "parse:end");
const [entry] = performance.getEntriesByName("parse");
console.log(`${entry.duration.toFixed(2)} ms`);
performance.clearMarks();
performance.clearMeasures();
ラッパー関数で使い回す
async function measure<T>(label: string, fn: () => Promise<T>): Promise<T> {
performance.mark(`${label}:start`);
const result = await fn();
performance.mark(`${label}:end`);
performance.measure(label, `${label}:start`, `${label}:end`);
const [entry] = performance.getEntriesByName(label);
console.log(`[${label}] ${entry.duration.toFixed(2)} ms`);
return result;
}
// 使い方
const users = await measure("fetchUsers", () => fetchUsers());
console.time / timeEnd の使い分け
console.time は手軽ですが、performance.now() より精度・用途が限られます。使い分けの基準は以下のとおりです。
| 用途 | 推奨 |
|---|---|
| 手軽にデバッグ | console.time |
| 精度が必要・DevTools に残したい | performance.mark |
| Node.js スクリプト | process.hrtime.bigint() |
console.time("label");
doSomething();
console.timeEnd("label");
// label: 12.345ms
ネストしたラベルで複数区間を同時計測できます。
console.time("total");
console.time("step1");
await step1();
console.timeEnd("step1");
console.time("step2");
await step2();
console.timeEnd("step2");
console.timeEnd("total");
Node.js で高精度な計測が必要な場合は process.hrtime.bigint() を使います。
const start = process.hrtime.bigint();
doHeavyWork();
const end = process.hrtime.bigint();
console.log(`${Number(end - start) / 1_000_000} ms`);
Lighthouse CLI でスコアを自動計測する
インストールと基本実行
npm install -g lighthouse
lighthouse https://example.com --output json --output-path ./report.json --chrome-flags="--headless"
スコアだけ取り出す
lighthouse https://example.com \
--output json \
--output-path ./lh.json \
--chrome-flags="--headless" \
--quiet
node -e "
const r = require('./lh.json');
const cats = r.categories;
console.log('Performance:', Math.round(cats.performance.score * 100));
console.log('Accessibility:', Math.round(cats.accessibility.score * 100));
console.log('Best Practices:', Math.round(cats['best-practices'].score * 100));
console.log('SEO:', Math.round(cats.seo.score * 100));
"
CI(GitHub Actions)に組み込む
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lhci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci && npm run build
- run: npx lhci autorun
lighthouserc.json でスコアの下限を設定してゲートにできます。
{
"ci": {
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.9 }],
"categories:accessibility": ["warn", { "minScore": 0.9 }]
}
},
"upload": { "target": "temporary-public-storage" }
}
}
Bundle サイズを分析する
Vite: vite-bundle-visualizer
npx vite-bundle-visualizer
vite.config.ts でビルド後に自動実行したい場合は以下のとおりです。
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
visualizer({
open: true,
filename: "dist/stats.html",
gzipSize: true,
brotliSize: true,
}),
],
});
webpack: webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer
// webpack.config.js
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "static",
reportFilename: "bundle-report.html",
openAnalyzer: false,
}),
],
};
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json
サイズを数値で確認する(CI 向け)
# ビルド後の JS/CSS 合計サイズを確認
find dist -name "*.js" -o -name "*.css" | xargs du -sh | sort -h
# gzip 後のサイズ
find dist/assets -name "*.js" | while read f; do
original=$(wc -c < "$f")
gzipped=$(gzip -c "$f" | wc -c)
echo "$f: ${original}B → ${gzipped}B (gzip)"
done
Web Vitals(LCP・CLS・INP)を計測する
web-vitals ライブラリで計測
npm install web-vitals
import { onLCP, onCLS, onINP } from "web-vitals";
onLCP((metric) => {
console.log("LCP:", metric.value, "ms");
});
onCLS((metric) => {
console.log("CLS:", metric.value);
});
onINP((metric) => {
console.log("INP:", metric.value, "ms");
});
Analytics に送信する
import { onLCP, onCLS, onINP, type Metric } from "web-vitals";
function sendToAnalytics(metric: Metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
id: metric.id,
navigationType: metric.navigationType,
});
navigator.sendBeacon("/api/vitals", body);
}
onLCP(sendToAnalytics);
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
ブラウザ DevTools で手動確認(コピペ用スニペット)
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === "largest-contentful-paint") {
console.log("LCP:", entry.startTime.toFixed(0), "ms");
}
}
}).observe({ type: "largest-contentful-paint", buffered: true });
new PerformanceObserver((list) => {
let clsScore = 0;
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) clsScore += entry.value;
}
console.log("CLS:", clsScore.toFixed(4));
}).observe({ type: "layout-shift", buffered: true });
Node.js の —prof と clinic.js でプロファイリングする
—prof でフレームグラフを生成
node --prof server.js
# 負荷をかけてから Ctrl+C で停止
# isolate-*.log が生成される
node --prof-process isolate-*.log > processed.txt
cat processed.txt | head -60
clinic.js でビジュアルプロファイリング
npm install -g clinic
# フレームグラフ(CPU ボトルネック)
clinic flame -- node server.js
# バブルチャート(I/O・イベントループ)
clinic bubbleprof -- node server.js
# ヒープ解析(メモリリーク)
clinic heapprofiler -- node server.js
単発の関数をプロファイルする
import { Session } from "node:inspector/promises";
import { writeFileSync } from "node:fs";
async function profileFn<T>(fn: () => Promise<T>, outPath: string): Promise<T> {
const session = new Session();
session.connect();
await session.post("Profiler.enable");
await session.post("Profiler.start");
const result = await fn();
const { profile } = await session.post("Profiler.stop");
writeFileSync(outPath, JSON.stringify(profile));
session.disconnect();
console.log(`CPU profile saved to ${outPath}`);
return result;
}
// 使い方(出力は Chrome DevTools の JavaScript Profiler で開く)
await profileFn(() => heavyComputation(), "profile.cpuprofile");
まとめ
| 計測対象 | ツール・API | 特徴 |
|---|---|---|
| 任意コードの処理時間 | performance.now() / performance.mark() | ブラウザ・Node.js 両対応、DevTools と連携 |
| 手軽なデバッグ計測 | console.time / process.hrtime.bigint() | コードが少ない、精度はやや低い |
| ページ総合スコア | Lighthouse CLI / LHCI | CI に組み込んでゲートにできる |
| バンドルサイズ分析 | vite-bundle-visualizer / webpack-bundle-analyzer | ビジュアルでチャンク構成を把握 |
| Core Web Vitals | web-vitals ライブラリ / PerformanceObserver | LCP・CLS・INP をリアルユーザーで計測 |
| Node.js CPU プロファイル | --prof / clinic.js | フレームグラフでホットスポットを特定 |
計測は一度やって終わりではなく、コードの変更や依存関係の更新のたびに継続するのが効果的です。CI に組み込んでリグレッションを自動検知できると、パフォーマンス品質を長期的に保ちやすくなります。