Polymer v0.8.0 Preview 所感

Improvement

Polymer の Twitter アカウントが気になる発言をしていたので、Polymer のアップデート内容を確認した。

Chrome と Safari における実行速度の高速化はもちろんわかるが、ファイルサイズが 87% 減というのはかなり大きい。v0.5.1 の polymer.min.js が 123KB なのに対して、新しい v0.8.0 では 15KB になっているということだ。

それなりに大きい取捨選択が行われたとみて間違いないので、一応チェックしておこうという感じで Chrome Dev Summit 2014 の動画をチェックした。このあたりの話を取り扱っていたのは、"Polymer: State of the Union” のセッションだろう。

▶ Polymer: State of the Union - Chrome Dev Summit 2014 (Matt McNulty) - YouTube

0.8.0 Preview

Polymer/polymer at 0.8-preview をみると、Preview バージョンがすでに整いつつあるので、セッション動画とこれを見ながら変更点を確認した。

(ブランチが消されたらデッドリンクになるので注意)

セッションでは Performance と Complexity を Needs Improvement として紹介し、それを 0.8 で改善したとある。たとえば Mobile Safari で動かすには苦しい感じがするのは周知の事実。

WebComponents ラッパー+αで機能を乗せた Polymer が初見でどんな振る舞いをするのか分かりづらいのも分かる話だ。

改善の概要

このような文脈を踏まえて、全体的な改善と取捨選択が行われている。Polymer: State of the Union によれば次のようなことを行ったらしい。

  • 全体的なリファクタリング(Layer?)
  • コア機能の高速化とシェイプアップ
  • 拡張的な機能のオプトイン化
    • 80:20の法則的な考えで、本当によく使う機能だけをベースに残した
  • データバインディングの改善

    • パフォーマンス改善と、機能のシンプル化
    • あらゆるプロパパティをバインディングする必要はない
    • 双方向データバインディングをオプトインに
    • 他のFWとの相互運用性のため変更通知はイベントを使うように
  • Shadow DOM Polyfill の縮小

    • スペック通りに再現した Polyfill は超すげーんだけど重い
    • Polymer が必要な部分の shim だけ使えば軽くなる

リスニングが貧相なので、誤りはTwitterで教えてplz

内訳を調査していないが、冒頭のファイルサイズ減については余分な機能をオプトインにしてるのが大きそう。いやー、そうだよね。うんうん。Claylump を習作してるときも拡張機能は全部プラグイン化すべきだと思っていて云々...。

全体的にトリッキーな実装と振る舞いが取り除かれてより洗練されたような印象を受ける。もともとAngularと比べれば素直なほうとは思っていたけど。

ロードマップ

  • 0.8 - preview today
  • 0.9 - beta Q1
  • 1.0 - Q2

という感じらしい。

0.8 preview 版がいつごろ npm やら bower に publish されるのかが気になるところ。半年くらいで 1.0 にするつもりがあるようなので、予定通りにいけばそんなに時間をかけず出てくるとは思うのだがどうなるやら。

WebComponents.js

ちなみに今回の話にはあまり関係ないが、前からアナウンスされていた platform.js の webcomponents.js への改名が WebComponents.org へのリポジトリ引き渡しをもって完了したようだ。

Overview

ここからは実際のリポジトリを見た感じの所感。実際に動作させてないので誤りがあるかもしれない程度の雑な紹介。

これらを見比べると、コミットログからして積み直されているので本当にイチからコードを組み直したような印象がある。

  • <polymer-element> がなくなった
  • 双方向データバインディングがオプションになった
  • published プロパティの追加
  • attributes の自動型変換
  • id 要素の自動コレクション と イベントハンドラ属性は annotation という括りに
  • listeners プロパティの追加
  • JavaScriptソースがHTMLになった

順に、簡単な説明を書いた。

<polymer-element> がなくなった

テストコード と README を見た感じだと、<polymer-element> がなくなった。 <template> の特定は、<script> の直前の要素をそれとして見なすらしい。あ、なんかムズムズする…。

よって、次のようなコードは幻のものとなった。

<polymer-element name="editable-color-picker" attributes="owner color">
  <template>
    <p>
    This is a <strong>{{owner}}</strong>'s editable-color-picker.
    He likes the color <b style="color: {{color}}">{{color}}</b>.
    </p>
  </template>
  <script>
    Polymer({
    });
  </script>
</polymer-element>

双方向データバインディングがオプションになった

前述したとおり。この変更で HTML のテンプレーティングには別のソリューションを組み込みやすくなったかもしれない。今までだと Polymer で作った要素のプロパティが、容赦なくデータバインディング対象になっていたのでやりづらかった。

0.8.0 でも同様の機能を使いたければ、polymer.html と並んでいる data.html をビルドして読み込めばよさそう。

published プロパティの追加

こんな感じで、公開したいプロパティの型などを宣言するために published が加えられた。

Polymer({
  name: 'x-custom',
  published: {
    user: String,
    isHappy: Boolean,
    count: {
      type: Number,
      readOnly: true,
      notify: true
    }
  }
});

この宣言自体が何かをするわけではなくて、他の機能がこの情報を元に型を考慮したりなんやかんやするっぽい。

attributes の自動型変換

WebComponents では HTML から attribute を通してその要素の振る舞いを設定したいことが非常に多い。そこで設定値を参照するために Element#getAttribute() を使うと、すべて文字列で返ってくるので本来は任意の型に変換する処理が必要になる。

それを前項の publish で定義された型情報の通りに、Polymer が自動で型を変換してプロパティに代入してくれる。たぶんこういうことだ。

<script>
  Polymer({
    name: 'x-custom',
    published: {
      itemId: Number
    },
    created: function() {
      console.log(this.getAttribute(‘itemId’)); // => ‘123’
      console.log(this.itemId); // => 123
    }
  });
</script>

<x-custom item-id=“123”></x-custom>

この attributes の機能は確かに便利かもしれない。AngularJS のときもダルいなーと思っていたポイントだ。

id 要素の自動コレクション と イベントハンドラ属性は annotation という括りに

id が指定された要素の自動コレクションは次のようなやつで、これに annotations-nodes という名前がついていた。this.$ の下に id で指定された名前で要素の参照が格納される。

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

annotations-eventson-click=“onClick” みたいなお馴染みの記述だ。前はデータバインディングの機能と一体化していたが、独立した機能になったので指定するときに {{ }} の記号で囲う必要がなくなった。

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

これらの機能は Polymer のベース機能に含まれているようだが、本当に必要かは怪しい気もする。まあ、カスタムビルドしろって話だ。

listeners プロパティの追加

events の機能として、listeners プロパティが追加されている。いわゆる Backbone.js の events プロパティみたいなイベント宣言だ。

コードから読み取る限り、次のような感じになるはず。確認した時点のコードでは、node-annotations に依存した実装になっている。

<template>
  <button id=“foo">Foooo!!! ✌(՞ټ՞✌)</button>
</template>
<script>
  Polymer('x-foo', {
    listeners: {
      ‘foo.click’: ‘onClick'
    }
    onClick: function() {
      alert('Foooo!!! ✌(՞ټ՞✌)');
    }
    });
</script>

あと keys とかはキーボードのイベントを監視してるっぽい。シンプルな実装なのでいいんだけど、これまたベース機能なのか…?

JavaScriptソースがHTMLになった

最後に地味なコード上の変化として、v0.5.1 の当時は src 内にあるのが .js だったが、v0.8.0 になって .html に総入れ替えされている。中身は <script> 内に書かれた JavaScript がほとんどなのだが。

polymer.html をルートにして、HTML Imports で各機能を読み込むようなスタイルになり、より Web Components らしい状態になっている。(ES6 modulesって….)

build.bat では、polymer.html と data.html をそれぞれ vulcanize でビルドしている。data.html がデータバインディングを足すためのオプトイン分っぽい。

後記

以上、非常に雑な説明なので全体像を掴みづらいところはあると思うが、そのあたりは公式のドキュメントが更新されれば明らかになるだろう。

と、自分用メモを兼ねてるので悪しからず〜。