2024年3月21日
執筆
有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)に所属するテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。著書に『Bootstrap 5 フロントエンド開発の教科書』、『作って学べるHTML+JavaScriptの基本』など。
監修
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。
主な著書に「独習」シリーズ、「これからはじめるReact実践入門」、「改訂3版 JavaScript本格入門」他、著書多数。
フロントエンドの技術トレンドを解説する連載「新発見!フロントエンド技術のいま」。第2回のテーマは「Web Components」です。
Web Componentsを簡単に表現すると、HTMLに新しいタグ(カスタム要素)を追加する機能(API群)です。HTMLに新しいタグを追加して利用できれば確かに便利そうですが、このような機能がなぜ生まれてきたのか、その背景を見てみましょう。
HTMLの標準は、WHATWGによるHTML Living Standardで明確に定められています。ブラウザは、その標準に沿って動作するように設計され、開発されています。よって、「個人としてはこんなタグが欲しい、使いたい」「便利なタグをつくって、それを皆に使って欲しい」というニーズがあっても応えられないわけです。
しかしながら、Webページにおける独自のタグをつくるという試みは進められてきました。それが、第1回でも紹介したReactやVueといったコンポーネント指向のライブラリです。コンポーネント指向とは、見た目とロジックをコンポーネント(部品)という単位にカプセル化するという考え方です(関連記事)。コンポーネントを使うにあたって、その中身を気にする必要はなく、必要な情報を与えるだけで利用が可能になります。
例えばReactでは、HTML内に配置したコンポーネントにprops(パラメーター)を渡すと、レンダリングされたマークアップが返されるので、それをクライアントでレンダリングして表示する、という流れになります。
しかしながら、ReactやVueのコンポーネント指向は、当然のことながらReactやVueの設計や実装に依存します。学習コストが必要ですし、ReactをやめてVueをやるということになったら、習得や実装はやり直しです。ReactやVueを使うメリットは大きいので、学習コストをかけることは全くムダではないのですが、長い間「標準的な方法をつくれないか?」ということがWeb開発者の世界で議論されてきました。
そこで生まれたのが本日のテーマであるWeb Componentsです。長らくHTML4を使ってきた筆者は、HTML5が登場したときに、便利なタグがたくさんあるのに驚いた記憶があります。例えば、<video>や<embed>といったタグです。以下のようにURLを渡すだけでコントローラー付きで動画再生が可能になるので、Adobe Flashや専用の動画プレイヤーに頼らないで済むようになりました。
このように、見た目とロジックを1個のタグ(カスタム要素)に押し込み、配布も簡単にできれば、環境を選ばずに便利な機能を使ってもらうことができます。
Web Componentsのアイデアは、2011年のFronteers ConferenceでAlex Russell氏によって発表されました。その2年後に、GoogleはポリフィルとしてPolymerを開発しました。2016年にはChromeとSafari、2018年にはFirefox、2020年はMicrosoft Edgeという具合に、現在のメジャーなブラウザで次々と実装されました。このように、Web Componentsはその発表から10年ほどかけて利用できる環境が整えられてきたわけです。
Web Componentsの仕様は、HTML Living Standardの一部として策定されています。ドキュメントは、MDNで公開されています。Web Componentsは総称であり、以下の3つの技術から構成されます。
(1)カスタム要素(Custom Element):カスタム要素の動作を定義するためのAPI
(2)シャドウDOM(Shadow DOM):カプセル化されたシャドウDOMを制御するAPI
(3)テンプレートHTML(Template HTML):カスタム要素で利用できるテンプレート
APIこそ別ですが、実際にはこれらを組み合わせて利用することで、Web Componentsの目的を果たします。なお、カスタム要素に対して、通常のHTMLタグは組み込み要素として区別します。
では、実際のカスタム要素を見ながら、これらの技術の使われ方を見ていきましょう。
画像のようなカスタムボタンをつくるとします。最初から角丸で、テキストを大きく、色も付いた状態のボタンをカスタム要素としてつくってしまおうというわけです。「そんなボタンなら、CSSでスタイルを作成して、クラスでもIDでも<button>要素側で指定してやればいいのでは?」と思うかもしれませんが、これはコンポーネントという姿勢から言えば、カプセル化できていないし、コード(スタイルなど)の再利用もしにくく、好ましい形ではありません。そこで、Web Componentsの出番となるのです。
Web Componentsを使ってどのように実現するのでしょうか。最もシンプルに表現すると、以下のようになります。
①JavaScriptでカスタム要素のマークアップとスタイル、動作をクラスとして定義し、そのクラスをカスタム要素名で登録する
②HTMLではそのJavaScriptコードを取り込み、組み込み要素と同様に< … >で囲んで使用する
わかりやすいのは①でしょう。以下のように、HTML中に普通に< … >で囲ってタグを記述します。ここではcustom-buttonタグを定義したとしています。カスタム要素の名前はほぼ自由に決められますが、大きな制約として必ず「-」を含まなければなりません。これは、組み込みタグの名前との衝突を避けるためです。
<custom-button> <span slot="label">押してね!</span> </ custom -button>
なお、HTML中にはマークアップの再利用を目的としてテンプレートHTMLを置くこともできます。テンプレートHTMLは、テンプレート全体を定義する<template>タグと、差し込み要素の場所を示す<slot>タグで構成されます。このテンプレートは、後述のJavaScriptコードから利用できます。
②は、以下のようにHTMLElementを継承したクラスを定義し、それをカスタム要素APIであるcustomElements.defineメソッドで登録するというのが基本です。
class CustomButton extends HTMLElement { ←カスタム要素の定義 constructor() { super(); this.attachShadow({ mode: "open" }); this.shadowRoot.innerHTML = ` カスタム要素のマークアップ… `; } }
クラス内では、シャドウDOMという仕組みを用いてカスタム要素のマークアップやスタイルを登録します。シャドウDOMとは、本来のDOMとは別に独立してレンダリングされるDOMを言います。「Shadow=影」と付いているように、表には出てこない存在、ということです。シャドウDOMでは、本来のDOMに対するスタイル設定などの影響を受けませんし、その逆もありません。カプセル化という観点からは、非常に便利な性質を備えています。
シャドウDOMへは、都度マークアップやスタイルを登録できますが、前述のテンプレートを参照して登録することもできます。共通のマークアップを複数のカスタム要素で使い回す、といったことも可能になっているわけです。
ここまで、Web Componentsの技術的な部分についてざっくりと紹介しました。最後に、Web Componentsを取り巻く技術として、いくつかのトピックを紹介します。
ここまでのように、Web ComponentsはHTML、CSS、JavaScriptを駆使して書いていくのですが、ほぼ定型とは言えマークアップやJavaScriptコード、スタイルを都度書いていくのは面倒なものです。そこで、Litという2021年リリースの新しいJavaScriptライブラリが登場しました。Litは、先述のPolymerの発展形という位置付けで、簡略化された形式でカスタム要素の記述を行えるようになります。シンプルな記述と高速な動作が特徴となっています。
たとえば、上記のCustomButtonをLitで記述すると、以下のようになります。
import {LitElement, html, css} from lit; export class CustomButton extends LitElement { static properties = { プロパティの定義 }; static styles = css` スタイルの定義 `; constructor() { super(); プロパティの初期化 } render() { return html` コンポーネントのマークアップ `; } } customElements.define("custom-button", CustomButton);
プロパティはオブジェクトとして定義し、スタイルとHTMLはタグ付きテンプレート文字列で定義します。データ、スタイル、マークアップを分離でき、見通しの良いコードを作成できるので便利なライブラリです。
Ionic Frameworkは、Drifty社により2013年にリリースされた、クロスプラットフォーム対応のUIツールキットです。モバイル、Web、デスクトップで動作するUIを単一のJavaScriptコードで記述することができます。状態管理にAngular、React、Vueといった主立ったフレームワーク、ライブラリを選択することができます。
Ionic Frameworkでは、Web Components技術を中核に据えて、上記のフレームワーク、ライブラリもWeb Componentsのカスタム要素に収めており、独自のカスタム要素が利用できるなど、自由度が高いのが特徴です。
上記のIonic Frameworkでは、Web Components内にReactなどのコンポーネントを収めることで、外部からはReactの存在を見えなくするようにしています。では、React、VueといったライブラリとWeb Componentsの間の相互運用(Reactコンポーネントの中にWeb Componentsを入れる、またはその逆など)はどうなっているでしょうか。
Web Componentsは標準技術ではありますが万能ではありません。複雑な状態管理やルーティングが必要な場合には、それを得意とするライブラリ、フレームワークと組み合わせることを検討するべきです。
ただし、たとえばReactコンポーネントの中にWeb Componentsのカスタム要素を単純に埋め込んでもうまくいきません。これは、現時点でのReactはWeb Componentsの利用が前提とはなっていないためです。したがって、propsをカスタム要素の属性に与えたときや、コールバックの処理がうまくいかなかったりすることがあります。
Reactは、本稿作成時点で近々にリリースされると思われるReact 19で、Web Componentsのカスタム要素のシームレスな利用が可能になるとアナウンスされています。それ以前のバージョンでは、先述のLitライブラリを使うことで、Reactコンポーネントの中にWeb Componentsのカスタム要素を埋め込むことが容易になるとされています。
Vueでは、Web Componentsとの親和性がReactより進んでいるようで、現在のVue 3でVueコンポーネントへのカスタム要素の配置が一定の条件下で可能になっています。
両者とも、独自コンポーネントをWeb Componentsのカスタム要素化することも可能になっていますから、おのおのの長所を生かしながら相互運用するといったことを現実路線として検討することができるでしょう。
今回は、Web Componentsという標準技術について、その誕生の背景、主要な技術要素、実際のコード、そして周辺技術についても紹介しました。この記事をきっかけに、Web Componentsに関心を持ってくれる読者がいれば幸いです。
関連記事
人気記事