メインコンテンツへ移動

Web Components

無料2017-12-01#Front-End#Web Components标准化#Web Components入门指南#Web Components spec#Web Components status

Web Components とは、いったい何なのか?

一.What?

Web Components are a new browser feature that provides a standard component model for the Web, consisting of several pieces: Shadow DOM, Custom Elements, HTML Imports and HTML Templates.

w3c/webcomponents から抜粋)

つまり、Web Components は Web コンポーネントモデル標準で、ブラウザがネイティブ特性サポートを提供し、Shadow DOM、Custom Elements、HTML Imports と HTML Templates を含みます

規範状態

依存する 4 つのもの、現在(2017/12/1)の状態は以下の通り:

Completed Work
2014-03-18  HTML Templates  Group Note

Drafts
2017-09-05  Shadow DOM  Working Draft
2016-10-13  Custom Elements  Working Draft
2016-02-25  HTML Imports  Working Draft

Obsolete
2014-07-24  Introduction to Web Components  Retired

All Standards and Drafts - W3C から抜粋)

つまり、これら 4 つの依存項は現在 1 つのみ完了(HTML Templates)しており、まだ推薦標準になっていません(NOTE のみで REC になっていません)。残り 3 つはすべて作業草案(WD)状態です

P.S. REC、NOTE などの W3C 文書状態に関するより多くの情報は、[W3C 規範制定プロセス](/articles/w3c 規範制定プロセス/) を参照

Web Components 規範自身は 14 年に 1 版(Introduction to Web Components)を出しましたが、廃棄されました(Retired)。現在 Web Components は規範なし状態で、github はまだ動きがあるようです

P.S. 本当に気になる場合は、廃棄されたバージョン を一見できます

実装状態

規範は不明朗な状態ですが、一部のブラウザは Web Components 依存の特性に不同程度的なサポートを提供:

Shadow DOM
    v0 Chrome25+ Firefox X Safari X Opera15+ Android4.4+ IOS Safari X
    v1 Chrome53+ Firefox X Safari 10+ Opera40+ Android5+ IOS Safari 10.2+
Custom Elements
    v0 Chrome33+ Firefox X Safari X Opera20+ Android4.4.4+ IOS Safari X
    v1 Chrome54+ Firefox X Safari 10.1+ Opera41+ Android5+ IOS Safari 10.3
HTML Imports
    Chrome36+ Firefox X Safari X Opera23+ Android5+ IOS Safari X
HTML Templates
    Chrome26+ Firefox 22 Safari 7.1 Opera15+ Android4.4+ IOS Safari 8

注意:Android5+ は Android5-6.x WebView: Chromium 62 を指し、部分サポートと完全サポートを区別していません

Chrome、Opera は一路緑で、Safari 及びモバイル端はほぼ追いかけ、Firefox は少しだけ。しかし現在の互換形勢から見ると、PC 端とモバイル端はすべて使用不可です

P.S. 互換性に関するより多くの情報は、Can I use... Support tables for HTML5, CSS3, etc を参照

さらに、polyfill とコンポーネントライブラリで試遊可能:

作用

Web Components は規範的なコンポーネント定義方式を提供し、Web コンポーネント標準化を推進することを希望

ブラウザがコンポーネント化方案に必要な環境特性をサポートし、Shadow DOM、Custom Elements、HTML Imports と Templates を含みます

ではまずこれら 4 つの依存特性が何ができるか見てみましょう:

  • Shadow DOM:サンドボックス環境、コンポーネント隔離に使用。コンポーネントは外部の影響を受けず、コンポーネント間も互いに影響しません

  • Custom Elements:コンポーネント参照方式。カスタム要素の形式でコンポーネントを参照

  • HTML Imports と Templates:コンポーネントリソースロード方式とコンポーネント声明方式。コンポーネント声明は <template> タグ内に置き、<link rel="import" href> でコンポーネントリソースをロード

核心はコンポーネント封鎖であることがわかります。Shadow DOM でコンポーネント詳細を隠蔽し、効果は以下に類似:

<video src="./video.mp4" controls></video>

このフラグメントを含む HTML ページは機能完全なビデオプレーヤーを提示し、再生ボタン、プログレスバー、音量調節ボタンなどを含みます

Web Components の用法はこれに類似:

<my-app>
    <my-nav-bar>
        <my-title>Order Center</my-title>
    </my-nav-bar>
    <my-aside-menu/>
    <my-filter-panel/>
    <my-order-list/>
</my-app>

ビュー構造とスタイルをカスタム HTML 要素にパッケージし、ブラックボックスコンポーネント形式で参照。コンポーネントは隔離された環境(Shadow DOM)で定義され、HTML、CSS、Script はすべて安全で、外部はコンポーネント内部のロジック/ビュー状態を直接変更できません

もちろん、コンポーネントは封鎖性の他に、少なくとも以下が欠如:

