つい先日、Polymerがついに1.0になり、Production readyということになりました。webcomponents.jsというpolyfillが必要って時点で本当に Production readyなのか?という疑問は尽きませんが、それでも一つの里程標となったことでしょう。
さて、約1年半ほど前、0.4の時代のPolymerを社内システムとはいえ、プロダクションに適用してみたものの、メンテナンスの煩雑さやそもそもそれを使った機能が使われない(!!)ことから、ほとんどいじることはありませんでした。
その間に、0.5が出て、0.8が出て、ときたわけですが、1.0が来たということなので、ちょっと時間を使って0.4からどれくらい変わっているかを身を以て体験してみました。
Polymerのアップグレード(with Bower)
Polymerは推奨通りにBowerでインストールしてましたんで、
$ bower install --save Polymer#polymer#^1.0.0
これでアップグレードは完了です。
core-*のアップグレード
core-系統はiron-とかpaper-に名前が変わってるので、それぞれ頑張って追随します。使い方は大抵同じような感じですね。
ただし、iron-やpaper-にいたる間で、そもそも削除されてたりするものや統合されて名前が変わっているものもありました。私がぶちあたったのはcore-transitionでしたが、よく見たら実は使ってなかったので無問題。
0.5から1.0へのマイグレーションガイド
0.5から1.0へのマイグレーションについてはマイグレーションガイドがあるのでそれを参照すればいいと思います。
・・・しかし、忘れてはならないのは、私の環境はそもそも0.4であるという事実です。0.5の時点でそれなりに違いがありましたが、↑のマイグレーションガイドを見れば分かる通り、かなりの量の変更点です。
以降では、頑張って書き換えた中で実際にぶち当たった変更点です。
polymer-elementからdom-moduleへ
単に置換するだけです。
CSSのlinkはtemplate外に
置き直すだけですね。
Polymer.domのAPIを利用するようにする
setAttributeとかquerySelectorとかは、Polymer.domのAPIで置き換える必要があるようです。なんかあれなんですが、getAttributeについてはPolymer.domを使う必要はないんですが、setAttributeは使う必要があるということで、なんとなく対称性が取れていないような・・・。
APIの一覧はあるので別に困りません。ただ、classListなどについてもこちらを利用するように記述があります。
setAttributeとpropertyアクセスの違い
setAttributeで設定する値は、 設定先の属性の型に合わせて、値として渡したデータをJSONに変換して渡します。
対して、propertyアクセスで代入した場合、 代入した参照がそのまま利用されます。
この違い、以外とハマります。個人的にはどっちかに統一できるようにした方がいいと思いますが。。。
Vulcanize
多くのコンポーネントを利用する場合、概ねvulcanizeを利用することになると思います。私のプロジェクトではgulp-vulcanizeを利用していましたが、こちらについては基本的にアップグレードでいけます。
ただし、オプションが変わっているのと、 CSPというオプションを利用していた場合、その機能が分離されて、crisperというツールに分離されています。幸い、gulpからは https://www.npmjs.com/package/gulp-crisper で使えるので、組み合わせれば基本的にそのまま利用できます。
また、こちらでは若干異端ではありますが、vulcanizeしたcomponentに対してwebpackでmodularizeしてます。こちらも何もいじらなくても動きました。
bind expressionの変更
基本的には後述のproperties以外は書けなくなった感じです。式がなんでもかけるってのは間違いなくカオス(Angularとかではいけるんでしたっけ)への一歩なので、シンプルになった感じです。
関数は使えるので、加工したいって時も基本的には困りません。フィルタを利用している箇所が若干ありましたが、単純な関数呼び出しに書き換えればOKです。
on-*系のイベントハンドラ
なんでこうなったのかよくわかりませんが、on-*がプレフィックスである、イベントハンドラについては、{{}}のプレースホルダは不要になりました。不要というか、入れてると動きません。
この辺、地味に分かりづらくなるような・・・
templateのif、repeatがそのままでは使えなくなった
ifを使いたい場合は、 <template is="dom-if" if=〜>
のように、またrepeatを使いたい場合は、 <template is="dom-repeat" repeat=〜>
のようにします。
is=〜で、 テンプレートは〜で、〜する みたいに覚えられるんじゃないでしょうか。ifとrepeatを同時に利用することはたぶんできないような感じなので、入れ子にするなりなんなりで。もしかしたら同時に使えるのかもしれませんが、特に困ってないので。
ref/bindはそのまま使えました。
async内でのasyncの仕様変更
asyncの中で、同じ関数をasyncに入れて、アニメーションみたいなことしてましたが、asyncの中で呼ばれたasyncは、 同期的に実行される ため、無限ループになります。
アニメーションしたければ requestAnimationFrame イベントをハンドリングするようにしましょう。
propertiesに一本化
もっとも大きな変更かつ、挫折した要因です。というか違いすぎて涙しか出ませんでした。
詳しくは、Describe propertiesというドキュメントに全部書いてあります。確かに、0.4のかなりカオスな奴に比べれば分かりやす・・・いかどうかは個人の判断に任せるとして、機構としては統一されたので、多少は使いやすいです。
ただし、これがまた予想外の挙動を引き起こしまくりました。
createdで初期化できない
前は、createdで初期化することができましたが、1.0からはできません。具体的に何が起こるかというと、createdのタイミングだと、propertiesから作られたsetterが働いて、自動的に存在しているpropertiesのキーのどれかに設定しようとしますが、キーが存在しない場合、なんと例外で落ちます。
初期化は必ずpropertiesでやりましょう。内部でしか使わないpropertyにはreadOnlyつけておけばいいです
privateなプロパティ
ないです。この辺、最近React.jsを触っている身としては若干うーん・・・って感じです。
簡単に言えば、propsとstateが全部propertiesになった、って感じです。ただし、外から入れるのを防ぐ機構はあります。
それがreadOnlyプロパティで、これを指定すると、特殊なセッターを介さないで入れようとしても、setterで弾かれます。
observerの働くタイミング
これが一番厳しい変更でした。以前の propChanged
のような一定の規則に従った名前の関数が自動的にobserverになるわけではなく、observerプロパティで指定した関数がobserverになるようになったんですが、これの動作タイミングというかなんというかがかなり厳しいです。
具体的には、createdからreadyに至ったタイミングで、 propertiesで宣言した全てのproperitesのobserverが呼ばれます。
以前は、あくまで外から設定された時に呼ばれてた気がするんですが、nullだろうがundefinedだろうが、とにかく最低一回は呼ばれます。
なので、全てのobserverには、 必ずnullチェック が必要です。これが不要であるような記述ってのは見つけられませんでした。
if=で渡したプレースホルダの呼び出しタイミング
このタイミングは概ねcreatedが終わったくらいになってるようで、関数は呼べても、propertiesはまだ 存在すらしてません。 なので、this.foo とかやっても取得できません。
なので、こういった関数はthisを使わず、引数にだけ依存するように作るのがベターのようです。
オブジェクトの参照を渡す時の注意
ほとんどいないと思いますが、がBackbone.Modelとか独自のオブジェクトを受け渡ししていたような場合、一旦考え直したほうがいいかもしれません。
前述の通り、setAttributeしていると、JSONへシリアライズされてから、値になるときにデシリアライズされます。こうなると、関数とかは復元できないので、そもそもBackbone.Modelを使っている意味なんてなくなります。
なので、Polymerのコンポーネントに受け渡すような値は、参照はあきらめて、pureなJavaScriptリテラルだけに絞ったほうがいいかもしれません。2-way bindingなどが使える、というのもありますので。
2-way bindingにするための方法
以前と違い、普通に書くと2-way bindingにはなりませんので要注意です。この辺はちゃんとドキュメントになってるんですが、気を抜くと2-way bindingにならなくてあれー?ってなります。
結局
だいたい1日半ほどひたすら書き換えをやってみましたが、かなりきついです。というかもうデータフローを書き直したほうがいいんじゃないかっていう・・・。
propertiesの仕様変更の影響があまりに甚大で、ほぼ全てのコンポーネント(だいたい20個ちょっと)に大幅に手を入れることになりましたが、それでもかなり厳しいです。
また、若干予想外だったんですが、おそらくsetter/getterにかなりの処理を入れているためか、0.4.2のときよりもパフォーマンスが落ちました。
具体的にどう落ちたかというと、:hoverに対してtransitionでつけていたアニメーションが動かなくなってあれ?ってなったくらいです。
まぁだいぶ厳しいことしてるのであれですが・・・CSSのことがなければ、React.jsを使って、shouldComponentUpdateを適切に使ったほうが性能が良く、書きやすいと個人的には思いました(身も蓋もない)
とりあえず1.0でなんとか動作させるようにはするつもりですが、まだ0.4とか利用されている方々は要注意ということで。
安易に使ってみたいからプロダクションで使うと痛い目を見るという実例でした。