1. コアウェブバイタル
| メトリック | 説明 | 良い | 改善方法 |
|---|---|---|---|
| LCP | 最大のコンテンツ豊富なペイント | < 2.5秒 | 優先画像、プリロードフォント |
| INP | 次のペイントへのインタラクション | < 200ms | JS を減らし、トランジションを使用する |
| CLS | 累積レイアウトシフト | < 0.1 | 画像サイズを設定し、FOUT を回避する |
2. 動的インポートとコード分割
import dynamic from 'next/dynamic';
// Lazy load heavy component
const CodeEditor = dynamic(() => import('@/components/CodeEditor'), {
loading: () => <div>Loading editor...</div>,
ssr: false, // Client-only component
});
// Conditional dynamic import
const AdminPanel = dynamic(() => import('@/components/AdminPanel'));
export default function Page({ isAdmin }: { isAdmin: boolean }) {
return (
<div>
<CodeEditor />
{isAdmin && <AdminPanel />}
</div>
);
}
3. メモ化パターン
import { memo, useMemo, useCallback } from 'react';
// memo — skip re-render if props unchanged
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
return (
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
});
// useMemo — cache expensive computation
function Dashboard({ data }: { data: number[] }) {
const stats = useMemo(() => ({
total: data.reduce((a, b) => a + b, 0),
average: data.reduce((a, b) => a + b, 0) / data.length,
max: Math.max(...data),
}), [data]);
return <div>Total: {stats.total}</div>;
}
// useCallback — stable function reference
function Parent() {
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []);
return <ExpensiveChild onClick={handleClick} />;
}
4. バンドル分析
npm install -D @next/bundle-analyzer
// next.config.ts
import withBundleAnalyzer from '@next/bundle-analyzer';
const config = withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})({
// ...other config
});
export default config;
# Analyze bundle
ANALYZE=true npm run build
5. フォントの最適化
// app/layout.tsx
import { Inter, JetBrains_Mono } from 'next/font/google';
const inter = Inter({
subsets: ['latin', 'vietnamese'],
display: 'swap',
variable: '--font-inter',
});
const mono = JetBrains_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-mono',
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="vi" className={`${inter.variable} ${mono.variable}`}>
<body className="font-sans">{children}</body>
</html>
);
}
6. ヘッダーのキャッシュ
// next.config.ts
const config = {
async headers() {
return [
{
source: '/images/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
],
},
{
source: '/api/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, s-maxage=60, stale-while-revalidate=300' },
],
},
];
},
};
7. React コンパイラー (実験的)
// next.config.ts
const config = {
experimental: {
reactCompiler: true, // Auto-memoize components
},
};
次の記事: Docker と CI/CD — コンテナ化、GitHub Actions、Vercel デプロイメント。