这部分内容は Web Components 自身の規範に定義されるべきです(例えば以前廃棄された Introduction など)。より上層のものについては、Web Components の考慮範囲内にありません。結局コンポーネント標準化の第一歩は無から有であるべきです

二.video から说起ぶ

1 行の video タグを書くだけで:

<video src="./foo.webm" controls></video>

ページは機能完全なビデオプレーヤーを提示します。では再生ボタン、プログレスバーの構造定義とスタイル声明はどこに隠れているのでしょうか?难道は单选ボタンなどのフォーム要素のように、システムプラットフォームがコントロールをレンダリング?それなら、プラットフォーム無関係の部分は、例えばテキストボックスの placeholder は、どのように実装されているのでしょうか?

実際、テキストボックスの placeholder は video と類似で、いくつかの見えるが(構造化ドキュメント内で)見つからない要素はすべて Shadow DOM に隠れています:

videoinputブラウザの内蔵コンポーネントに相当し、コンポーネントビュー構造及びデフォルトスタイルは Shadow DOM に隠れ、コンポーネントロジックは完全に隠され、autoplayoninput などの状態/行動 Hook のみ外部との通信のために暴露

ここで、Web Components の概���と非常に似ていることがわかります。所谓 Web Components はブラウザのコンポーネントメカニズムを暴露して、Web 開発者に使用させるだけです。こうすれば私たちも楽しく「ネイティブコントロール」(コンポーネント)を定義できます

三.Shadow DOM

前面でずっとものを Shadow DOM に隠すことを強調してきました。Shadow DOM の作用は sandbox(サンドボックス)に相当し、コンポーネント隔離環境を提供

Chrome でこの特性を試遊:

Shadow DOM 表示スイッチをオンにした後、テキストボックスの隠れた部分を覗いてみましょう:

<input class="nav-search column-07 start-18" name="s" type="text" placeholder="嗯。我看到过你的小熊。">
  #shadow-root (user-agent)
    <div pseudo="-webkit-input-placeholder" id="placeholder" style="display: block !important; text-overflow: clip;">嗯。我看到过你的小熊。</div>
    <div id="inner-editor"></div>

placeholder は静かにここに待機し、#inner-editor は入力されたテキストを表示

ブラウザが提供する Shadow DOM 特性を利用して、自分の Shadow Root を作成:

document.body.innerHTML = '<div class="container"></div>';
var host = document.querySelector('.container');
var root = host.createShadowRoot();
root.innerHTML = '<p>How <em>you</em> doin?</p>'

此時ノード構造はこう:

<div class="container"></div>
  #shadow-root (open)
    <p>How <em>you</em> doin?</p>

ページは「How you doin?」を表示。Shadow Root 下のコンテンツはページに呈現されるため

Shadow Root は createShadowRoot() が返す Fragment を指:

host.createShadowRoot() instanceof DocumentFragment === true

Fragment に対して DOM 操作ができ、独立した HTML 解析環境に相当し、外界の干渉を受けません

さらに 2 つの概念:Shadow Host と Shadow Boundary

Shadow Host

