microCMS

microCMS + Svelte + Sapper + TypeScriptでJamstackブログを作ってみよう

チュートリアル
2021/01/19 柴田 和祈

本記事はブログ作成チュートリアルのSvelte + Sapper版になります。

先日発表されたStateOfJS 2020によると、Svelteがフロントエンドフレームワークの満足度ランキング首位となっていたので注目されている方も多いのではないでしょうか。

今回はSvelteとSapperというフレームワークを用いてブログを作成していきます。
ざっくりな説明としては、Reactに対してNext、Vueに対してNuxt、Svelteに対してSapperというような関係性です。

Svelteとは

ビューを生成するライブラリです。
ReactやVueと異なる点は、ライブラリのコードをほとんど含まないVanilla JSに近い形でコンパイルされるという点です。
そのためコンパイル後のJSファイルのサイズがかなり小さく、高いパフォーマンスが期待できます。
「ライブラリのコードをほとんど含まない」ことを実現するために、SvelteではVirtual DOMを敢えて採用せず、代わりにコンポーネントを非常に効率的な命令型コードに変換して、DOMをピンポイントに更新します。
ブログやメディアなど、ブラウザ上での複雑な処理が多くない場合は確かにVirtual DOMはオーバースペックかもしれません。
詳しくは公式のこちらの記事をご覧ください。

Sapperとは

Svelteと組み合わせて使うアプリケーションフレームワークです。
NextやNuxtのように柔軟なファイルシステムによるルーティングやSSR, SSGをサポートしています。
注意点として、公式からは今後はSapperでなくSvelteKitの利用が推奨されています。
まだSvelteKitは安定版ではないので、今のところはSapperで開発しつつ、SvelteKitが正式リリースされたタイミングで移行するのがベストと思われます。
公式の記事によると、Sapperからの移行はそんなに大変なものではなく移行ガイドも用意されるとのことです。

さて、今回は公式が用意してくれているテンプレートを使ってブログを作成していきます。
このテンプレートは元々ブログ機能を含んでいるので、主な作業としては次の2点だけです。

  • TypeScript化
  • microCMSとの連携


それでは順に作業していきましょう。

0. 前提

下記のバージョンで開発を行っています。バージョンの差異によって若干機能が異なる可能性があります。

  • Svelte 3.31.2
  • Sapper 0.28.10


1. Sapperプロジェクトを用意する

まずは公式が用意してくれているテンプレートを用いてプロジェクトを作成します。
Rollup版とWebpack版がありますが、今回はRollup版で進めていきます。

$ npx degit "sveltejs/sapper-template#rollup" my-app
$ cd my-app
$ npm install
$ npm run dev & open http://localhost:3000

上記の作業を行うと次のようにアプリケーションが立ち上がります。

すでにhome、about、blogのページが用意されているので、今回はblogの部分を拡張していきます。

2. TypeScript化する

TypeScriptをセットアップするスクリプトが用意されているのでそれを実行します。

$ node scripts/setupTypeScript.js

新たにパッケージが追加されているので再度パッケージのインストールを行います。

$ npm install

一度ソースコードを見てみると、client.tsserver.tsなどのファイルがTypeScript化されていることが確認できると思います。

VSCodeをお使いの場合、プラグインが用意されているのでインストールしておきましょう。

3. microCMSの用意をする

次に、microCMSでAPIを作成していきます。手順は下記に用意しました。詳細については、microCMSのドキュメントを参照してください。

アカウント登録
ログイン
サービスの作成


サービスIDは一度設定すると変更ができないので、慎重に決めましょう。

APIの作成

それでは、ブログ用のAPIを作成していきます。API名にブログ、エンドポイントにblogを入力してください。

そして、リスト形式を選択します。

タイトルと本文のフィールドを用意します。APIスキーマは以下の通りです。

コンテンツの作成

適当に内容を入力し、公開します。
コンテンツ一覧画面に戻り、画面右上のAPIプレビューをクリックします。
取得ボタンをクリックし、入力内容がAPI経由で取得できるか確認してください(レスポンスJSONが表示されます)。

4. APIキーを環境変数で保護

microCMSではリクエストにAPIキーを含める事で特定のデータを取得できます。
このAPIキーが、GitHubでパブリックに公開されてしまうのはセキュリティ面でよくありません。
そこで環境変数を用いてAPIキーを保護してあげましょう。

環境変数を用いるためにまずは下記パッケージをインストールします。

$ npm install -D dotenv

.envファイルにAPIキーを記載します。

API_KEY=xxxxxxxxxxxx

.envに記載した変数はプロジェクト内で下記のように参照することができます。

process.env.API_KEY

.env.gitignoreに追加して、git管理しないようにしてください。

次にrollup.config.jsを開き、下記のように設定をします。

import dotenv from 'dotenv'
// 略
dotenv.config();
const apiKey = process.env.API_KEY
// 略

export default {
  client: {
    // 略
    plugins: [
      replace({
        'process.browser': true,
        'process.env.NODE_ENV': JSON.stringify(mode),
        'process.env.API_KEY': JSON.stringify(apiKey)  // ココを追加
      }),
    ]
  },
  server: {
    // 略
    plugins: [
      replace({
        'process.browser': false,
        'process.env.NODE_ENV': JSON.stringify(mode),
        'process.env.API_KEY': JSON.stringify(apiKey)  // ココを追加
      }),
    ]
  }
}


5. ブログ一覧を表示する

デフォルトではsrc/routes/blog/_posts.jsのデータがブログ一覧画面に表示されています。
今回はmicroCMSで用意したデータを表示できるようにしていきます。

microCMSからデータを取得するために非同期通信用のパッケージを追加します。
Sapperではサーバーサイドとクライアントサイドの両方で通信処理を行う必要があるので、今回はisomorphic-unfetchというパッケージを利用します。

$ npm install -S isomorphic-unfetch

まずはsrc/routes/blog/index.json.jsを次のように書き換えましょう。
※ your-service-id部分はmicroCMSで作成したサービスIDに直してください

import fetch from 'isomorphic-unfetch';

export async function get(_, res) {
  fetch(`https://your-service-id.microcms.io/api/v1/blog/`, {
    headers: { "X-API-KEY" : process.env.API_KEY }
  })
    .then(res => res.json())
    .then(json => {
      res.writeHead(200, {
          'Content-Type': 'application/json'
      });
      res.end(JSON.stringify(json))
    });
}

次にsrc/routes/blog/index.svelteのデータ取得部分を変更します。

<script context="module" lang="ts">
  export function preload() {
    return this.fetch('blog.json').then(res => res.json())
  }
</script>

<script lang="ts">
  export let contents: { id: string; title: string, html: HTMLElement }[];
</script>

<style>
  ul {
    margin: 0 0 1em 0;
    line-height: 1.5;
  }
</style>

<svelte:head>
  <title>Blog</title>
</svelte:head>

<h1>Recent posts</h1>

