👀

SvelteとmicroCMSを用いたCSRでのプレビューページ作成

2022/12/30に公開

ヘッドレス CMS を導入する際、プレビュー環境をどのように用意するべきか迷いますよね。
その際、コンテンツのレンダリング手法は SSR や CSR で実装されるケースが多いかと思います。

API キーの隠蔽などを鑑みると可能な限り、SSR で対応したくなります。ただし、利用する技術構成やインフラ等の事情で CSR で実装しないといけないケースもあるかと思います。この記事では Svelte で CSR を行い、プレビューページを作成する方法についてみていきたいと思います。

各レンダリング手法の違いは以下記事を参照いただくのが分かりやすいかと思います。
https://zenn.dev/bitarts/articles/37260ddb28ae5d

前提条件

基本的に Svelte と microCMS がメインの内容となりますが、この記事では Pure Liquid という制作テンプレートを用いた事例で紹介します。「Svelte X microCMS」といっても構成はさまざまかと思いますので、ご自身の環境に合わせ読み替えていただけると幸いです。

https://pure-liquid-dev.netlify.app/
https://zenn.dev/kazuki_tam/articles/98c9b1fc18f36d

microCMS のプレビュー設定

プレビューページの設定方法として microCMS のドキュメントでは以下 2 つの方法が紹介されています。

  • draftKey パラメーターを利用する方式
  • API キーに「下書き全取得」権限を設定する方式

https://help.microcms.io/ja/knowledge/how-to-implement-preview

今回は「draftKey パラメーターを利用する方式」で実装を進めたいと思います。

まずは下書き中のコンテンツを準備しましょう。コンテンツが下書き中ステータスの場合、draftKey が microCMS 上で発行されます。この draftKey を API リクエスト時にパラメーターとして付与することで下書き状態のコンテンツを取得できます。

microCMS下書き

microCMS 管理画面の API 設定から画面プレビューを展開してみましょう。以下画面でプレビューボタン押下時の遷移先を制御できます。

コンテンツ ID や draftKey{CONTENT_ID}{DRAFT_KEY}といった文字列で動的に置換できます。ブログ記事など複数のコンテンツを 1 つの API で管理する場合は {CONTENT_ID} が必要になるでしょう。

microCMS画面プレビュー

今回はシンプルに {DRAFT_KEY} だけで完結する単体ページでの実装を行なってみます。たとえば以下のような構成でドキュメントページのプレビュー環境を用意したいと想定します。

// 本番用
https://your-domain/docs/

// プレビュー用
https://your-domain/docs/preview/?draftKey={DRAFT_KEY}

「プレビュー用」となっている URL を microCMS の画面プレビュー設定に登録します。

Svelte を用いた CSR

Pure Liquid の場合、https://your-domain/docs/ のページは Liquid と呼ばれるテンプレートエンジンでページが静的にマルっと生成されます。一方、https://your-domain/docs/preview/?draftKey={DRAFT_KEY}の方はレウアウトの枠だけ静的に生成し、コンテンツ部分は Svelte で DOM を生成します。

以下ドキュメントページを microCMS で運用していると想定します。
https://pure-liquid-dev.netlify.app/docs/

その場合、src/docs/ 配下に preview.liquid を作成します。

  • index.liquid(SSG)
  • preview.liquid(CSR)

preview.liquid の記述は以下のようになります。index.liquid 側の記述は割愛します。
ここで注目していただきたいのは、<div class="l-container" id="doc-container"></div> のように空のdivタグが配置されている箇所です。

src/docs/preview.liquid
---
layout: layout/preview-theme.liquid
eleventyComputed:
  title: "{{ doc.title }}"
  description: "{{ doc.description }}"
pagePath: docs/preview/
pageId: docs
breadcrumbs: [{item: "Docs", path: "docs/"}]
eleventyExcludeFromCollections: true
---

<div class="l-container" id="doc-container"></div>

ではコンテンツの出力処理はどこに記述されているかというと、*.svelte ファイルになります。詳しくは後ほど解説します。

Svelte のセットアップ

まずは Svelte が利用できる環境をセットアップしましょう。この記事では webpack を用いたパターンで環境を構築します。

Svelte を webpack、TypeScript 環境で動かすために必要なモジュールをインストールします。

yarn add -D svelte svelte-loader @tsconfig/svelte dotenv-webpack @types/dotenv-webpack

