HTML5Experts.jp

Web Componentsを簡単・便利にするライブラリ「Polymer」を使いこなそう

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を作る最もシンプルな例を比べてみましょう。

<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を使うことで以下のようにシンプルに書き換えることができます。

<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つがあります。

以下はconstructor属性とattributes属性を指定する例です。

<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()関数内で初期値を指定しており、この例においてfoobarbazの初期値はそれぞれ0'Hello!'undefinedということになります。

プロパティの宣言については、他にもpublishedを使った方法があります。こちらに関してはPolymer公式のPublished properties – API developer guideを参照してください。

ライフサイクルコールバック

カスタム要素のプロトタイプに指定していたライフライクルコールバックは、それぞれ”Callback”が省略され、Polymer()に引数として指定します。

<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特有のコールバックとしてreadydomReadyがあります。readyは<sample-element>内で行っているShadow DOMの構築やイベントへのリスナの定義といった様々な処理が完了したタイミングで発火し、domReadyは、宣言するカスタム要素に内包されるコンテンツの生成が完了したタイミングで発火します。

テンプレート機能とデータバインディング

Templateの仕様は不活性なHTML要素を提供するという非常にシンプルなものですが、Polymerは、テンプレートエンジンMustacheでお馴染みのブラケット{{}}を使ったテンプレート機能を提供します。

<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}}"のような書式でイベントハンドラの設定をする他、そのままデータバインディングにもなります。

<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もサブセットとして利用しています。

以前まではplatform.jsという名前で知られていましたが、Polymerの公式アナウンスv0.5.0からwebcomponents.jsへの名称変更とGitHubのリポジトリがWeb Componentsへ移管が発表されました。コミットログを見る限り開発体制も変わっていないように見えますので、役割に応じて分担を適切に図った結果と言えるでしょう。

webcomponents.jsのインストールと利用方法

webcomponents.jsはGitHubのリポジトリからダウンロードすることができます。

Bowerもしくはnpmからもダウンロードすることが可能です。プロジェクトのパッケージ管理に合わせて選んでください。

# bowerでインストール
$ bower install --save webcomponentsjs

npmでインストール

$ npm install --save webcomponents.js

あとはダウンロードしてきたwebcomponents.jsをHTML内でロードするだけです。Web ComponentsのAPIを実行される前に評価されている必要があることに注意する必要があります。公式では、head要素で最初のscript要素としてロードすることを推奨しています

<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.jsHTMLImports.jsShadowDOM.jsが同梱されているので、これらをロードすることで個別にポリフィルを行うことも可能です。

自身でカスタムビルドを行う場合はリポジトリをクローンし、gulpで用意されているカスタムビルドを実行します。

# リポジトリのクローン
$ 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 について扱います。