Web Componentsを簡単・便利にするライブラリ「Polymer」を使いこなそう
この記事は、連載「基礎からわかる Web Components 徹底解説 〜仕様から実装まで理解する〜」の第3回目になります。連載の第3回目となる今回は、Googleが中心となって開発を進めるPolymerというWeb Componentsのライブラリについて解説します。
Web Componentsをより柔軟に、そして強力にするライブラリ
Polymerは素のWeb Componentsにおいて、煩雑である部分を簡略化し、機能をより強力なものにし、基礎となるコンポーネントを提供します。BSDライセンスのもと、オープンソースで開発が行われており、ソースコードもGitHubにて公開されているので、Pull Requestを送るなどのかたちで私たちも開発に貢献することが可能です。
Polymerが提供する機能は、主に3つのパートに分かれています。Web Componentsの作成を簡単に、そして強力なものにする Polymer Core 。次に、一般的なUIの基礎の他、アニメーションや通信機能等を抽象化した Core Elements 。最後に、Google I/O 2014で発表されたデザインコンセプト「Material Design」をWebで実現する Paper Elements です。また、その他にも同じくPolymerチームが開発する、Web Componentsが未実装のブラウザでもWeb Componentsを利用可能にする WebComponents.js というポリフィルも提供されています。
Core Elements及びPaper Elementsは、Polymer Coreをベースに作られているため次回紹介するものとし、今回はPolymerの機能の中心となる Polymer Core と、この WebComponents.js について解説します。
Polymer Core
Polymer Coreは、素のWeb Componentsでは煩雑とも言える作成の手順を簡略化し、テンプレート機能やデータバインディングといった、Web Componentsを作る上で土台となる強力な機能を提供します。Core ElementsやPaper ElementsはこのPolymer Coreをベースに構成されています。よって、Core ElementsやPaper Elementsの利用にはPolymer Coreが必要です。
カスタム要素の作成をシンプルに書き換える
まずは、Web Componentsを作る最もシンプルな例を比べてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<template id='sample-element-template'> <button>Sample Element</button> </template> <script> var SampleElementPrototype = Object.create(HTMLElement.prototype); SampleElementPrototype.createdCallback = function () { var shadowRoot = this.createShadowRoot(); var template = document.querySelector('#sample-element-template'); var clone = document.importNode(template.content, true); shadowRoot.appendChild(clone); }; document.registerElement('sample-element', { prototype: SampleElementPrototype }); </script> |
これはPolymerを使うことで以下のようにシンプルに書き換えることができます。
1 2 3 4 5 6 7 8 9 10 11 12 |
<link rel="import" href="polymer.html"> <polymer-element name="sample-element"> <template> <button>Sample Element</button> </template> <script> Polymer({ ready: function () {} }); </script> </polymer-element> |
document.registerElement('sample-element')
によるカスタム要素の宣言は、Polymerの場合、<polymer-element name=”sample-element”>のように<polymer-element>のname
属性によって行われます。
また、カスタム要素の雛形となる<template>からの要素のコピーとShadow Rootの作成をcreatedCallback
で行っていた部分は、<polymer-element>が自動的に行うため省略できます。
このカスタム要素の定義のエントリポイントとなる<polymer-element>は、polymer.html
をインポートすることで利用可能になります。
<polymer-element>の属性値
<polymer-element>固有の属性値として、次の4つがあります。
name
定義するカスタム要素名を指定する(必須)。attributes
プロパティをスペース区切りで指定する(任意)extends
継承する他の要素名を指定する(任意)。document.registerElement()
の第二引数に指定するextends
と異なり、カスタム要素名を与えることが可能。noscript
Polymer()
をコールする必要がない場合に指定する(任意)constructor
カスタム要素のコンストラクタ名を指定する(任意)。カスタム要素のコンストラクタは指定した名称でグローバル空間にエクスポートされる。document.registerElement()
の返り値に指定する名称と同等。
以下はconstructor
属性とattributes
属性を指定する例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<link rel="import" href="polymer.html"> <polymer-element name="sample-element" constructor="SampleElement" attributes="foo bar baz"> <template> <button>Sample Element</button> </template> <script> Polymer({ foo: 0, bar: 'Hello!' }); </script> </polymer-element> |
constructor="SampleElement"
と指定することで、このHTMLが評価されると、new SampleElement()
でインスタンスを作成することができます。
また、attributes="foo bar baz"
でプロパティを3つ定義しました。これはPolymer()
関数内で初期値を指定しており、この例においてfoo
、bar
、baz
の初期値はそれぞれ0
、'Hello!'
、undefined
ということになります。
プロパティの宣言については、他にもpublished
を使った方法があります。こちらに関してはPolymer公式のPublished properties – API developer guideを参照してください。
ライフサイクルコールバック
カスタム要素のプロトタイプに指定していたライフライクルコールバックは、それぞれ”Callback”が省略され、Polymer()
に引数として指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<link rel="import" href="polymer.html"> <polymer-element name="sample-element"> <template> <button>Sample Element</button> </template> <script> Polymer({ created: function () {}, attached: function () {}, detached: function () {}, attributeChanged: function () {} }); </script> </polymer-element> |
また、素のCustom Elementsでは提供されていないPolymer特有のコールバックとしてready
とdomReady
があります。ready
は<sample-element>内で行っているShadow DOMの構築やイベントへのリスナの定義といった様々な処理が完了したタイミングで発火し、domReady
は、宣言するカスタム要素に内包されるコンテンツの生成が完了したタイミングで発火します。
テンプレート機能とデータバインディング
Templateの仕様は不活性なHTML要素を提供するという非常にシンプルなものですが、Polymerは、テンプレートエンジンMustacheでお馴染みのブラケット{{}}
を使ったテンプレート機能を提供します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<template> <ul> <template repeat="{{item in items}}"> <li>{{item.name}} - {{item.message}}</li> </template> </ul> </template> <script> Polymer({ ready: function () { this.items = [{ name: 't32k', message: 'In Canada.' }, { name: 'hiloki', message: 'So hungry.' }, { name: '1000ch', message: 'Feeling lucky.' }]; } }); </script> |
{{variable}}
のように変数名を記述することで値が展開されるほか、コード例の{{item in array}}
のようにin
を挿入することで、配列の要素を列挙することも可能です。この変数のコンテキストは常に作成するカスタム要素(this
)になります。
また、この{{}}
による変数の挿入は、次のon-click="{{onClick}}"
のような書式でイベントハンドラの設定をする他、そのままデータバインディングにもなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<link rel="import" href="bower_components/polymer/polymer.html"> <polymer-element name="sample-element" attributes="message"> <template> <input type="text" value="{{message}}"> <button on-click="{{onClick}}"><content></content></button> </template> <script> Polymer({ message: '', onClick: function () { alert(this.message); } }); </script> </polymer-element> |
まず、on-click="{{onClick}}"
によってボタンがクリックされた時の処理を指定し、このonClick
で指定されるイベントハンドラはPolymer()
コンストラクタを参照しています。もちろんここでも変数のコンテキストはカスタム要素自身(this
)になっています。
次に<input type=”text” value=”{{message}}”>となっている部分に着目してください。<input>要素の入力値に対し{{message}}
という変数を割り当てていますが、これはデータバインディングの力によって入力値が随時message
変数に代入されるようになります。先ほどのonClick
において、このmessage
をアラートで表示するようにしていますが、message
変数を介して`<input>要素への入力値が表示されることになります。
最後に、<sample-element>の属性値を宣言しているattributes
でもmessage
という属性を定義しています。こちらも例外なくデータバインディングが適用され、<sample-element message=”Input Value”>Button</sample-element>とすれば Input Value が初期値として表示されます。
Web Componentsのポリフィルライブラリwebcomponents.js (旧platform.js)
Web Componentsを実際のWebで使うには、Custom ElementsやHTML Importsといった機能が、仕様にそってブラウザに実装されている必要があります。各機能は2014年末の時点で、実装を先行して積極的に進めていたChrome(Version 39.0.2171.71)をはじめ、Opera(26.0.1656.32)、Firefox(34.0.5)はフラグを立てることでサポートされています。
しかしSafari、Internet Explorerは共に実装を見合わせている状態で、ユーザーの環境を考慮するとこれらのブラウザでもWeb Componentsが動くことが望ましいです。
こういった環境をサポートするのが、webcomponents.js
というPolymerチームの開発するポリフィルライブラリです。これをロードすることで、Web Componentsに関する機能が未実装のブラウザでも利用することが可能になります。また、Mozillaが開発するWeb ComponentsのライブラリであるX-TagやUIコンポーネント群であるBrickもサブセットとして利用しています。
- Polyfills – WebComponents.org
webcomponents.js
に関する解説 - webcomponents/webcomponentsjs GitHubのリポジトリ
以前まではplatform.js
という名前で知られていましたが、Polymerの公式アナウンスでv0.5.0
からwebcomponents.js
への名称変更とGitHubのリポジトリがWeb Componentsへ移管が発表されました。コミットログを見る限り開発体制も変わっていないように見えますので、役割に応じて分担を適切に図った結果と言えるでしょう。
webcomponents.jsのインストールと利用方法
webcomponents.js
はGitHubのリポジトリからダウンロードすることができます。
Bowerもしくはnpmからもダウンロードすることが可能です。プロジェクトのパッケージ管理に合わせて選んでください。
1 2 3 4 5 |
# bowerでインストール $ bower install --save webcomponentsjs # npmでインストール $ npm install --save webcomponents.js |
あとはダウンロードしてきたwebcomponents.js
をHTML内でロードするだけです。Web ComponentsのAPIを実行される前に評価されている必要があることに注意する必要があります。公式では、head要素で最初のscript要素としてロードすることを推奨しています。
1 |
<script src="bower_components/webcomponentsjs/webcomponents.js"></script> |
また、webcomponents.js
はWeb Componentsの仕様4つ全てをサポートしますが、Shadow DOMのポリフィルを除いたwebcomponents-lite.js
もあります。重いShadow DOMのサポートがない分、軽量化されているので、もしShadow DOMを必要としないのであればwebcomponents-lite.js
を選択するのもよいでしょう。
webcomponents.jsのカスタムビルド
Web Componentsのポリフィルを提供するwebcomponents.js
ですが、webcomponents-lite.js
のように補完する機能を選べるように、Custom Elements・HTML Imports・Shadow DOMそれぞれ個別のポリフィルを行うカスタムビルドが用意されています。先程のbower
によるインストールでCustomElements.js
、HTMLImports.js
、ShadowDOM.js
が同梱されているので、これらをロードすることで個別にポリフィルを行うことも可能です。
自身でカスタムビルドを行う場合はリポジトリをクローンし、gulp
で用意されているカスタムビルドを実行します。
1 2 3 4 5 6 7 8 9 10 11 |
# リポジトリのクローン $ git clone git@github.com:webcomponents/webcomponentsjs.git # クローンしたリポジトリに移動 $ cd webcomponentsjs # 依存モジュールのインストール $ npm install # gulpのbuildタスクを実行 $ gulp build |
実行後、dist
フォルダにそれぞれのファイルが生成されます。ES6の機能であるWeakMapやMutationObserverもポリフィルとして用意され、Web Componentsに関する仕様であるCustom Elements、HTML Imports、Shadow DOMに、内部で使われているのは非常に興味深い点です。
Polymerとwebcomponents.jsの取り扱い
このように、Polymerはカスタム要素を構築する上での機能拡張であり、webcomponents.js
はWeb Componentsの前提となる機能を提供します。よって、「Web Componentsを作る上でPolymerがなければ作れない」ということはありませんし、提供したいブラウザ環境にWeb Componentsの仕様が実装されていればwebcomponents.js
は不要です。
説明した通りPolymerでは、素のWeb Componentsとは少々異なる方法論を強いられます。これは、Web Componentsの仕様が今後変わる場合に、PolymerがAPIのギャップを吸収してくれる可能性を示唆しています。ですが、Polymer自体のAPIの変更がある場合にそれに追従しなければならないですし、Web Componentsの仕様そのものに比べればサポートがいつまで行われるかという懸念もあります。これらを踏まえると、Web Componentsの機能をひと通り理解した上でPolymerを使うことが望ましいと言えるでしょう。
まとめ
Web Componentsの基礎仕様・実践的なコンポーネント作成に続き、今回はWeb Componentsを支えるPolymerについて解説しました。次回はPolymerを実際に使って作られた、便利な基礎コンポーネント群 Core Elements と、Material DesignをWebで実現する Paper Elements について扱います。