株式会社HAMWORKS
🤖

先取り Movable Type 8 で採用予定の Svelte.js を使って Web Component を作ってみた

2022/12/09に公開

この記事は Movable Type Advent Calendar 2022 の9日目の記事です。

Movable Type 8 で採用予定のSvelte.jsとは?

来年秋にリリース予定のMovable Type 8にリリースを予定しています。
このリリースに関しての概要が公開されています。

この中で自分が注目してるのが Riot.js → Svelte.jsの変更です。

Riot.js → Svelte.js

Movable Type 7では、リスティングやコンテンツタイプの編集画面にRiot.jsを利用しています。この部分がMovable Type 8から Svelte.jsを利用するように変更されるようです。

Riot.js 利用部分

ReactやVueではなく、Svelte.jsを採用した理由をいくつか考察と今回ブログを書くにあたって、簡単なSvelte.jsのComponentを作ってそれをPluginで呼び出すようなことをやりました。

Svelte.jsとは

Svelte.jsとは、ブラウザ上で動かくUIを構築できるJavaScriptのライブラリです。
比較されるフレームワークとしては、ReactやVue等があります。

大きな違いは、仮想DOMを利用していない点になります。
基本的にSvelteのComponentは、Web Componentになります。

VueやReactといった宣言的にUIを似たように記述することができます。
Svelteは、独自で書かれた記法をコンパイル時に 素のJSやCSSに変換してくれます。
このあたりが仮想DOMのフレームワークとは異なる点ではないでしょうか。

他のフレームワークと異なるアプローチでレンダリングを行います。

Svelte.jsの 思想は 重要なのは読みやすさである という点から不要なコードを削減し読みやすさに特化したフレームワークのようです。

コンポーネント単位で、コード量などを少なく書くことができるようになっています。

ほかのフレームワークとは異なりお作法もJavaScriptをそのまま書くことで動かすことが可能です。

<script>
let count = 0;

function increment() {
    count += 1;
}
</script>

<svelte:options tag="my-counter" />
<button on:click="{increment}">Count Up</button>

<p>{count}</p>

このようにカウントアップの処理を行うコンポーネントではこのような書き方で画面のバインディング(画面の更新)を行うことができます。

記法はVueに近い形になっていることから、Vueを書いたことがある人にはとっつきやすさはあります。

No. virtual DOM

ReactやVueなどのフレームワークは仮想DOMを採用しています。

仮想DOMの作業はブラウザが行っています。
ReactやVueでは、ブラウザが読み込む時間など大きければパフォーマンスの低下に繋がります。

Svelte.jsでは、仮想DOMを利用していません。
Svelte.jsはコンパイラで実行して、コードを小さなJavaScriptとしてコンパイルを行います。

そのため、実行負荷の軽減やパフォーマンスを維持することができます。

ほかにもたくさんメリットはあるようです。

Movable Type 8 でなぜ採用したか

Svelte.jsは、他のフレームワークとは異なりCDNのような読み込みで自由に使えることが出来ません。

作成したコンポーネントをビルドを行い、ブラウザで読み込める Web Componentとして提供します。

例えば、先程の Counter.svelte でビルドしたあとに HTMLでは、 <my-counter /> このように利用することができます。

Movable Typeでは .tmplalt-tmpl といったMTMLを利用できる形で、画面が作られています。

この形は、従来どおりで維持しつつ簡易的に画面のバインディング(画面の更新)を、提供するWeb Componentで利用したいという意図から採用されたのではないでしょうか。

また、Svelteはコード自体が、展開されると素のJSやCSSになります。
そのため軽量で高パフォーマンスに維持できるという点でしょうか。

バンドルサイズやメモリ消費も最小で済むため、Movable Type自体をコードへの影響範囲(速度)が最小限に済むための採用理由だったのかなと個人的に思います。

このようにビルドを行う手間がある反面、Componentとして独立したものを提供することから、Movable Type 8では、組み込みやすさなどComponentの分離で実装を行う形で提供していくのではないかなと個人的に思っています。

Movable Type 7の環境でSvelte.jsを利用してみた

Movable Type 8では、どのような形で利用してくのかは今後で変わってきますが、基本的にSvelte.jsはMovable Type 7の環境にも組み込むことが可能です。

今回のブログ用にサンプルコードとそれを扱うためのPluginとして書いてみました。

mt-plugin-SvelteComponent

まずはじめに、出力したSvelte.jsのComponentを呼び出すためのプラグインを用意しました。

ここでの処理は単純に、設定画面にSvelte.jsで出力されたjsファイルを読み込むだけの処理を行っています。

どこで利用したいかなどは、プラグインの組み込み部分次第と言った感じです。
サンプルとして、今回は設定部分に組み込んでいます。

Svelte.jsで吐き出されたJSファイル js/svelte-component.min.js を読み込むCallbacksを用意したのみです。

package SvelteComponent::Callbacks;

use strict;
use warnings;
use utf8;
use MT::Util;

sub plugin {
    return MT->component('SvelteComponent');
}

sub template_source_header {
    my ( $cb, $app, $tmpl_ref ) = @_;
    my $app_vars;
    my $p         = plugin();
    my $static_path        = $app->static_path;
    my $static_plugin_path = $static_path . $p->envelope . '/';
    $app_vars->{static_path}        = $static_path;
    $app_vars->{static_plugin_path} = $static_plugin_path;

    $$tmpl_ref =~ s!(<head[^>]*>)!$1\n!;

    my $svelte_component_url = $static_plugin_path . 'js/svelte-component.min.js';

    my $out = <<__EOD__;

    <script type="module" crossorigin src="$svelte_component_url"></script>
__EOD__

    $$tmpl_ref =~ s!</head>!$out</head>!g;
}

Svelte.jsの設定画面

それぞれ独立したコードを svelte_component.tmpl に差し込むように設定画面に配置しています。

<div class="mb-2">
    <release-note />
</div>
<div>
  <my-text />
</div>

今回のコンポーネントでは、 <release-note /> で単純なテキストを配置したコンポーネントと <my-text /> で書いたマークダウンのテキストを下の 本文の入力 で表示するシンプルなコンポーネントになります。

Svelte.jsの設定画面

svelte-components

svelte-components では、コンポーネントを提供するリポジトリになります。

ここで好きなコンポーネントを作りビルドを行い Plugin側の mt-staticへ展開しました。

このあたりは、NPMとか公開にしておけばCDNで展開できたりするので、場合によってCDNで提供でもよいのかなと思っています。

今回は単純に配置しているだけの対応です。

リポジトリをクローンして、package.jsonで npm run dev することで 配置している index.html が描画を行い読み込んでるコンポーネントを表示できます。

Svelte.jsのコンポーネントリスト

最終的な成果物を npm run build -- -c=vite.config.js./dist/assets/index.19f174c3.js にコンポーネントの情報が含まれたJSファイルが生成されます。

Svelte.jsのコンポーネントリスト

この出力された乱数なファイル名を 識別できる svelte-component.min.js に変更してMovable TypeのPlugin側に定義しています。

Svelte.jsを利用するあたって、環境構築は簡単にはじめられます。

Node環境があれば vite を実行することで Select a framework の中に Svelte が入っています。

とくに設定不要で始めることができます。

npm init vite

✔ Project name: · svelte-web-components
✔ Select a framework: · svelte
✔ Select a variant: · svelte-ts

package.jsonの起動スクリプトは以下が提供されます。

  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "check": "svelte-check --tsconfig ./tsconfig.json"
  },
  • npm run dev で現在のアプリケーションを見れます
  • index.htmlに配置したコンポーネントを呼び出すことができます
  • npm run build -- -c=vite.config.js でビルドを行います。

ビルド時には以下のvite.config.jsに設定が必要です。

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    svelte({
      compilerOptions: {
        customElement: true,
      },
    }),
  ],
});

customElementを trueにすることで、コンポーネントをタグで提供できるようになります。

詳しいインストールや手順は参考サイトを観ていただければと思います。

今後どうなっていくか

今回の方法は、単純に出力されたコンポーネントを呼び出しただけの簡易的なものです。

Movable Type 8ではどのような画面になっていくのか次第ですが、コンテンツフィールドにオリジナルのコンポーネントを用意したフィールドなんかも作れたりすのではないでしょうか。(保存データはJSONかもしれません。。)

ということで、少し先取りしてみた記事になります。

コンポーネント開発とか興味ある方は参考にしてみてはいかがでしょうか。

株式会社HAMWORKS
株式会社HAMWORKS

Discussion