こんにちは。よっしーです(^^)
今日は、SvelteKitでのパフォーマンスについて解説しています。
背景
SvelteKitでのパフォーマンスについて調査する機会がありましたので、その時の内容を備忘として記事に残しました。
コードサイズの削減
Svelteのバージョン
最新バージョンのSvelteの使用を推奨します。Svelte 5はSvelte 4より小さく高速で、Svelte 4はSvelte 3より小さく高速です。
- バージョン比較
- Svelte 5 > Svelte 4 > Svelte 3
- パフォーマンス面での改善
- バンドルサイズの最適化
- 具体的な実装方法
// package.json
{
"dependencies": {
"svelte": "^5.0.0",
"@sveltejs/kit": "^2.0.0"
}
}
- バージョンアップグレードの手順
# 最新バージョンへのアップグレード
npm install svelte@latest
# 関連パッケージも更新
npm install @sveltejs/kit@latest
実務的なアドバイス:
- アップグレード時の注意点
- 破壊的変更の確認
- 非推奨機能の対応
- 依存パッケージの互換性確認
- バージョン管理のベストプラクティス
- 定期的なアップデート
- テスト環境での事前確認
- 段階的なアップグレード
- パフォーマンスの比較
- ビルドサイズの確認
- 実行速度の測定
- メモリ使用量の監視
- 移行の準備
- 変更履歴の確認
- 移行ガイドの参照
- テストケースの準備
これらの推奨事項に従うことで、アプリケーションの最適なパフォーマンスと最新機能を確保することができます。
パッケージ
`rollup-plugin-visualizer`は、サイトのサイズに最も影響を与えているパッケージを特定するのに役立ちます。また、ビルド出力を手動で検査することで(Vite設定で`build: { minify: false }`を使用すると出力が読みやすくなりますが、アプリをデプロイする前に元に戻すことを忘れないでください)、またはブラウザのdevtoolsのネットワークタブを通じて、コードを削除する機会を見つけることもできます。
- rollup-plugin-visualizerの実装
// vite.config.js
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
// 分析結果をHTMLファイルとして出力
open: true, // ビルド後に自動でブラウザを開く
filename: "stats.html",// 出力ファイル名
gzipSize: true, // Gzip圧縮後のサイズを表示
brotliSize: true, // Brotli圧縮後のサイズを表示
template: 'treemap' // 視覚化テンプレート
})
],
});
- ビルド出力の分析設定
// vite.config.js
export default defineConfig({
build: {
// 開発時の分析用設定
minify: false, // コードの最小化を無効化
sourcemap: true // ソースマップを有効化
}
});
- 実践的な分析手順
# 1. 開発用ビルド
npm run build -- --mode development
# 2. プロダクション用ビルド
npm run build -- --mode production
# 3. サイズ比較
ls -lh dist/assets/
最適化のベストプラクティス:
- パッケージサイズの分析
- バンドルの構成確認
- 大きなパッケージの特定
- 重複モジュールの検出
- 最適化の手法
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// 大きなライブラリを個別のチャンクに分割
'vendor': ['lodash', 'moment'],
'ui': ['@material-ui/core'],
}
}
}
}
});
- 代替手段の検討
// 重いライブラリの代替例
// Before:
import moment from 'moment';
const formattedDate = moment(date).format('YYYY-MM-DD');
// After:
import { format } from 'date-fns';
const formattedDate = format(date, 'yyyy-MM-dd');
分析・最適化チェックリスト:
- 初期分析
- バンドルサイズの確認
- 依存関係の確認
- 重複モジュールの特定
- 最適化ポイント
- 未使用コードの削除
- 軽量な代替パッケージの使用
- 動的インポートの活用
- パフォーマンス確認
- 読み込み時間の測定
- メモリ使用量の確認
- 実行速度の検証
- モニタリング設定
// パフォーマンスモニタリングの例
console.time('ModuleLoadTime');
import('./heavyModule.js').then(() => {
console.timeEnd('ModuleLoadTime');
});
実装のポイント:
- 開発時の設定
// vite.config.js
export default defineConfig({
// 開発モードの設定
build: {
minify: false,
sourcemap: true
},
// 本番モードの設定
production: {
build: {
minify: true,
sourcemap: false
}
}
});
- パッケージの最適化
// コード分割の例
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}
これらの分析ツールと手法を適切に活用することで、アプリケーションのパフォーマンスを大幅に改善することができます。
外部スクリプト
ブラウザで実行される外部スクリプトの数を最小限に抑えるよう努めてください。例えば、JavaScriptベースの分析の代わりに、CloudflareやNetlify、Vercelなど、SvelteKitアダプターを提供する多くのプラットフォームが提供するサーバーサイド実装の使用を検討してください。
サードパーティスクリプトをWebワーカーで実行する(メインスレッドのブロックを回避する)には、PartytownのSvelteKit統合を使用してください。
- サーバーサイド実装への移行
// サーバーサイドアナリティクスの例(Vercel)
// hooks.server.js
export async function handle({ event, resolve }) {
// ページビューの記録
if (event.platform?.env?.ANALYTICS_ID) {
await event.platform.env.ANALYTICS.writeDataPoint({
blobs: [event.url.pathname],
doubles: [1], // カウント
indexes: [event.platform.env.ANALYTICS_ID]
});
}
return await resolve(event);
}
- Partytownの実装
// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
// Partytownの設定
partytown: {
forward: ['dataLayer.push'], // Google Analyticsの例
}
},
preprocess: vitePreprocess()
};
export default config;
実装のベストプラクティス:
- サーバーサイドアナリティクス
// routes/+page.server.js
export async function load({ platform }) {
// プラットフォーム固有のアナリティクス
if (platform?.env?.ANALYTICS) {
await trackPageView({
path: event.url.pathname,
timestamp: new Date(),
// その他の必要なデータ
});
}
}
- Partytownの設定例
<!-- app.html -->
<html>
<head>
%sveltekit.head%
<!-- Partytown設定 -->
<script>
partytown = {
lib: "/_partytown/",
forward: ["dataLayer.push"],
debug: true
};
</script>
<!-- Partytownローダー -->
<script src="/_partytown/partytown.js"></script>
</head>
<body>
%sveltekit.body%
</body>
</html>
- 外部スクリプトの最適化
// 外部スクリプトのロード制御
const loadExternalScript = async (url) => {
if (browser) {
// クライアントサイドでのみロード
const script = document.createElement('script');
script.type = 'text/partytown';
script.src = url;
document.head.appendChild(script);
}
};
最適化チェックリスト:
- 外部スクリプトの監査
- 必要性の評価
- パフォーマンスへの影響分析
- 代替手段の検討
- サーバーサイド実装の検討
- プラットフォームの機能確認
- データ収集要件の整理
- プライバシー考慮事項
- Partytown実装の手順
- 依存関係のインストール
- 設定ファイルの作成
- スクリプトの移行
プラットフォーム別の実装例:
- Cloudflare
// analytics-cloudflare.js
export async function trackPageView(event) {
return await event.platform.env.ANALYTICS.writeDataPoint({
blobs: [event.url.pathname],
doubles: [1],
indexes: ['pageviews']
});
}
- Netlify
// analytics-netlify.js
export async function handleAnalytics(event) {
return await fetch('/.netlify/functions/analytics', {
method: 'POST',
body: JSON.stringify({
path: event.url.pathname,
timestamp: new Date()
})
});
}
- Vercel
// analytics-vercel.js
import { analytics } from '@vercel/analytics';
export function trackPageView(path) {
analytics.track('pageview', {
path,
timestamp: new Date()
});
}
これらの最適化を適切に実装することで、外部スクリプトによるパフォーマンスへの影響を最小限に抑えながら、必要な機能を維持することができます。
選択的読み込み
静的な`import`宣言でインポートされたコードは、ページの残りの部分と自動的にバンドルされます。何らかの条件が満たされた場合にのみ必要なコードがある場合は、動的な`import(…)`形式を使用して、コンポーネントを選択的に遅延読み込みしてください。
- 静的インポートと動的インポートの違い
// 静的インポート(すべて初期バンドルに含まれる)
import HeavyComponent from './HeavyComponent.svelte';
// 動的インポート(必要時に読み込まれる)
const loadComponent = async () => {
const module = await import('./HeavyComponent.svelte');
return module.default;
};
- 実装例
<!-- 条件付き遅延ローディングの例 -->
<script>
let HeavyComponent;
let showComponent = false;
async function loadHeavyComponent() {
const module = await import('./HeavyComponent.svelte');
HeavyComponent = module.default;
}
function handleClick() {
showComponent = true;
loadHeavyComponent();
}
</script>
<button on:click={handleClick}>
Load Component
</button>
{#if showComponent}
{#await HeavyComponent}
<p>Loading...</p>
{:then Component}
<svelte:component this={Component} />
{/await}
{/if}
実践的な実装パターン:
- ルートベースの遅延ローディング
// routes/+page.js
export const load = async ({ url }) => {
if (url.searchParams.has('advanced')) {
// 高度な機能が必要な場合のみロード
const { advancedFeatures } = await import('../lib/advanced-features');
return { advancedFeatures };
}
return {};
};
- 条件付きコンポーネントローディング
<script>
let userType = 'basic';
let Dashboard;
$: if (userType === 'admin') {
import('./AdminDashboard.svelte').then(module => {
Dashboard = module.default;
});
} else {
import('./BasicDashboard.svelte').then(module => {
Dashboard = module.default;
});
}
</script>
{#if Dashboard}
<svelte:component this={Dashboard} />
{:else}
<p>Loading dashboard...</p>
{/if}
- 機能ベースの遅延ローディング
// 機能モジュールの動的インポート
async function loadFeature(featureName) {
try {
const module = await import(`./features/${featureName}.js`);
return module.default;
} catch (error) {
console.error(`Failed to load feature: ${featureName}`, error);
return null;
}
}
最適化のベストプラクティス:
- コード分割の戦略
- 重要度に基づく分割
- ユーザーフローに基づく分割
- サイズに基づく分割
- ローディング状態の管理
<script>
let isLoading = false;
let error = null;
let Component = null;
async function loadComponent() {
isLoading = true;
try {
const module = await import('./HeavyComponent.svelte');
Component = module.default;
} catch (e) {
error = e;
} finally {
isLoading = false;
}
}
</script>
{#if isLoading}
<LoadingSpinner />
{:else if error}
<ErrorMessage {error} />
{:else if Component}
<svelte:component this={Component} />
{/if}
- プリフェッチの実装
// プリフェッチ関数
function prefetchComponent() {
// ユーザーの操作を予測して事前に読み込み
const prefetch = import('./HeavyComponent.svelte');
return () => prefetch;
}
実装時のチェックリスト:
- パフォーマンス考慮事項
- 初期バンドルサイズ
- 遅延ロード時のタイミング
- ネットワーク条件への対応
- ユーザー体験の最適化
- ローディング表示
- エラーハンドリング
- フォールバックコンテンツ
- メンテナンス性
- コードの構造化
- 依存関係の管理
- デバッグのしやすさ
これらの最適化テクニックを適切に実装することで、アプリケーションの初期読み込み時間を短縮し、必要なコードのみを効率的に提供することができます。
おわりに
今日は、 SvelteKitでのパフォーマンスについて解説しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント