こんにちは! エンジニアの きみどりはるか です!
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系だと対応している
Try① HTML Templateを使ったコンポーネント
チュートリアル的に作ろうと思えば、HTML Templateとslotを用いた方法でコンポーネントを作ることになるかも。
<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>
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
の中身はこんな感じを考えた
<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を利用してコンポーネントを作成していくことが可能。
すなわち、こういう感じにしてしまうこと(百聞は一見にしかず)
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を組み合わせるとかなり良さそうなので、次回はそっちを試してみたいです
以上!きみでりはるかでした!