LoginSignup
1
1

More than 1 year has passed since last update.

Reactの代わりにWeb Componentsを使おうとした時の所感

Posted at

こんにちは! エンジニアの きみどりはるか です!
Web Componentsを触ってみたので報告するね!

まとめ

  • HTML Templateは現状ちょっと使えない
  • JavaScriptファイルにHTMLも書き込んでコンポーネント化するのが良さそう
  • Reactとの組み合わせに期待

まずWeb Componentsの紹介

概要(公式翻訳+要約)

  • 新しいカスタム、再利用可能な、カプセル化されたHTMLタグを作成することができるウェブプラットフォームAPIのセット
  • 既存のWeb標準がベース

4つの特徴

  • Custom Elements
  • Shadow DOM
  • ES Modules
  • HTML Template

具体的な説明はこちらを参照
WEBCOMPONENTS.ORG

ブラウザサポート

もっとも対応が進んでいないCustom Elementsを代表してみてみよう
2022年1月現在は以下のような感じ
Safariで完全対応していないけど、Chromium系だと対応している
スクリーンショット 2022-01-29 19.19.32.png

Try① HTML Templateを使ったコンポーネント

チュートリアル的に作ろうと思えば、HTML Templateとslotを用いた方法でコンポーネントを作ることになるかも。

HTML
<template id="hello-web-components">
  <style>
    slot[name="hello-text"] {
      text-align: center;
      font-weight: bold;
    }
  </style>

  <slot name="hello-text"></slot>
</template>

<hello-web-components>
  <h1 slot="hello-text">こんにちわ xxxxさん</h1>
</hello-web-components>
JavaScript
class HelloWebComponents extends HTMLElement {
  // 要素がドキュメントに挿入された時にコールされる
  connectedCallback() {
    const 
      shadow = this.attachShadow({ mode: 'closed' }),
      template = document.getElementById('hello-web-components').content.cloneNode(true)

    shadow.append( template );
  }
}

// カスタム要素の登録
customElements.define('hello-web-components', HelloWebComponents);

HTMLの方は<template></template>とCustom Elementの<hello-web-components></hello-web-components>から成っている。
JavaScriptの方のコードは<hello-web-components>を定義している。
少しややこしいが、Custom Elementの定義の際に<template>...</template>を使用していて、出来上がったCustom ElementをHTMLで使用するという関係になっている。
私は上記のことを完全に理解すると、さっそくコンポーネントの作成に取り掛かった。

難しい問題に行き当たる

さっそくテキストフォームのコンポーネントを作成しようとし、componentsディレクトリ直下に、text-form.htmlファイルを作成

project_root/
├── index.html
├── components/
│   └── text-form.html
└── src/

components配下には他にもたくさんコンポーネントを作っていって、index.htmlで読み込んで使っていけばよさそうだ
text-form.htmlの中身はこんな感じを考えた

text-form.html
<template id="text-form">
  <style>
    ...
  </style>

  <div id="ok-label">OK</div>
  <div id="cancel-label">Cancel</div>
  <input type="text" id="input" placeholder="入力してください" />

  <script>
    const input = document.getElementById('input');
    input.addEventListener('blur', (e) => {
      ...      
    }
  </script>
</template>

<script>
class TextForm extends HTMLElement {
   ...(上記のテンプレートを使ってカスタム要素を作る処理)...
}

customElements.define('text-form', TextForm)
</script>

ここまでやって、コンポーネントをindex.htmlにインポートする手段が無いことに気づいた
実はWeb ComponentsにはHTML Importsという、htmlファイルを他のhtmlファイルにインポートする仕組みがあった。
しかし紆余曲折あり、その機能は提供されなくなった。
そうなるともう、このHTMLコンポーネントをどう取り込めばいいのか頭を悩ませることになった。
先人の中には、webpackのhtml-loader等でバンドルしている方もいたが、それだと使用しないモジュールも含めてバンドルすることとなり、実用的でない気がする。
一旦この方式は諦める

Try② JavaScriptにHTMLも書いちゃう

HTML Templateを諦めれば、Custom ElementsとShadow DOMを利用してコンポーネントを作成していくことが可能。
すなわち、こういう感じにしてしまうこと(百聞は一見にしかず)

text-form.js
class TextForm extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = this.template();
  }

  template() {
    return `
      <div id="ok-label">OK</div>
      <div id="cancel-label">Cancel</div>
      <input type="text" id="input" placeholder="入力してください" />

      <script>
        const input = document.getElementById('input');
        input.addEventListener('blur', (e) => {
          ...
        }
      </script>
    `;
  }
}

customElements.define('text-form', TextForm);

ご覧のようにCustom Elementの定義の中にHTMLを書き込んでいる
こうすればtext-form.jsをimportすることで<text-form>要素を使用することができるようになる

この方式の欠点

今のところ、vs codeのシンタックスハイライトが対応してなくて、HTMLがめちゃくちゃ書きにくいぐらいしかない
もっと作っていくと色々見えてくるかもしれない

webcomponents.orgで公開されているコンポーネントはほぼTry②を採用

webcomponents.orgには有志がWeb Componentsで便利コンポーネントを作成し、公開しておられます。
もちろん全数調査したわけではありませんが、私の観測範囲ではすべてTry②方式でした。
バンドラーを前提としない形での配布になるのは、あたりまえっちゃ当たり前かもしれませんが…

感想

正直Reactは使った方がいいなと思いました
ただ、ReactやVueなどのフレームワークとShadow DOMを組み合わせるとかなり良さそうなので、次回はそっちを試してみたいです
以上!きみでりはるかでした!

1
1
0

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
1
1