<ul>
  {#each contents as content}
    <li><a rel="prefetch" href="blog/{content.id}">{content.title}</a></li>
  {/each}
</ul>

ここまでで一覧画面が表示されるようになったはずです。

ちなみにsrc/routes/blog/index.svelteで直接microCMSに通信することも可能ですが、ページ遷移時にもmicroCMSに通信が行われてしまいます。
src/routes/blog/index.json.js経由でmicroCMSとの通信を行うことで、SSG時に通信結果のレスポンスもすべて静的化することができます。

6. ブログ詳細を表示する

次にブログ詳細画面を実装します。
一覧画面で行ったことと同様のことを詳細画面にも適用していきます。
まずはsrc/routes/blog/[slug].json.jsを次のように書き換えましょう。
※ your-service-id部分はmicroCMSで作成したサービスIDに直してください

import fetch from 'isomorphic-unfetch';

export async function get(req, res) {
  const { slug } = req.params;
  fetch(`https://your-service-id.microcms.io/api/v1/blog/${slug}`, {
    headers: { "X-API-KEY" : process.env.API_KEY }
  })
    .then(res => res.json())
    .then(json => {
      res.writeHead(200, {
          'Content-Type': 'application/json'
      });
      res.end(JSON.stringify(json))
    });
}

次にsrc/routes/blog/[slug].svelteのデータ取得部分を変更します。

<script context="module" lang="ts">
  export function preload({ params }) {
    return this.fetch(`blog/${params.slug}.json`)
      .then(res => res.json())
      .then(res => ({ post: res }))
  }
</script>

<script lang="ts">
  export let post: { id: string; title: string, html: HTMLElement };
</script>

以上で詳細画面が表示されるようになればOKです。

見た目の装飾

記事の装飾についてはsrc/routes/blog/[slug].svelte内のstyleタグで調整しましょう。
スタイリングしやすいようにSassを導入します。
下記のパッケージをインストールしましょう。

$ npm install -D svelte-preprocess postcss autoprefixer node-sass

次にrollup.config.jsで設定を行います。

import sveltePreprocess from 'svelte-preprocess';
// 略
const preprocess = sveltePreprocess({
  scss: {
    includePaths: ['src'],
  },
  postcss: {
    plugins: [require('autoprefixer')],
  },
});

// 略

export default {
  client: {
    plugins: [
      svelte({
        preprocess,  // ココを編集
        // 略
      }),
  },
  server: {
    plugins: [
      svelte({
        preprocess,  // ココを編集
        // 略
      }),
    ],
  },
};

以上で設定は完了です。
あとは下記のようにstyleタグにlang="scss"を指定することでscssのコンパイルが可能です。

<style lang="scss">
  // ...
</style>


7. ビルドして静的ファイルを生成してみる

今まではずっとnpm run devコマンドで開発をしてきました。これはローカルでWebサーバーが起動され、SSRによるページが表示されている状態です。
Jamstack構成にするためにはnpm run exportコマンドを叩き、ファイルの静的生成をする必要があります。
試しに一度ローカル環境でビルドしてみましょう。

$ npm run export


ビルドに成功すると、__sapper__/exportというディレクトリ内にファイルが出力されます。
次のコマンドを叩くと生成したアプリケーションをローカルで起動できます。

$ npx serve __sapper__/export

問題なく起動できればOKです。
devツールのネットワークタブを開きながらブログ一覧画面と詳細画面を行き来してみると、microCMSへの通信は走らず、静的化されたJSONを取得しにいくことが見て取れると思います。

8. ファイルをホスティングする

今回はホスティングサービスとしてVercelを使用します。
https://vercel.com

まずは、GitHubリポジトリを作成します。その後、今まで作成したデータをpushしましょう。

$ git init
$ git add .
$ git commit -m 'first commit'
$ git remote add origin your-repository  // 自分のリポジトリを入力
$ git push -u origin master


次にVercel上で作成したリポジトリと連携をしていきます。
ダッシュボードの New Project ボタンからリポジトリの連携を行いましょう。
その際にビルドコマンドと環境変数を次のように設定します。(.envファイルはgit管理しないため、Vercel側でも環境変数の設定が必要です)

デプロイが成功すれば、無事Vercelのドメインでアクセス可能になります。

Webhookの設定

現状ではGitHubにソースコードをPushするたびにビルド&デプロイがされますが、microCMSの記事を追加・更新してもビルドはされません。
そこで、microCMSのWebhook機能を使って、記事の追加・更新のたびにVercelでビルド&デプロイがされるように設定します。
Vercelの Settings > Git > Deploy Hooks で新しくWebhookを作成します。
※ブランチ名は main の可能性もあります(自分のブランチをチェックしてください)

生成されたWebhook URLをコピーし、次にmicroCMSの API設定 > Webhook に移動します。
カスタム通知用のWebhookを作成し、先ほどコピーしたURLを入力します。
Webhook通知のタイミングはコンテンツの公開時・更新時・非公開時・削除時で良いでしょう。(自由に設定してください)

以上でWebhook設定は完了です。
試しにmicroCMSで記事を追加や更新をしてみて、Vercel側でビルドが走ればOKです。

9. サムネイル画像を追加する

今はタイトルと本文のみで殺風景なので、サムネイル画像を追加してみましょう。
まずはmicroCMSの API設定 > APIスキーマ で画像フィールドを追加します。

APIスキーマを変更したことで、コンテンツ編集画面に画像フィールドが追加されます。
画像を設定して保存すると、次のようにAPIレスポンスに画像URLが含まれるようになります。

あとはこの画像URLをフロントで表示してあげましょう。
表示させる内容が増えているので、src/routes/blog/[slug].svelteにサムネイル画像用の型定義を追加します。

<script lang="ts">
  interface thumbnail {
    url: string
  }
  export let post: { id: string; title: string, html: HTMLElement, thumbnail: thumbnail };
</script>

そして、タイトルの下に画像表示用のimgタグを設置しましょう。

<h1>{post.title}</h1>
<img src={post.thumbnail && post.thumbnail.url} alt="" />
<div class="content">
  {@html post.html}
</div>


以上で画像の表示は完了です。

OGP画像に指定する

ついでに今回用意したサムネイル画像をOGP画像に設定してみましょう。
Svelteではhead要素の記述も可能なので、下記のようにsrc/routes/blog/[slug].svelteに追加します。

<svelte:head>
  <title>{post.title}</title>
  <meta property="og:type" content="article" />
  <meta property="og:title" content={post.title} />
  <meta property="og:image" content={post.thumbnail && post.thumbnail.url} />
</svelte:head>

metaタグに関してはその他自由に設定してください。

おわりに

今回はmicroCMSとSvelte / Sapperを組み合わせてJamstack構成のブログを作成しました。
ここまでのソースコードはmicrocms-sampleリポジトリに置いておりますので参考にしていただければと思います。

実際に作成してみるとバンドルされたJSファイルは大きくても20KB前後で、かなり軽量だと感じました。

GoogleのLighthouseチェックでも読み込むJSファイルのサイズは影響があるので、特にブログやメディアなど静的ファイルがメインで、複雑な処理が少ないサイトでは良い選択肢になるかもしれません。

下書きプレビューの実装方法などは別記事として公開したいと思いますので、しばらくお待ちいただければと思います。

-----

microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitterメールからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!

ABOUT ME

柴田 和祈
microCMSのデザイン、フロントエンド担当 / ex Yahoo / 2児の父 / 著書「React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで 」 / Jamstack

microCMSとは

  1. 開発者、編集者どちらも分かりやすい管理画面

  2. 細かな権限管理や豊富な外部サービス・データ連携

  3. 安心の日本製・日本語でのチャットサポート