NuxtユーザがSapper(svelte)に入門してみた

※この記事は、以下のバージョンに基づき作成されています。

```
$ node -v
v12.18.3
$ npm -v
6.10.3
$ npx -v
6.14.10
$ json-server -v
0.16.3
```

この記事は[ウェブクルー Advent Calendar 2020](https://qiita.com/advent-calendar/2020/webcrew) の21日目の記事です。

Sapperは、フロントエンドフレームワークSvelteを拡張した、サーバーサイドレンダリングをするアプリケーションや静的コードジェネレータを開発するのに必要な設定が予め梱包されているフレームワークです。ReactでいうところのNext.jsやVue.jsにおけるNuxt.jsのようなポジションのフレームワークになります。公式では、"Sapper is a Next.js-style framework"と謳っています。ちなみに、Sapperの名前の由来は、"Svelte APP makER"から来ています。

最近はNuxtで仕事をすることもあるのですが、SvelteはVueと比べても文法や記法がかなりシンプルな点が良いなと思います。かつVueに文法も近いので、VueユーザであればSvelteは学習コストが低めでキャッチアップしやすいのかなという感じです。また、Svelte自体は、フレームワークというよりはどちらかと言うとコンパイラと言った方が正しくビルド時にplainなJavaScriptコードを吐きます。なので、ある程度ビルド時にエラーが発見出来るかも知れないという期待感や、ReactやVueよりも良いベンチマーク結果が出ていたりと、個人的には今後使っていきたいな、と思っているフレームワークの1つです。

## Sapperのプロジェクトを作成する

Sapperのプロジェクトは以下のコマンドでプロジェクト作成、必要なパッケージのインストール、開発サーバを立ち上げられます。

```
$ npx degit 'sveltejs/sapper-template#webpack' <プロジェクト名>
$ cd <プロジェクト名>
$ npm install
$ npm run dev
```

npm run devすると、以下の様な感じでデフォルトのポート番号3000で立ち上がります。

```
$ npm run dev

> TODO@0.0.1 dev /Users/xxx/workspace/simple_bcc
> sapper dev

✔ server (3.9s)
✔ client (4.1s)
> Listening on http://localhost:3000
✔ service worker (327ms)
```

立ち上げてブラウザで表示すると以下のような感じでブラウザに表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211867/5c479b11-89c6-19bd-fd20-6598aa59fafe.png)


かわいいw もうすでにブログが出来てしまっています。

### Nuxtとディレクトリ構造を比較する

プロジェクトですが、以下のようなディレクトリ構造でした。

```
<プロジェクトRoot>.
├── README.md
├── node_modules (※省略)
├── package-lock.json
├── package.json
├── scripts
│   └── setupTypeScript.js
├── src
│   ├── ambient.d.ts
│   ├── client.js
│   ├── components
│   │   └── Nav.svelte
│   ├── node_modules(※省略)
│   ├── routes
│   │   ├── _error.svelte
│   │   ├── _layout.svelte
│   │   ├── about.svelte
│   │   ├── blog
│   │   │   ├── [slug].json.js
│   │   │   ├── [slug].svelte
│   │   │   ├── _posts.js
│   │   │   ├── index.json.js
│   │   │   └── index.svelte
│   │   └── index.svelte
│   ├── server.js
│   ├── service-worker.js
│   └── template.html
├── static
│   ├── favicon.png
│   ├── global.css
│   ├── logo-192.png
│   ├── logo-512.png
│   └── manifest.json
└── webpack.config.js
```

