Help us understand the problem. What is going on with this article?

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした