一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • Web Componentsの実践活用:フレームワーク非依存のUI部品開発

Web Componentsが必要な理由

フロントエンド開発では、React、Vue、Angular、Svelteなど数多くのフレームワークが存在します。それぞれ独自のコンポーネントモデルを持つため、あるフレームワークで作ったコンポーネントを別のフレームワークで再利用することは困難です。Web Componentsは、この問題をブラウザ標準の技術で解決します。

Web Componentsは3つの技術で構成されています。Custom Elements(カスタム要素の定義)、Shadow DOM(スタイルとDOMのカプセル化)、HTML Templates(テンプレートの定義)です。これらを組み合わせることで、どのフレームワークからも利用できる再利用可能なUI部品を作成できます。

Custom Elementの作成

基本的なCustom Elementの実装例を見てみましょう。ユーザーカードコンポーネントを作成します。

class UserCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}

static get observedAttributes() {
return ['name', 'role', 'avatar'];
}

attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}

connectedCallback() {
this.render();
}

render() {
const name = this.getAttribute('name') || 'Unknown';
const role = this.getAttribute('role') || '';
const avatar = this.getAttribute('avatar') || '';

this.shadowRoot.innerHTML = \`
<style>
:host {
display: block;
font-family: sans-serif;
}
.card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
display: flex;
align-items: center;
gap: 12px;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
}
.name { font-weight: bold; }
.role { color: #666; font-size: 0.9em; }
</style>
<div class="card">
\${avatar ? \`<img class="avatar" src="\${avatar}" alt="\${name}">\` : ''}
<div>
<div class="name">\${name}</div>
<div class="role">\${role}</div>
</div>
</div>
\`;
}
}

customElements.define('user-card', UserCard);

Shadow DOMにより、コンポーネント内のスタイルは外部に漏れず、外部のスタイルの影響も受けません。このカプセル化がWeb Componentsの最大の強みです。

Litを使ったモダンなWeb Components開発

素のWeb Components APIは冗長になりがちです。Googleが開発するLitライブラリを使うと、宣言的かつ効率的にコンポーネントを記述できます。

import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('todo-item')
export class TodoItem extends LitElement {
static styles = css\`
:host { display: block; }
.done { text-decoration: line-through; color: #999; }
\`;

@property() text = '';
@property({ type: Boolean }) done = false;

render() {
return html\`
<div class="\${this.done ? 'done' : ''}">
<input type="checkbox" .checked="\${this.done}"
@change="\${this._toggle}">
<span>\${this.text}</span>
</div>
\`;
}

_toggle() {
this.dispatchEvent(new CustomEvent('toggle', {
detail: { done: !this.done },
bubbles: true
}));
}
}

Litのテンプレートリテラルは差分更新を行うため、再描画のパフォーマンスが優れています。また、TypeScriptのデコレータでプロパティ定義を簡潔に記述できます。

フレームワークとの統合

Web Componentsの最大の利点は、ReactやVueのプロジェクト内でそのまま使えることです。HTML要素と同じように扱えるため、フレームワーク側で特別な対応は基本的に不要です。デザインシステムの共通コンポーネントをWeb Componentsで構築し、各プロジェクトのフレームワークから利用するというアーキテクチャは、大規模な組織で非常に有効なアプローチです。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア