これから来るとか来ないとか、海外ではもう来てるとか言われている web フロントエンド JS のフレームワーク(コンパイラ)の **Svelte**。 今回はその Svelte を Web アプリケーション制作用途に拡張するフレームワーク、**Sapper** のお話。
{参考}
#静的ジェネレート
SSR メインなフレームワークで作ったサイトを、 Netlify 等の静的ファイルホスティングサービスで運用する、すなわち JAMstack な構成をとりたいとき、全ページを事前に静的ジェネレート(プリレンダリング)する必要がある。
Nuxt.js で言えば nuxt generate
、Next.js で言えば next export
が該当する。
Sapperでは sapper export
がそれに該当する。
問題点 : ページ遷移時にAPIリクエストが走る
※エラーハンドリングは省略
<script context="module">
export async function preload() {
const res = await this.fetch('https://your-api-endpoint-here.com/');
const data = await res.json();
if (res.status === 200) {
return { post: data };
}
}
</script>
<script>
export let post;
</script>
{@html post.content}
上記のような svelte を sapper export
し、トップページにアクセス後にこの svelte に対応するページ( /example )に aタグ遷移してみると、クライアントサイドから API サーバーへの fetch が発生してしまう。
すなわちpreload
関数( Nuxt.js でいう asyncData
/ Next.js でいうgetInitialProps
)がそのままクライアントサイドで実行される。
※ Sapper では aタグはデフォルトで History API を使った遷移になる
APIサーバーがあまり強くないときや、リミットが設けられている等、この仕様はいただけない、完全静的にしたいと感じた時に次の方法で解決できる。
解決策 : API 通信にプロキシを用意する
this.fetch
の通信先をいきなり API サーバーにするのではなく、一旦ローカルに向ける。
<script context="module">
export async function preload() {
//ここでいきなり https://~~ ではなく、ローカルを参照する
const res = await this.fetch('api.json');
const data = await res.json();
if (res.status === 200) {
return { post: data };
}
}
</script>
そしてこのローカルに向けた先に
import fetch from 'node-fetch'
export async function get(req, res) {
fetch(`https://your-api-endpoint-here.com/`)
.then(response => response.json())
.then(json => {
res.writeHead(200, {
'Content-Type': 'application/json'
});
res.end(JSON.stringify(json))
})
}
と、APIサーバーと通信して内容を返してくれる js を設置する。
(通信ライブラリに node-fetch を使用しているが、似たようなものであれば何でも良いと思う)
この状態で sapper export
すると、APIサーバーからのレスポンス内容が静的ファイル( json )としてしっかり書き出され、ページ遷移時もこのローカル json を参照するようになる。
#なぜ
sapper export
したときのメカニズム
Docs • Sapper #How it works
-
sapper build
して出来上がったプロダクションファイル(この段階ではまだ SSR )をクロールし、ルートから全てのaタグを巡ってキャプチャーし、静的ファイルにする。 -
preload
関数中のthis.fetch
の先すらもキャプチャーして静的ファイルにする。
If preload is calling this.fetch and that's getting a file at a local path, that response will also be saved when export is crawling the site. If the data is coming from a remote server, you can make a local endpoint that proxies to the remote endpoint, and have this.fetch request that.
静的ファイルにするのはローカルパスのみ。
なので this.fetch
にリモートパスを直接書くのではなく、一旦ローカルパスにして、プロキシすると、プロキシした内容をキャプチャーして静的ファイルが生成される、という仕組み。
#☕
この Svelte / Sapper は Vue / Nuxt をやった事がある人ならかなりとっつきやすいと感じました。
学習面で言えば公式チュートリアルが充実しているのも魅力です。
個人的に Sapper の aタグを力技で全力キャプチャーする仕組みは気に入りました。
ルート指定しなくてもいいので楽ですね。
$Thank$ $You$.