はじめに
本記事で書いていること
本記事は本当にただ SSG として動くいわゆるハローワールドやってみた(API からのフェッチはする)しか扱っていません。記載しているコードも公式ドキュメントをそのまま転用しているだけだったりしているところも多いです。
Jamstack については素晴らしい記事が本アドベントカレンダー後半で投稿されますのでそちらをご覧ください。
ベンチ要員
SI アドベントカレンダー2022、素晴らしいことに定員が埋まっておりましたが、もしかしたら業務上の理由などで投稿が間に合わない方などいらっしゃる可能性があるので、とベンチを温めていました。
なんで SvelteKit で SSG
The State of JS 2021: フロントエンドフレームワーク
によると最近熱いらしい Svelte。
名前をちらほら聞いておりまして、
そんなタイミングでたまたまイベント告知ページを作ってくれやと知人に頼まれ、
またたまたまプロジェクトのメンバーの一人が Jamstack が熱いと教えてくれたこともあり、
じゃあ SvelteKit 使ってみよ、ということになりました。
環境
$ node -v
v18.12.1
$ npm -v
8.19.2
$
VSCode でやります。
プロジェクト作成
npm create svelte@latest {プロジェクト名}
を実行します、色々あれ入れるかこれ入れるか聞いてくれてあたたかい。
$ npm create svelte@latest my-app
create-svelte version 2.0.0-next.199
Welcome to SvelteKit!
This is release candidate software; expect bugs and missing features.
Problems? Open an issue on https://github.com/sveltejs/kit/issues if none exists already.
✔ Which Svelte app template? › Skeleton project
✔ Add type checking with TypeScript? › Yes, using TypeScript syntax
✔ Add ESLint for code linting? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
✔ Add Playwright for browser testing? … No / Yes
✔ Add Vitest for unit testing? … No / Yes
Your project is ready!
✔ Typescript
Inside Svelte components, use <script lang="ts">
✔ ESLint
https://github.com/sveltejs/eslint-plugin-svelte3
✔ Prettier
https://prettier.io/docs/en/options.html
https://github.com/sveltejs/prettier-plugin-svelte#options
✔ Playwright
https://playwright.dev
✔ Vitest
https://vitest.dev
Install community-maintained integrations:
https://github.com/svelte-add/svelte-adders
Next steps:
1: cd my-app
2: npm install (or pnpm install, etc)
3: git init && git add -A && git commit -m "Initial commit" (optional)
4: npm run dev -- --open
To close the dev server, hit Ctrl-C
Stuck? Visit us at https://svelte.dev/chat
$
my-app
ディレクトリに移動して npm install
したら npm run dev
でローカルで動かせます。
SSG にしていく
ググればいろんな人が教えてくれているんですが、ビルドする時に API とかからデータとってサーバで HTML (レンダリング済) 作ってサーバに置いておくってやつですな。ビルドしないと内容変わらないから、ブログとか Wiki には向いている。
SSG 用パッケージのインストール
$ npm install -D @sveltejs/adapter-static
...
$
設定 js 修正
import adapter from '@sveltejs/adapter-static'; -- 使用する adapter を上でインストールしたものに変える
import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),
kit: {
adapter: adapter()
}
};
export default config;
prerenderable
この段階で npm run build
を実行すると、失敗します。
$ npm run build
...
> Using @sveltejs/adapter-static
@sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic:
- src/routes/
You have the following options:
- set the `fallback` option — see https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode for more info.
- add `export const prerender = true` to your root `+layout.js/.ts` or `+layout.server.js/.ts` file. This will try to prerender all pages.
- add `export const prerender = true` to any `+server.js/ts` files that are not fetched by page `load` functions.
- pass `strict: false` to `adapter-static` to ignore this error. Only do this if you are sure you don't need the routes in question in your final app, as they will be unavailable. See https://github.com/sveltejs/kit/tree/master/packages/adapter-static#strict for more info.
If this doesn't help, you may need to use a different adapter. @sveltejs/adapter-static can only be used for sites that don't need a server for dynamic rendering, and can run on just a static file server.
See https://kit.svelte.dev/docs/page-options#prerender for more details
error during build:
Error: Encountered dynamic routes
at adapt (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/@sveltejs/adapter-static/index.js:53:12)
at adapt (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/@sveltejs/kit/src/core/adapt/index.js:28:8)
at Object.handler (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/@sveltejs/kit/src/exports/vite/index.js:543:12)
at async PluginDriver.hookParallel (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/rollup/dist/es/shared/rollup.js:22670:17)
at async Object.close (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/rollup/dist/es/shared/rollup.js:23750:13)
at async Promise.all (index 0)
at async build (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/vite/dist/node/chunks/dep-5605cfa4.js:45548:13)
at async CAC.<anonymous> (file:///Users/k.sakaguchi/Documents/temp/my-app/node_modules/vite/dist/node/cli.js:756:9)
$
書かれているように、すべてのルートで prerender できるようにしないとダメだぞと怒られます。
SvelteKit では src/routes/
配下のディレクトリ構成がそのままルーティングになります。この際、このディレクトリ構成範囲外の URL にアクセスされた時どうするねんってのを指定しろということですな。
SPA であれば fallback オプションを使えーいとかありますが、今回は SSG ですので、fallback とかは指定しないです。二個目の export const prerender = true
を含んだ +layout.server.ts
を配置します。
SvelteKit では *.svelte
ファイルがページを、 同名の .js / .ts
で API などからのデータを取得する load 関数を実装するという仕組みです。
+layout.svelte
は全ページで表示される内容を実装するのですが、今回は SSG するだけの男なので見た目とかありません、 +layout.server.ts
だけを実装します。
export const prerender = true;
これで npm run build
が通るようになります。
データを load する
ここで、ビルド時に API からデータを取得する部分の実装をします。デフォルトで配置されている +page.svelte
が下記になります。
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
このファイルの修正と、このページ用のデータを取得する +page.server.ts
を追加します。
今回ヘッドレス CMS として microCMS を使用してます。
環境変数は Netlify で設定できますが、.env
ファイルを作成してそこから取得することも可能です。
import { error } from '@sveltejs/kit';
import { API_KEY } from '$env/static/private'
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async () => {
const headers: HeadersInit = new Headers();
const apiKey: string = API_KEY ?? "";
console.log(apiKey)
headers.set("X-MICROCMS-API-KEY", apiKey)
const res = await fetch('https://example.microcms.io/api/v1/hoge', {
headers: headers
});
const resJson = await res.json()
console.log(resJson)
if (res.ok) {
return await resJson;
}
throw error(404, 'Not found');
}
環境変数から API-KEY を取得し、リクエストを投げています。
env
を設定で指定します、.env
の配置している場所を指定するため、もう一度 svelte.config.js
を修正します。
import adapter from '@sveltejs/adapter-static';
import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),
kit: {
adapter: adapter()
},
env: {
dir: process.cwd()
}
};
export default config;
取得した値を +page.svelte
で反映します。
<script lang="ts">
import type { PageServerData } from './$types';
export let data: PageServerData;
</script>
<h1>{data.contents[0].fuga}</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
npm run dev
で実行すると、 console.log()
の内容がブラウザ側ではなく実行しているターミナルに表示され、サーバで動いてるんやでってのが分かりますね。
環境変数の取り方も 4 種類あります、
$env/static/private
$env/dynamic/private
$env/static/public
$env/dynamic/public
詳しい説明は ドキュメント に任せますが、SSG でビルドする時に必要なだけなので、今回は $env/static/private
を使用してます。
Jamstack
今回の記事の対象ではないので簡単に書きますが、SSG はビルドしたファイルをサーバに配置して配信するのですが、流石にブログでも更新するたびに自分でビルドしてデプロイしてというわけには行きません。はじめに書いたように、いい感じにやる Jamstack という構成を今回使用しました。
microCMS で API を作成し、ホスティングサービスとして Netlify を選択。
今回作成した SSG アプリとくみあわせると、
- microCMS で API のレスポンスの値を変更したタイミング
- main ブランチが更新されたタイミング
でビルドしてデプロイという CD をしてくれる仕組みをかなり簡単に作れます。いいね。
はじめにでも書きましたが、詳しくは SI アドベントカレンダー後半の記事を要チェックや。
以上、拙文失礼しました。
参考
Introduction • Docs • SvelteKit
icroCMS + Svelte + Sapper + TypeScriptでJamstackブログを作ってみよう
Welcome to Netlify | Netlify Docs