こんにちは。よっしーです(^^)
今日は、SvelteKitでのVercelについて解説しています。
背景
SvelteKitでのVercelについて調査する機会がありましたので、その時の内容を備忘として記事に残しました。
Vercelとは
Vercelは、ウェブサイトやウェブアプリケーションのホスティングと継続的デプロイメントを提供する現代的なクラウドプラットフォームです。主な特徴は以下の通りです:
- 静的サイトホスティング: HTML、JavaScript、CSSなどの静的ファイルを簡単にホストできます。
- サーバーレス関数: Node.js、Python、Goなどの言語でサーバーサイド機能を実装できます。
- エッジコンピューティング: Vercel Edgeを使用して、ユーザーに近い場所でコードを実行できます。
- 継続的デプロイメント: GitHubやGitLabなどのリポジトリと連携し、コードの変更を自動的にデプロイします。
- プレビュー機能: プルリクエストごとにユニークなURLでプレビュー環境を自動生成します。
- カスタムドメイン: 独自ドメインの使用と、自動的なSSL証明書の提供があります。
- グローバルCDN: 世界中のエッジロケーションを活用した高速なコンテンツ配信を実現します。
- 分析機能: リアルタイムのパフォーマンス分析やユーザーインサイトを提供します。
- チーム連携: 複数の開発者やチームでのプロジェクト管理をサポートします。
- フレームワークサポート: Next.js、Nuxt、SvelteKit、Astroなど、多くのモダンなフレームワークをサポートしています。
Vercelは特に、JAMstackアーキテクチャを採用したプロジェクトやシングルページアプリケーション(SPA)の開発に適しており、フロントエンド開発者に人気があります。
Vercelへのデプロイ
Vercel にデプロイするには、adapter-vercel を使用します。 このアダプタは、adapter-auto を使用するとデフォルトでインストールされますが、プロジェクトに追加すると、Vercel 固有のオプションを指定できます。
使い方
npm i -D @sveltejs/adapter-vercel でインストールし、アダプターを svelte.config.js に追加します。
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
// see below for options that can be set here
})
}
};
デプロイ設定
Vercelに関数としてルートをデプロイする方法を制御するために、デプロイメント設定を指定することができます。これは、前述のオプションを使用するか、+server.js
、+page(.server).js
、+layout(.server).js
ファイル内でexport const config
を使用して行えます。
例えば、アプリケーションの一部をエッジ関数としてデプロイすることができます…
// about/+page.js
/** @type {import('@sveltejs/adapter-vercel').Config} */
export const config = {
runtime: 'edge'
};
…その他はサーバーレス関数として使用できます (レイアウト内で構成を指定すると、すべての子ページに適用されることに注意してください)。
// admin/+layout.js
/** @type {import('@sveltejs/adapter-vercel').Config} */
export const config = {
runtime: 'nodejs18.x'
};
すべての関数に適用されるオプションは以下の通りです:
runtime
:'edge'
、'nodejs18.x'
、または'nodejs20.x'
。デフォルトでは、アダプターはVercelダッシュボードで設定されているプロジェクトのNodeバージョンに対応する'nodejs<version>.x'
を選択します。regions
: エッジネットワークリージョンの配列(サーバーレス関数の場合、デフォルトは["iad1"]
)、またはruntime
がedge
の場合は'all'
(これがデフォルト)。サーバーレス関数の複数リージョンはエンタープライズプランでのみサポートされています。split
:true
の場合、ルートが個別の関数としてデプロイされます。アダプターレベルでsplit
がtrue
に設定されている場合、すべてのルートが個別の関数としてデプロイされます。
さらに、以下のオプションはエッジ関数に適用されます:
external
: 関数をバンドルする際にesbuildが外部として扱うべき依存関係の配列。これはNode外では実行されないオプションの依存関係を除外するためにのみ使用されます。
そして、以下のオプションはサーバーレス関数に適用されます:
memory
: 関数が利用可能なメモリ量。デフォルトは1024
Mbで、128
Mbまで減らすことができ、ProまたはEnterpriseアカウントでは64Mb単位で3008
Mbまで増やすことができます。maxDuration
: 関数の最大実行時間。Hobbyアカウントではデフォルトで10
秒、Proでは15
秒、Enterpriseでは900
秒です。isr
: 増分静的再生成(Incremental Static Regeneration)の設定。詳細は後述。
関数が特定のリージョンのデータにアクセスする必要がある場合、最適なパフォーマンスを得るために同じリージョン(または近いリージョン)にデプロイすることが推奨されます。
この情報は、Vercelでの関数デプロイに関する詳細な設定オプションを説明しています。主なポイントは:
- ランタイムの選択(Edge、Node.jsのバージョン)
- デプロイリージョンの指定
- 関数の分割デプロイ
- エッジ関数とサーバーレス関数それぞれに特有の設定
- メモリ割り当てとタイムアウト設定
- 増分静的再生成(ISR)の設定
これらの設定により、開発者はアプリケーションの各部分を最適な方法でデプロイし、パフォーマンスとコスト効率を調整することができます。
画像の最適化
画像構成を設定することで、Vercel が画像を構築する方法を制御できます。詳細については、画像構成リファレンスを参照してください。例として、次のように設定できます。
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter({
images: {
sizes: [640, 828, 1200, 1920, 3840],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 300,
domains: ['example-app.vercel.app'],
}
})
}
};
Incremental Static Regeneration
Vercelは増分静的再生成(Incremental Static Regeneration、ISR)をサポートしています。ISRは、プリレンダリングされたコンテンツのパフォーマンスとコスト面での利点を、動的にレンダリングされるコンテンツの柔軟性と組み合わせて提供します。
ルートにISRを追加するには、config
オブジェクトにisr
プロパティを含めます:
この情報は、VercelでのISR(増分静的再生成)の使用に関する重要なポイントを説明しています:
- ISRの利点:
- プリレンダリングされたコンテンツの高パフォーマンス
- プリレンダリングされたコンテンツの低コスト
- 動的にレンダリングされるコンテンツの柔軟性
- ISRの設定方法:
config
オブジェクトにisr
プロパティを追加する
ISRは、静的サイト生成(SSG)と動的レンダリングの中間的なアプローチです。これにより、頻繁に更新される可能性のあるページでも、静的ページのパフォーマンス利点を活用しつつ、最新のコンテンツを提供することができます。
// blog/[slug]/+page.server.js
import { BYPASS_TOKEN } from '$env/static/private';
export const config = {
isr: {
// Expiration time (in seconds) before the cached asset will be re-generated by invoking the Serverless Function.
// Setting the value to `false` means it will never expire.
expiration: 60,
// Random token that can be provided in the URL to bypass the cached version of the asset, by requesting the asset
// with a __prerender_bypass=<token> cookie.
//
// Making a `GET` or `HEAD` request with `x-prerender-revalidate: <token>` will force the asset to be re-validated.
bypassToken: BYPASS_TOKEN,
// List of valid query parameters. Other parameters (such as utm tracking codes) will be ignored,
// ensuring that they do not result in content being regenerated unnecessarily
allowQuery: ['search']
}
};
有効期限プロパティは必須ですが、その他はすべてオプションです。
事前レンダリングされたページは ISR 構成を無視します。
環境変数
Vercelは、デプロイメント固有の環境変数のセットを利用可能にしています。他の環境変数と同様に、これらは$env/static/private
と$env/dynamic/private
からアクセス可能です(ただし、後述するように、常にというわけではありません)。また、これらの変数はパブリックな対応物からはアクセスできません。これらの変数の1つにクライアントからアクセスするには:
この情報は、Vercelでの環境変数の使用に関する重要なポイントを説明しています:
- デプロイメント固有の環境変数:
Vercelは、デプロイメントに関連する特定の環境変数を提供しています。 - アクセス方法:
$env/static/private
からアクセス可能$env/dynamic/private
からアクセス可能
ただし、常にこれらの方法でアクセスできるわけではないようです。
- アクセス制限:
これらの変数は、対応するパブリックな方法(おそらく$env/static/public
や$env/dynamic/public
)からはアクセスできません。 - クライアントからのアクセス:
クライアントサイドからこれらの変数にアクセスする方法があることが示唆されていますが、具体的な方法は省略されています。
この情報は、SvelteKitプロジェクトをVercelにデプロイする際の環境変数の取り扱いについて説明しています。特に、セキュリティとプライバシーを考慮した環境変数の管理方法に焦点を当てています。
// +layout.server.js
import { VERCEL_COMMIT_REF } from '$env/static/private';
/** @type {import('./$types').LayoutServerLoad} */
export function load() {
return {
deploymentGitBranch: VERCEL_COMMIT_REF
};
}
// +layout.svelte
<script>
/** @type {import('./$types').LayoutServerData} */
export let data;
</script>
<p>This staging environment was deployed from {data.deploymentGitBranch}.</p>
Vercel上でビルドする際、これらの変数はすべてビルド時と実行時の間で変更されないため、$env/dynamic/private
ではなく$env/static/private
を使用することをお勧めします。$env/static/private
は変数を静的に置換するため、デッドコード除去のような最適化が可能になります。
この情報は、Vercelでの環境変数の使用に関する重要な推奨事項を説明しています:
- 環境変数の不変性:
Vercel上でのビルドでは、これらの特定の環境変数はビルド時から実行時まで変更されません。 - 推奨される使用方法:
$env/static/private
の使用が推奨されています。 $env/static/private
の利点:
- 変数を静的に置換します。
- これにより、デッドコード除去などの最適化が可能になります。
$env/dynamic/private
との比較:
静的な置換と最適化の可能性から、$env/dynamic/private
よりも$env/static/private
が優先されています。
この推奨事項の背景には、パフォーマンスの最適化があります。静的な環境変数を使用することで、ビルド時に不要なコードを除去したり、より効率的なコードを生成したりすることができます。
これは特にVercelでのデプロイメントに関する最適化テクニックとして重要です。SvelteKitプロジェクトをVercelにデプロイする際、このアプローチを採用することで、より効率的で最適化されたアプリケーションを実現できます。
Skew保護
アプリの新バージョンがデプロイされると、以前のバージョンに属するアセットにアクセスできなくなる可能性があります。ユーザーがアプリを使用中にこれが発生すると、ナビゲーション時にエラーが発生することがあります。これは「バージョンスキュー」として知られています。SvelteKitは、バージョンスキューによるエラーを検出し、アプリの最新バージョンを取得するためにハードリロードを引き起こすことでこれを軽減しますが、これによりクライアント側の状態が失われる可能性があります。(updated
ストアの値を監視することで、新しいバージョンがデプロイされたときにクライアントに通知することで、これを事前に軽減することもできます。)
スキュー保護は、クライアントのリクエストを元のデプロイメントにルーティングするVercelの機能です。ユーザーがアプリにアクセスすると、デプロイメントIDを含むクッキーが設定され、その後のリクエストは、スキュー保護がアクティブである限り、そのデプロイメントにルーティングされます。ページをリロードすると、最新のデプロイメントが取得されます。(updated
ストアはこの動作から除外されるため、引き続き新しいデプロイメントを報告します。)この機能を有効にするには、VercelのプロジェクトSettingsの「Advanced」セクションにアクセスしてください。
クッキーベースのスキュー保護には一つ注意点があります:ユーザーが複数のタブでアプリの複数のバージョンを開いている場合、古いバージョンからのリクエストは新しいバージョンにルーティングされます。つまり、SvelteKitの組み込みスキュー保護に頼ることになります。
備考
Vercel functionsについて
プロジェクトのルートにあるapi
ディレクトリにVercel functionsがある場合、/api/*
へのリクエストはSvelteKitによって処理されません。代わりに、これらをSvelteKitアプリのAPIルートとして実装するべきです。ただし、JavaScriptではない言語を使用する必要がある場合は例外で、その場合はSvelteKitアプリに/api/*
ルートがないことを確認する必要があります。
Nodeバージョンについて
特定の日付以前に作成されたプロジェクトは、SvelteKitが現在要求しているものより古いNodeバージョンをデフォルトで使用している可能性があります。プロジェクト設定でNodeバージョンを変更することができます。
トラブルシューティング
ファイルシステムへのアクセスについて
エッジ関数ではfs
を使用することはできません。
サーバーレス関数ではfs
を使用することは可能ですが、プロジェクトからデプロイメントにファイルがコピーされないため、期待通りに動作しません。代わりに、$app/server
からread
関数を使用してファイルにアクセスしてください。ただし、read
はエッジ関数としてデプロイされたルート内では機能しません(これは将来変更される可能性があります)。
別の方法として、問題のルートをプリレンダリングすることもできます。
おわりに
今日は、 SvelteKitでのVercelについて解説しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント