Polymer と Web Components の違い9選(もとい Polymer の便利機能)

違い、または付加機能

色々な周辺事情で、勢力を広げつつある Polymer さん。(つい最近、それに加担したような気もする)

「どこまでが Web Components で、どこからが Polymer なのか」を理解するためにもPolymerの機能をメモる。Polymer は色々なことを便利にしてくれるライブラリであり、差分を言い出すとキリがないので主要なポイントだけ。

  1. <template> が自動で Shadow DOM に放り込まれる
  2. Shadow DOM内の <link> をインラインの <style> に展開
  3. repeat のサポート
  4. {{interpolate}} のサポート
  5. <element> のかわりを <polymer-element> としてサポート
  6. on-click とかイベントハンドラの宣言
  7. this.$ による idが付加された要素のコレクション
  8. observe によるプロパティの変更監視
  9. layout 属性によるレイアウトのサポート

自然に溶け込みすぎな Polymer の機能

Polymer・Web Componentsともに初めて見る場合、全く区別が付かなそうなポイントを挙げる。ほとんどが、貧弱な HTML Templates を補強している部分。

1. <template> が自動で Shadow DOM に放り込まれる

Polymer では、JavaScript であれこれしなくても、<template>を ShadowDOM の中に自動で展開してくれている。Template の内容をDocumentFragmentとして取り扱って ShadowDOM などに展開するには、通常 this.shadowRoot.appendChild(template.content.cloneNode(true)); のような操作が必要だ。

Web Components を構成する仕様が4つに分離しているからこそ生まれている穴だが、たしかにそこは勝手にやってくれよ感がある。

2. Shadow DOM内の <link> をインラインの <style> に展開される

あんまりやる人はいないかもしれないが、<template>内で<link>で CSS を呼び出そうとすると、Shadow DOMに追加されても読み込まれない。ところが Polymer はその常識を覆す。

<polymer-element noscript>
  <template>
    <link rel="stylesheet" href="owata.css">
    <div>\(^o^)/</div>
  </template>
</polymer-element>

これが

<polymer-element noscript>
  <template>
    <style>
      .owata {
         /* … */
       }
    </style>
    <div>\(^o^)/</div>
  </template>
</polymer-element>

のようにインライン展開される。

HTML Imports してる上に、スタイルまで外部ファイルにするとリクエスト数は増える一方だ。よほど複雑で大きい要素でない限りは、1枚の HTML に CSS と JavaScript も記述したほうが素直で良いのではと思う。

3. repeat のサポート

repeat="item in items" のようなループ処理は、世間のテンプレートエンジンと名前がつくライブラリでは定番の機能だが、これも Polymer の機能だ。

<template>
  <ul>
    <template repeat="{{item in items}}">
      <li>{{item.title}}</li>
    </template>
  </ul>
</template>

こんな感じになる。<li> 要素に直接 repeat 属性を記述するのではなく、<template> を挟む必要がある点だけ気をつければ、違和感なく使えるだろう。

4. {{interpolate}} のサポート

repeat の例で既出だが、<template> 内で {{ }} のシンボルで囲った変数を挿入できる。これ自体が双方向データバインディングになっている。つよい。

ここまで挙げたような、いかにもテンプレートな機能は残念ながらすべて Polymer の機能である。

その他の Polymer 機能

いかにも Polymer 特有と分かる機能についてもいくつか触れておく。

5. <element> のかわりを <polymer-element> としてサポート

In August 2013, Dimitri Glazkov posted to public-webapps announcing its removal, at least for now. Custom Elements: defining new elements in HTML - HTML5 Rocks

という通り、document.registerElement() に代わる <element> という宣言的な記述の仕様はいまのところ廃止されている。このようなスタイルの宣言を Polymer では <polymer-element> としてサポートしている。

JavaScript的に振る舞いを実装したい場合は、結局 Polymer(‘x-element’, { /* ... */ }) と書かなければならないが、JavaScriptが不要な要素であれば、noscript 属性をつけてやれば省略できる。(前述の例に出した通り)

6. on-click とかイベントハンドラの宣言

これもデータバインディングの一環だが、on-click=“{{onClickHandler}}” の書式でイベントハンドラを指定できる。

<polymer-element name="x-foo">
  <template>
    <button on-click="{{onClick}}">Foooo!!! ✌(՞ټ՞✌)</button>
  </template>
  <script>
    Polymer('x-foo', {
      onClick: function() {
        alert('Foooo!!! ✌(՞ټ՞✌)');
      }
    });
  </script>
</polymer-element>

なつかしい感じのする書式だが、どの要素のどのイベントでハンドラを実行するか、というのをJavaScriptの世界から切り離せる。

7. this.$ による idが付加された要素のコレクション

Automatic node finding - API developer guide - Polymer の例をそのまま拝借するが、下記のように id がふられた要素への参照を、自動で this.$ 以下に溜め込んでくれる。

<polymer-element name="x-form">
  <template>
    <input type="text" id="nameInput">
  </template>
  <script>
    Polymer('x-form', {
      logNameValue: function() {
        console.log(this.$.nameInput.value);
      }
    });
  </script>
</polymer-element>

このような this.$.nameInput.value のような記述は {{$.nameInput.value}} としてデータバインディングにも使える。(Polymer Designerの生成コードが参考になる)

地味に便利な仕組みだと思っていて、Backbone.js を使った拙作ライブラリであるPhalanxでも似たようなアプローチを採用している。View的な処理の中で不確かな DOM の世界をセレクタで探索するというのは、ストレスになる。HTML からの宣言を活かすべきだ。

8. observe によるプロパティの変更監視

プロパティの変更監視は、Polymer オブジェクトに propertyNameChanged な感じでプロパティ名 + Changed という命名のハンドラを生やしておけば勝手にやってくれる。

これをもっと宣言的に明示するならば observe ブロックを使うほうが好ましいかもしれない。(個人的に、命名規則でカバーがあまり好きではない)

Custom property observers - observe blocks - API developer guide - Polymer の例を拝借すると、次のような感じだ。

Polymer('x-element', {
  foo: '',
  bar: '',
  observe: {
    foo: 'validate',
    bar: 'validate'
  },
  ready: function() {
    this.foo = 'bar';
    this.bar = 'foo';
  },
  validate: function(oldValue, newValue) {
    // some validate
  },
});

余談だが、こういう JavaScript 側に宣言するノリは、X-Tagsのほうが積極的なイメージ。Polymer と比べると、細々としているが X-Tag もちゃんと続いているイメージ。

9. layout 属性を使ったレイアウトのサポート

Layout attributes - Polymer

レイアウト情報をHTMLの宣言内容に組み込むの、これまた一周して戻ってきた感じがするアイディアだが、ともあれ Polymer には layout 属性なるものが実装されている。

コンテナ要素に layout 属性をつけると、子要素を縦に積んだり横に並べたり、flexさせたりわりと自由自在な感じになる。詳しい説明は公式ドキュメントがちゃんと説明しきっているので、そっちを参照してほしい。

おしまい

他にもなんかいろいろpolymer-gestures とかあるけど、総じて「Web Components関連のアレ」というには、とっくにライブラリ単品として肥大化しているような印象がある。逆に言えば Polyfill の精度とか細かいことを気にせず使う分には、実戦で使うための機能強化が施された後とも考えられるかもしれない。または実験的に全部のせてみただけかもしれない。

複雑なDOM世界をラップしたjQueryはライバルすら淘汰した英雄だったが、Web ComponentsをラップしたPolymerにもライバルが必要なんじゃないかと思う次第である。