LoginSignup
8

More than 3 years have passed since last update.

posted at

updated at

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

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
What you can do with signing up
8