12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Sapper] export で 完全静的サイトを作る [Svelte]

Last updated at Posted at 2019-12-07
Sapper の Hello World のおっちゃん ( Sapper のクセの強い Hello World 画面 )
これから来るとか来ないとか、海外ではもう来てるとか言われている web フロントエンド JS のフレームワーク(コンパイラ)の **Svelte**。 今回はその Svelte を Web アプリケーション制作用途に拡張するフレームワーク、**Sapper** のお話。
{参考}

#静的ジェネレート

SSR メインなフレームワークで作ったサイトを、 Netlify 等の静的ファイルホスティングサービスで運用する、すなわち JAMstack な構成をとりたいとき、全ページを事前に静的ジェネレート(プリレンダリング)する必要がある。
Nuxt.js で言えば nuxt generate 、Next.js で言えば next exportが該当する。

Sapperでは sapper export がそれに該当する。

問題点 : ページ遷移時にAPIリクエストが走る

※エラーハンドリングは省略

/src/routes/example.svelte

<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 サーバーにするのではなく、一旦ローカルに向ける。

/src/routes/example.svelte
<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>

そしてこのローカルに向けた先に

/src/routes/api.json.js

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 を参照するようになる。

api.jsonが存在する

#なぜ

sapper export したときのメカニズム
Docs • Sapper #How it works

  • sapper build して出来上がったプロダクションファイル(この段階ではまだ SSR )をクロールし、ルートから全てのaタグを巡ってキャプチャーし、静的ファイルにする。
  • preload 関数中の this.fetch の先すらもキャプチャーして静的ファイルにする。

###ただし
Is it possible to run preload once for completely static html generate when "sapper export" was used? · Issue #972 · sveltejs/sapper

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$.

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?