6
2

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.

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

Last updated at Posted at 2020-12-21

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

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

この記事はウェブクルー Advent Calendar 2020 の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

かわいい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という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オプションにより古いブラウザに対応したコードを吐き出してくれるようです。 @myLifeAsaDog さんに教えていただきました。(コメント欄参照、2020/12/22 16時追記)
  • Nuxtと比較するとどうしてもWeb上の資料(公式以外のドキュメント)が少ない。(まぁ、これは後発なので仕方ないかなという気もしますが。。。)

良かった点

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

終わりに

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

参考文献

  1. polkaの実装を修正すれば対応できるようで、こちらpolka sapper api proxy exampleにサンプルがあります。ただ、やはり自前実装よりもライブラリとして実装したいですね。。。

  2. これも上記同様、polkaのカスタマイズで対応可能なのかも知れません。Nuxtだとこちらの記事、Nuxt のルーティングを PC とスマートフォンで切り替える | ymmooot blogを参考にしてPC/SP出し訳をやりました。

  3. まぁ、今どきIEかよって話ではあるのですが、仕事だとまだ対応が必要になる事も多々あり。。。Nuxtのmodernプロパティは良かったです。殆どのIE周りのトラブルを回避できました。

6
2
2

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
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?