https://www.npmjs.com/package/svelte
https://www.npmjs.com/package/svelte-loader

webpack.config.js の修正

webpack.config.jssvelte-loader の記述を追加します。

module: {
  rules: [
    {
      test: /\.tsx?$/,
      use: 'ts-loader',
    },
    {
      test: /\.(html|svelte)$/,
      use: [
        {
          loader: 'svelte-loader'
        }
      ]
    }
  ],
},

さらに環境変数をクライアントでも利用したいので dotenv-webpack を読み込みます。

const Dotenv = require("dotenv-webpack");

webpack のプラグインに new Dotenv({systemvars: true}) を追加します。

plugins: [
  new Dotenv({systemvars: true})
],

tsconfig.json の修正

tsconfig.json に以下記述を追加します。

"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.svelte", "src/**/*.js"],
"extends": "@tsconfig/svelte/tsconfig.json",

svelte.d.ts の追加

後述する Doc.svelte を読み込むために以下ファイルを追加します。

src/assets/ts/svelte.d.ts
declare module "*.svelte" {
  interface ComponentOptions<Props> {
    target: HTMLElement;
    anchor?: HTMLElement;
    props?: Props;
    hydrate?: boolean;
    intro?: boolean;
  }

  interface Component<Props> {
    new (options: ComponentOptions<Props>): any;
    $set: (props: {}) => any;
    $on: (event: string, callback: (event: CustomEvent) => any) => any;
    $destroy: () => any;
    render: (props?: {}) => {
      html: string;
      css: { code: string; map?: string };
      head?: string;
    };
  }

  const component: Component<{}>;

  export default component;
}

コンポーネントの作成

webpack で読み込むエントリーファイルを作成します。プレビュー用の JavaScript は本番環境で利用されるものとは分けておきたいため、preview.ts というファイル名で作成しました。

src/assets/ts/preview.ts
import Doc from "./preview/Doc.svelte";

const docContainer = document.getElementById("doc-container") as HTMLElement;
const doc = new Doc({
    target: docContainer,
});

export default doc;

ここでは Doc.svelte という名前で Svelte ファイルを作成しています。

src/assets/ts/preview/Doc.svelte
<script lang="ts">
  import queryString from "query-string";
  const MICROCMS_DOMAIN = process.env.MICROCMS_DOMAIN;
  const MICROCMS_API_KEY = process.env.MICROCMS_API_KEY;
  const { draftKey } = queryString.parse(location.search);
  const fetchData = async() => {
    const fetchOptions = {
      method: 'GET',
      headers: {
        "X-MICROCMS-API-KEY": MICROCMS_API_KEY
      }
    }
    const res = await fetch(`${MICROCMS_DOMAIN}/api/v1/doc?draftKey=${draftKey}`, fetchOptions);
    if (res) {
      return await res.json();
    } else {
      throw new Error('Error...');
    }
  };
</script>

<div>
  {#await fetchData()}
    Loading....
  {:then res}
    <h1 class="c-heading--lev1">📖 {res.title}</h1>
    <div class="docs__container">
      {#each res.body as block}
        {#if block.fieldId == "message"}
          <div class="p-message--caution">
            {#if block.title }
              <p class="p-message__title">⚡️ { block.title }</p>
            {/if}
            <p class="p-message__des">{ block.content }</p>
          </div>
        {/if}
        {#if block.fieldId == "body"}
          {@html block.body}
        {/if}
      {/each}
    </div>
  {:catch}
   Error...
  {/await}
</div>

上記大まかな処理の流れは以下になります。

  1. location.searchdraftKey を取得
  2. 取得した draftKey を用いて API リクエストを行う
  3. 取得した情報を Svelte の構文を用いて描画する

https://qiita.com/c-shiraga/items/02c3c8d9e044622380dc#非同期処理を扱う

以上でプレビューページの実装は終了です。microCMS の管理画面で「画面プレビュー」ボタンを押下し、プレビューの状態が表示されれば完了です。

まとめ

今回、Svelte をプレビューページの制作に利用した背景としては、Svelte が「書きやすく、分かりやすい」という点と「コンパイル後のファイルのサイズが小さく、高いパフォーマンスが期待できる」という点にあります。とくに Web 制作のコンテンツプレビューでは Virtual DOM を敢えて利用せずとも実 DOM を更新してくれる Svelte で十分な期待値が得られそうです。

https://svelte.jp/

Discussion