Shadow Root の「宿主」。Shadow DOM と DOM の接続点。说白了就是 Shadow DOM がどこに掛かっているか(上例中の host

寄宿関係があるので、試してみましょう:

host.parentElement.removeChild(host)

body 中に何もなくなったことがわかります。Shadow DOM は宿主と一緒に削除されました(戻り値は遊離の div ノード。此時 Shadow DOM は依然として div に掛かっており、ノードを append 戻して検証できます)

Shadow Boundary

1 つの抽象概念で、Shadow DOM 外のこの層の「結界」を指。Shadow Root 下の HTML と CSS を隔離でき、Shadow Host が所在するドキュメント内のスタイルと互いに影響せず、外界は JS で Shadow Root 下のノードオブジェクトを取得できません。iframe の隔離効果に類似

这正是 Web 開発がずっと欲しかったモジュール隔離。名前空間などの工程化方案で埋められますが、常にいくつかの埋められない欠陥があります。根本的な原因は最終的にページ上に呈現される HTML と CSS にはスコープの概念がなく、開発段階中の制限手段はここで道徳的制約になりました。另一方面、工程化手段で制約すると、制限が多すぎて業務が束手束脚になる問題も存在する可能性があります

Insertion Points

さらに、コンポーネント組合せの基本サポートを知る必要があります:

<div class="breaking">
    <ul>
        <slot name="breaking"></slot> <!-- slot for breaking news -->
    </ul>
</div>
<div class="other">
    <ul>
        <slot></slot> <!-- slot for the rest of the news -->
    </ul>
</div>

Shadow DOM W3C Editor's Draft 14 November 2017 から抜粋)

2 種類の挿入方式で、それぞれ具名スロットとデフォルトスロットを表。とても見覚えがあり、Vue テンプレートはこう書く。なぜなら:

For example, Vue components implement the Slot API and the is special attribute.

Vue と Web Components の関係については、後で話します

四.Custom Elements

カスタム要素を作成。錦上添花の小物。カスタム要素特性がない場合、こうする必要があります:

<ul class="stories">
    <li><a href="//example.com/stories/1">A story</a></li>
    <li><a href="//example.com/stories/2">Another story</a></li>
    <li class="breaking" slot="breaking"><a href="//example.com/stories/3">Also a story</a></li>
    <li><a href="//example.com/stories/4">Yet another story</a></li>
    <li><a href="//example.com/stories/5">Awesome story</a></li>
    <li class="breaking" slot="breaking"><a href="//example.com/stories/6">Horrible story</a></li>
</ul>

あれば、こう書けます:

<stories>
    <li><a href="//example.com/stories/1">A story</a></li>
    <li><a href="//example.com/stories/2">Another story</a></li>
    <breaking slot="breaking"><a href="//example.com/stories/3">Also a story</a></breaking>
    <li><a href="//example.com/stories/4">Yet another story</a></li>
    <li><a href="//example.com/stories/5">Awesome story</a></li>
    <breaking slot="breaking"><a href="//example.com/stories/6">Horrible story</a></breaking>
</stories>

意味の角度から見ると、カスタム要素の表現力はより強く、より簡潔です

カスタムタグには 2 つの制約:

  • タグ名は必ず短線を含む

  • プロトタイプは必ず HTMLElement から継承

同様に試遊:

document.body.innerHTML = '<template id="tmpl"><p>How <em>you</em> doin?</p><style>em {font-size: 200%;}</style></template><div class="container"></div>';

// Grab our template full of markup and styles
var tmpl = document.querySelector('#tmpl');

// Create a prototype for a new element that extends HTMLElement
var HowYouDoinProto = Object.create(HTMLElement.prototype);

// Setup our Shadow DOM and clone the template
HowYouDoinProto.createdCallback = function() {
var root = this.createShadowRoot();
root.appendChild(document.importNode(tmpl.content, true));
};

// Register our new element
var HowYouDoin = document.registerElement('how-you-doin', {
prototype: HowYouDoinProto
});

document.querySelector('.container').innerHTML = '<how-you-doin/>';

以下が得られます:

<div class="container">
    <how-you-doin>
      #shadow-root (open)
        <p>How <em>you</em> doin?</p>
        <style>em {font-size: 200%;}</style>
    </how-you-doin>
</div>

呈現されるコンテンツも我々の願い通り:大きな you

上例中の document.registerElement は Custom Elements 特性。importNode() については、ただの普通の DOM API(Custom Elements の一部ではない)で、ノードをクローンするため。否则テンプレートは使い切り

HTML Imports については、さらに重要ではありません。同様に CORS があり、シンプルなコンポーネントロー��方案で、ajax で手動ロードコンポーネントと大きな違いはありません。HTTP2.0 時代が本当に到来する前に、生産環境ではまだ多ファイルに分けないでください

P.S. HTML Imports に興味がある場合は、オンライン Demo と参考資料を参照

五.Vue と Web Components

ここまでで、上記で言及したすべての例は、どう見ても Vue コンポーネント定義のようです。没错。Vue は実装上で一部の Web Components 規範に従っているため。例えば Shadow DOM 内の slot:

You may have noticed that Vue components are very similar to Custom Elements, which are part of the Web Components Spec. That's because Vue's component syntax is loosely modeled after the spec. For example, Vue components implement the Slot API and the is special attribute.

主に 2 つの違い:

  1. The Web Components Spec is still in draft status, and is not natively implemented in every browser. In comparison, Vue components don't require any polyfills and work consistently in all supported browsers (IE9 and above). When needed, Vue components can also be wrapped inside a native custom element.
  1. Vue components provide important features that are not available in plain custom elements, most notably cross-component data flow, custom event communication and build tool integrations.

Web Components 規範がまだ成熟しておらず、サポート性も楽観的ではないため、polyfill なしでは生産に投入できません。Vue は構築ツールに頼って環境互換性問題を乗り越え、ブラウザ特性サポートに依存しませんが、同時に Shadow DOM 封鎖性などの Web Components 核心優位性をてました

さらに、Web Components は比較的底层のコンポーネント規範で、Vue はコンポーネント規範を定義する他に、コンポーネント通信、データバインディングなどの上層方案も提供

六.オンライン Demo

アドレス:http://www.ayqy.net/temp/web-components/image-slider.html

P.S. 実装が非常に巧妙な純 CSS でインジケーター付きの image slider コンポーネント。とても面白い。一般隣接選択器(E ~ F)に対する筆者の看法を更新

参考資料

コメント

コメントはまだありません

コメントを書く