アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

SvelteでWeb Componentsを作ってみた

morishitaです。

前回のエントリでフロントエンドフレームワーク Svelte を紹介しました。

tech.actindi.net

今回は Svelte で Web Components を作ってみようと思います。

Svelte 入門に書いた Typescript, Pug, Sass を使えるようにした状態を前提とします。

Web Components のための設定

Svelte コンポーネントを Web Components としてビルドするにはrollup.config.js の svelte の設定に追加が必要です。

以下に rollup.config.js の抜粋を示しますが、'<== 追加' の行を追加します。

export default {
  // 〜 略 〜
  plugins: [
    svelte({
      preprocess: sveltePreprocess({ sourceMap: !production }),
      compilerOptions: {
        customElement: true,  // <== 追加
        // enable run-time checks when not in production
        dev: !production,
      },
    }),
    // 〜 略 〜 
  ],
  // 〜 略 〜
};

これで Web Components がビルドできるようになります。

App.svelte をWeb Components化してみる

以前、Vue.jsでWeb Componentを作ってみた というエントリが書きました。 そのときと同様にSvelteのデフォルトテンプレートにあるコンポーネント App.svelte を Web Component 化してみます。

そのためにちょっとだけ変更が必要です。 次のように App.svelte ファイルの先頭に <svelte:options> タグを追加します。

<svelte:options tag="my-hello" immutable="{true}"/> <!-- この行を追加 -->
<script lang="ts">
   export let name: string;
</script>

<template lang='pug'>
  main
    h1 Hello {name}!
    p
      | Visit the
      a(href="https://svelte.dev/tutorial") Svelte tutorial
      | to learn how to build Svelte apps.
</template>

<style lang='sass'>
    main
        text-align: center
        padding: 1em
        max-width: 240px
        margin: 0 auto

    h1
        color: #ff3e00
        text-transform: uppercase
        font-size: 4em
        font-weight: 100

    @media (min-width: 640px)
       main
           max-width: none
</style>

<svelte:options> はコンポーネントのコンパイルオプションを指定するためのもので tag 属性でカスタムタグ名を指定します。 Web Components のカスタムタグ名は - (ハイフン) を含む必要があるのでここでは、my-hello としておきます。

で、このコンポーネントを次のコマンドでコンパイルします。

> yarn build

この結果、public/build/bundle.js ファイルが生成されます。 ちなみに yarn build で出力されるファイルはミニファイされています。

このコンポーネントを表示するための HTML ファイルは次のようになります。これを demo.html として保存します。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset='utf-8'>
   <meta name='viewport' content='width=device-width,initial-scale=1'>
   <title>Svelte app</title>
   <link rel='stylesheet' href='./global.css'>
   <script defer src='./build/bundle.js'></script>  <!-- ① -->
</head>
<body>
  <my-hello name="world"></my-hello> <!-- ② -->
</body>
</html>

重要なのは <!-- ① --><!-- ② --> とコメントしている2行です。 ①でコンパイルした JavaScript のコードを読み込んでいます。 そして②でカスタムタグを使っています。

もう1つのポイントは Vue による実装だと、ランタイムライブラリも読み込む必要があったのに対して、Svelte ではその必要がないということです。

表示結果は次のとおりです。

f:id:HeRo:20210405010052p:plain
App.svelte

表示結果は前回のエントリで紹介したものと同じですが、URL バーの URL を見てみてください。 Dev サーバ(http://localhost:5000)で動いているのではなく先程の demo.html を表示しています。

実際にブラウザで見てみたい方はこちらで試せます => Svelte app

Popup を Svelte で実装して Web Component 化してみる

次に Vue.jsでWeb Componentを作ってみた で実装したのと同じ Popup タグを実装してみます。

Svelte コンポーネントのコードは次のとおりです。カスタムタグの名前は svelte-popup としています。

<svelte:options tag="svelte-popup" immutable="{true}"/>
<template lang='pug'>
  div
    +if('isOpen')
      #popup(on:click="{close}")
        .dialog
          .title {title}
          .content {content}
    button(on:click="{open}") OPEN
</template>

<script lang="ts">
  export let title: string;
  export let content: string;
  let isOpen: boolean = false;

  function open() {
    isOpen = true;
  }

  function close() {
    isOpen = false;
  }
</script>

<style lang="sass">
  #popup
    position: fixed
    background-color: rgba(0,0,0,0.3)
    width: 100vw
    height: 100vh
    top: 0
    left: 0

  .dialog
    background-color: white
    width: 80vw
    margin: 100px auto 0 auto
    padding: 10px
    border-radius: 5px

  .title
    font-weight: bold
</style>

これを表示した様子は次のとおりです。 こちらで試せます。 => Web Components - Svelte Popup

f:id:HeRo:20210405010130p:plain
Popup タグ

先に示したコードは Vue.jsでWeb Componentを作ってみた で示した Vue による実装になるべく合わせたのでほとんど同じコードになりました。 Pug 内での if による表示の制御方法が違っているのと、 Svelte ではコンポーネントをクラスとして実装していない部分が主な違いです1

仕様が簡単でシンプルなので差がつきにくいですが、Svelte による実装の方が少しだけコード量が少なくなりました。

ビルド後の JavaScript ファイルですが、 Svelte による実装ではミニファイした状態で 5KB でした。一方、 Vue による実装では 12KB になりました。Vue ではランタイムも必要になるのでビルド後のフットプリントまで比べると Svelte が有利ですね2

まとめ

Svelte でも簡単に Web Components を実装できました。 やはりすべてが1ファイルに収まるこの形式はコンポーネントの実装には便利でわかりやすいと思います。もっともコード量が増えて来きたら分割したくなるだろうとは思いますが。

Vue よりも少ないコードで実装でき、ビルド後のフットプリントも小さくなりました。 Web Components だと JavaScript がコンポーネント内のスコープに封じ込められます。 そのため JQuery などで実装したレガシーなスクリプトと混在させてもお互いに干渉せず導入しやすいのではと思っています。

Svelte は活発に開発も続いているので、フロントエンドフレームワークの選定候補として検討してもいいかなと思いました。 しかもIntegrating Frameworks with Webpacker によると Webpacker は Svelte をサポートしているようです。まだ試してないですが、Rails への導入も難しくなさそうです。

最後に

アクトインディではエンジニアを募集しています。 actindi.net


  1. Svelteでは継承による機能拡張は好まないのでクラスとして実装しないようです。代わりにコンポーネントを複数のコンポーネントで構成することにより機能を実現するほうが良いという思想があるようです。

  2. vue.min.js@2.6.12 で約 33KB です。