routesがNuxtのpages相当になるようですね。ビルドすると`__sapper__`というディレクトリができており、ここにビルドした内容が出力されるようです。routes内は、**.svelteファイルと、**.jsファイルがあり、それぞれ該当のエンドポイントにアクセスすると、svelteのテンプレートが生成されたり、jsが実行されたり、という事のようです。componentsやstaticディレクトリはNuxtのディレクトリと同じ役割のディレクトリのようです。
server.jsがサーバーサイドの処理のエントリポイントのようで、コードを読むとデフォルトで、[polka](https://github.com/lukeed/polka)というWebサーバーが設定されています。

## fetch

クライアントサイドレンダリングであればfetchはデフォルトで呼び出し可能ですが、サーバーサイドでのレンダリングのうち、jsファイルにはfetchが用意されていないのでnode-fetchを利用する必要があるようです。svelteファイルの場合は、thisにfetchがデフォルトで実装されているので、それをそのまま利用できます。

```
<h1> BCC </h1>

{#each posts as post}
  <div>
    <div class="post_user"> 投稿者: { post.user_name } (投稿日: {post.posted_at})</div> <hr>
    <div class="comment"> { post.comment } </div>
  </div>
  <br>
{/each}

<script context="module">
  export async function preload({}) {
    const response = await this.fetch("bcc.json");
    const contents = await response.json();

    if (response.status !== 200) {
      this.error(status, "エラーが発生しました。")
    }
    console.log(contents.posts)
    return contents
  }
</script>
<script>
  export let posts
</script>
<style>
  .post_user {
    font-size: 80%;
  }
  .comment {
    font-size: 120%;
  }
</style>
```

こんな感じですね。


一方のサーバーサイドのfetchは、node-fetchをインストールして、

```
$ npm install --save node-fetch
```

api.js 等の実装を別途を用意して、processがクライアントサイドかサーバーサイドかで、分けるような処理を用意する必要があります。(api.jsのファイルの位置ですが、どこに設置するのが正解なのか分かっていないのですが、real-worldではsrc内のnode_modulesに設置されていましたので、そのやり方を踏襲しました。)

```
const fetch = process.client ? window.fetch : require('node-fetch').default
export default fetch
```

で、<プロジェクト名>/src/routes/hogehoge.json.js というファイルを配置すると、

```
import fetch from 'api.js';

export async function get(req, res, next) {
  const result = await fetch("http://localhost:3001/posts", { method: "GET" })
  console.log(result)
  const body = await result.json()
  res.writeHead(200,  {
			'Content-Type': 'application/json'
	});
  res.end(JSON.stringify({ posts: body }))
}
```

localhost:3000/hogehoge.jsonへGETリクエストを飛ばすとfetchでAPI呼び出しした結果が返ってきます。

このあたりのクライアントサイドとサーバーサイドの処理の紛らわしさはNuxt同様難しいですね。。。

## 感想

### 軽く触ってみた課題感
私の調査不足かも知れないですが、実務で使用するにあたって、以下の点が少し気になりました。(2020/12/21時点)

* axios/proxy的なライブラリが欲しい[^1]
* PC/SPでの出し分け(Dynamic Serving)をしたい。[^2]
* ~~IE対応[^3]~~ ※IE対応に関しては、[legacyオプション](https://sapper.svelte.dev/docs/#Browser_support)により古いブラウザに対応したコードを吐き出してくれるようです。 @myLifeAsaDog さんに教えていただきました。(コメント欄参照、2020/12/22 16時追記)
* Nuxtと比較するとどうしてもWeb上の資料(公式以外のドキュメント)が少ない。(まぁ、これは後発なので仕方ないかなという気もしますが。。。)

### 良かった点
* ローカル開発環境を構築するのが超楽。
* Svelteのコードが書ける。
* CoC(Convention over Configuration)な設計思想を引き継いでいる。
* 公式のドキュメントはある程度整っている。
* Nuxt/Vue使ってると入門するハードルが非常に低い。

## 終わりに

Vue.jsよりも更にシンプルなので、html/cssがメインでコーディングされてきた方が最初に入門するJSフレームワークとしては触りやすくて良いのかな、という感じです。
特に複雑なローカル開発環境のセットアップをしなくてもSapper側で殆ど準備してくれる点も良いですね。

## 参考文献
* [Svelte・Sapper で Hello World を行う | Noob Front End Engineer Blog](https://noob-front-end-engineer-blog.com/svelte-sapper-starter/)
* [JavaScript UIコンパイラ：SvelteとSolidの比較](https://ichi.pro/post/224075457891562)
* [sveltejs/realworld: Svelte/Sapper implementation of the RealWorld app](https://github.com/sveltejs/realworld)

[^1]: polkaの実装を修正すれば対応できるようで、こちら[polka sapper api proxy example](https://gist.github.com/tcrowe/c9a11fca78447ba78c5de2e7b9ba10d2)にサンプルがあります。ただ、やはり自前実装よりもライブラリとして実装したいですね。。。
[^2]: これも上記同様、polkaのカスタマイズで対応可能なのかも知れません。Nuxtだとこちらの記事、[Nuxt のルーティングを PC とスマートフォンで切り替える | ymmooot blog](https://ymmooot.dev/articles/3/)を参考にしてPC/SP出し訳をやりました。
[^3]: まぁ、今どきIEかよって話ではあるのですが、仕事だとまだ対応が必要になる事も多々あり。。。Nuxtのmodernプロパティは良かったです。殆どのIE周りのトラブルを回避できました。