ジンジャー研究室

長めのつぶやき。難しいことは書きません。

HTTP/2のサーバープッシュを自動化するNode.jsライブラリを作った

そろそろRFCとして公表されるHTTP/2ですが、GoogleがHTTP/2を使ったRPCフレームワークを出してみたりとか、その界隈は大盛り上がりですね!

HTTP/2の目玉機能と言ったら、やっぱりPUSH_PROMISE!

f:id:jinjor:20150227224512p:plain:w340

PUSH_PROMISEと言えば、「index.htmlが必要だったらapp.jsとかstyle.cssも要るよね!」とサーバ側で勝手に判断して送りつける、というのが良くある説明なのだが、それを自動でやってくれるわけではないので、Node.jsサーバ用にライブラリを作ってみた。

jinjor/auto-push · GitHub

npmにもpublishしたので、npm install auto-push可能。

やっていることは、レスポンスの直前にHTMLをパースして、必要そうなJavaScriptなりCSSなり画像なりをプッシュする。それだけ。

HTML Importsにもたぶん対応してるけど、まだちゃんとテストしていない。

サーバーに組み込む

典型的な使い方。

var fs = require('fs');
var autoPush = require('auto-push');
var http2 = require('http2');
var ecstatic = require('ecstatic');

var options = {
  key: fs.readFileSync(__dirname + '/ssl/key.pem'),
  cert: fs.readFileSync(__dirname + '/ssl/cert.pem')
};

http2.createServer(options, autoPush(ecstatic(__dirname + '/public'))).listen(8443);

Express.js上でもいけると思ったけど意外とトラブルに見舞われているので、対応にはもう少しかかりそう。

プロキシとして使う

var autoPush = require('auto-push');
var http = require('http');
var ecstatic = require('ecstatic');
var request = require('request');
var fs = require('fs');
var http2 = require('http2');

// server
http.createServer(ecstatic(__dirname + '/public')).listen(8080);

// proxy
var options = {
  key: fs.readFileSync(__dirname + '/ssl/key.pem'),
  cert: fs.readFileSync(__dirname + '/ssl/cert.pem')
};
http2.createServer(options, autoPush(function(req, res) {
  request({
    method: req.method,
    url: 'http://localhost:8080' + req.url,
    headers: req.headers
  }).pipe(res);
})).listen(8443);

プロキシならNode.js以外のサーバにも対応できる。

課題

304(Not Modified)をどう扱えばいいのか分からない。 具体的には、クライアントから直接リクエストされたもの以外のif-modified-sinceヘッダ情報が無いので、適当にHTMLファイルと同じ情報を使いまわすとまあバグる。 仕方が無いので、現在は更新があろうと無かろうと200で送り返すという雑なことをしている。

その辺の仕様的に妥当な処置とか、教えてもらえたら喜びます。

まとめ

たぶん既に誰かやってるとは思う。

副作用って何だっけ

関数型界隈で、「状態がある=副作用」みたいな話を何度か聴いてちょっと違和感があった。 副作用とは、主たる目的の他に外部に悪影響を与えることだと思っていたのだが。 つまり「Hello, World」を表示することは、あまり副作用と呼びたくない。(主作用?)

そこで、そもそも副作用がどんなもので何が悪かったのかを思い出してみた。

以下、ひたすら例を挙げて感想を述べていくことにする。 JavaScriptで記述しているが、任意の言語に読み替えて問題ない。

データの更新1

function completeTodo(todo) {
  todo.done = true;
}

引数を書き換えているが、そういう関数だと思って使えば特に問題にはならない。

データの更新2

function completeTodo(todo) {
  todo.done = true;
  return todo;
}

似ているがこっちは紛らわしい。新しいのは戻り値だけだと錯覚してしまう。

データの更新3

function completeTodo(todo) {
  todo.done = true;
  updateDB(todo);
}

与えられた変数でDBを更新するパターン。変数がDBと同期しているという意図なのか、単なるミスなのか。

データの更新中にエラーが発生した

function completeTodo(todo) {
  todo.done = true;
  sometimesThrowError();
}

「データの更新1」の途中で失敗してしまったパターン。巻き戻しが必要。

ついでに何かを収集する

function calculate(a, b, warnings) {
  if(a === 0) {
    warnings.push('a is zero');
  }
  return a + b;
}

あるあるパターン。これはあまり怒っても仕方ない気がする。

計算過程で引数が変化する

function lastTodo(todos) {
  todos.reverse();
  return todos[0];
}

これは完全にアウト。

計算過程で引数が変化するので戻した

function lastTodo(todos) {
  todos.reverse();
  var last = todos[0];
  todos.reverse();
  return last;
}

…。

危ういreduce1

var newValue = array.reduce(function(memo, data) {
  memo[data.id] = data;
  return memo;
}, {});

JavaScriptだとこうなりがち。この時点では無害。

危ういreduce2

var oldValue = {
  '_1': 'foo'
};
var newValue = array.reduce(function(memo, data) {
  memo[data.id] = data;
  return memo;
}, oldValue);

oldValueとnewValueは同一変数。どうしてこうなった。

遅延読込み

var cache = {};
function findById(id) {
  if(cache[id]) {
    return cache[id];
  }
  cache[id] = getFromDB(id);
  return cache[id];
}

var data = findById('_1');

変数に影響を及ぼしてはいるが、特に問題はならない。

IDの自動生成

var n = 0;
function nextId() {
  return '_' + n++;
}

function createTodo(name) {
  return {
    id: nextId(),
    name: name
  };
}

副作用と言えなくもないが、意図したものだと思う。

コンソール表示

function printTodo(todo) {
  console.log(todo.name);
}

これは副作用とは言わないのではないか。

分類

危険度による分類

  • バグ
  • 危うい(書いた本人の認識が薄い)
  • 危うい(書いた本人は意図しているが認識の共有が必要)
  • 無害(設計パターン)
  • 無害(状態更新を目的とした関数や画面表示)

副作用を及ぼす対象による分類

  • 引数
  • 関数外
  • 言語外

まとめ

ヤバい奴らはやっぱりヤバい。危険な状況は単純に書き換えるか否かというよりも、意図せず起こしがちなパターンに潜んでいる。 それから、mutableなデータ構造がしばしばそのような状況を生み出していることは紛れもない事実だと思う。

HTTP/2でHello Worldしてみた

アドベントカレンダー26日目。嘘です。

Node.jsで簡単なHTTP/2サーバを作ってみた。Hello, Worldしかできない。

説明のために誰でも読める風にしたかったので、ストリームもオブジェクト指向もなく、決めうちの多いシンプル実装。あとはブラウザで動かないとつまらないので、少なくともFirefox Nightlyでは動くようにはしておいた。

あと書きながら一部まだ理解できていない部分があったりするのも何とかしたい。

HPACKの実装は summerwind/sasazka · GitHub からお借りしました。感謝。

参考

HTTP/2 Draft 16 日本語訳 – SummerWind

HTTP/2 最速実装 v3 // Speaker Deck

脱PolymerなWeb Componentsデザパタ

はじめに

今朝話題になっていたWeb Componentsの基本的な使い方・まとめ に触発されてみる。

すごい…!私にはこんな緻密な資料は書けないorz

だけど何か書きたい。書くぞ。

そろそろWeb Componentsしたい

Web開発に革命をもたらすと噂のWeb Componentsだが、そろそろプロダクションへの導入を検討したい。 2014年12月現在、Chrome 36+で全機能が使えるので、デモ画面や開発(テスト)ツールでは既にPolyfillなしで色々出来る。積極的に遊んでみたい。

とはいえ、現状と言えばWeb Componentsに関する情報は紹介記事がほとんどで、あまりプラクティカルな領域に踏み込めていない感がある。まあそんなものは実際に使い始めればわんさか出るので、別にその時になってからでも良いのだが、せっかくなので戦略の一つや二つ練っておきたい。

もうひとつ、Google製PolyfillライブラリのPolymerについて。 少し触ってみた感じでは特に不自由もなく素晴らしいライブラリだと思うのだが、知らず知らずのうちにロックインされてしまう微妙さがある。 それには多分いくつかの要因がある。

  • Web Componentsの記事を調べに行くと一緒にPolymerを紹介される
  • Template要素に十分な機能を持たせるライブラリがPolymerにしかない
  • webcomponents.js(旧platform.js)とpolymer.jsの機能の境目が曖昧
  • polymer-elementとして流通しているライブラリを使うと伝染する

そういうわけで、「なんだか知らないけどPolymer使っとけば安心でしょ」みたいな思考放棄の果てにjQuery的世界を再現する前になんとかしたい。 PolymerもjQueryも悪くないけど。

というわけで、実際使っていく上で考えうる限りの使い方のバリエーション、留意点などをざっと並べてみようと思う。

以下ベストプラクティス集じゃないので、念のため。

4つの仕様のうち使いたいものだけを慎重に選ぶ

Web Componentsは以下の4つの仕様からなる。

  • Custom Element
  • Shadow DOM
  • Template Element
  • HTML Imports

もちろんだが、これらは全てを統合して使ってもいいし、独立して使うことも出来る。 全てが統合された理想世界については、既にPolymerの方で語られているのでここでは特に触れない。

ここからはPolyfillをどこまで信用するかという話でもあるのだが、やはり心情的にあまり危険な橋を渡りたくないので、自分のアプリにとって必要最小限の機能を使っていきたい。 (Webサイトならいざ知らず、ブラウザのバージョンアップによるデグレードに対してパッチを当てるまでに時間がかかり、しかもそれが致命的になる場合もある)

HTML Importsを使わない

まず第一歩。 勝手な推測では大多数の人の目的はCustom Element(+Shadow DOM)なのでは、と思わなくもない。 そこで、HTML Importsを使わずに普通に.jsファイルで済ましてしまうことが可能だ。

Template Elementを使わない

また、Template要素を使う必要性も必ずしもないので、ここも好みのテンプレートに置き換え可能だ。従来の使い慣れたテンプレートエンジンや、多分Virtual DOMも使える。 ES6では複数行の文字列リテラルを宣言できるので、うるさいバックスラッシュともオサラバできる。

Shadow DOMを使わない

ここはちょっと何が正しいのか判断が付かない。というのは、個人的にCSSの干渉で「そこまで」困ったことが無いので、いまいち重要性がピンと来ないというのがある。 個人的にはそれよりも、「共通のスタイルが欲しい時にCSSコンポーネント内にインライン展開するのってどうなの?」という方が気になる。(←見解があれば教えてください)

さておき、カプセル化を気にしなければShadow DOMも削ることが出来る。(Shadow DOMのPolyfill難易度は高い) 以上のように色々そぎ落としていくとCustom Elementだけで事が済む。ミニマル主義ならこれで行きたい。

非動作時の代替手段を用意する

何らかの事情でWeb Componentsが動作しない場合に、デフォルトの動きを用意するといったことも可能だ。

  • ブラウザがWeb Componentsをサポートしていない
  • ブラウザのJavaScript機能がオフになっている
  • CDNが落ちている
  • バグ

記憶によればGitHub<time is="relative-time">要素がそのような実装になっていたと思う。 通常動作時は相対時間を表示し、動作しない場合は絶対時間が表示される。私GitHub賢い思う。

Custom Elementの生成を動的にカスタマイズする

Custom Elementの扱い方として常に紹介されるやり方は、「HTML Importなりscriptタグで読み込むなりすれば後は自由に使えるよ」というものだ。ただ、それだけでは済まない場合、もっと柔軟に扱うことが可能だ。

別の実装に切り替える

まず準備として、scriptの実行時点ではdocument.registerElement()せず、何らかの関数が呼ばれた時に登録するようにする。 これによって動的に決まるコンテクストにおいて、同じインターフェイスを持つ別の実装に切り替えることが可能になる。

if(debug) {
  registerDebuggableXxxElement();
} else {
  registerXxxElement();
}

コンテクストを渡す

パラメータを渡すことも可能。

registerXxxElement(userSettings);

要素名を変える

もちろん代わりの要素名を渡すことも可能。

registerXxxElement(userSettings, 'another-name');

フォーム部品の動作に気を使う

最早あるある現象なのだが、ハイユーザビリティを謳いながらブラウザの機能を殺していく、という皮肉なことが気をつけていないと起こりかねない。 Custom Elementだからと言って調子に乗ってオレオレ実装を始めてしまうと、元々普通に出来ていた機能をどんどん奪ってしまう。その傾向は特にフォーム部品に顕著に現れる。

  • 適切なタイミングでchangeイベントを発火する
  • <select>相当の要素の場合、valueへの代入が配下のoptionに影響する
  • 上の逆
  • フォーム送信時にシリアライズされる
  • フォーカスがあたる
  • :checked擬似クラスによる選択、スタイルの変更が出来る

正直、これらを全て正しく実装することを考えると、私ならしんどい。テストも書いたがそれでもしんどい。 しかも、:checked擬似クラスを再現する方法は用意すらされていない。 手軽に機能を継承するならis属性(is="xxx")を使うのが良いだろうか。この辺りの知見がもっと欲しい。

ここで機能を奪ってしまうことによるデメリットは以下のようなものが考えられる。

  • 他のライブラリとの親和性…たとえば:checkedなどを当たり前のように利用する
  • より大掛かりなシステムへの依存…たとえばmy-formのような特別な要素と密結合する
  • 特別な使い方を覚えるための学習コスト

調べてもあまり出てこないが、この辺の議論や見解も欲しい。

Template Elementを使って画面の初期化を遅らせる

例えば、ページ内に複数のタブがあった場合、隠れている部分の初期化は後回しにしたいという要求がある。

<div id="content-a">
  <template id="tmpl-a">
    ...
    <script src="init-a.js"></script>
  </template>
</div>
<div id="content-b" style="display:none;">
  <template id="tmpl-b">
    ...
    <script src="init-b.js"></script>
  </template>
</div>

表示されるべきコンテンツの初期化が、隠れているコンテンツの初期化にブロックされてしまうのは勿体無い。 そこで、上のように隠れている#tmpl-bのアクティベートのタイミングを遅らせることによって、初期化順序を最適化することが出来る。特にtemplate中のスクリプトが重い処理であるほど有効だ(手元での検証結果)。 もちろん銀の弾丸では無いが、武器のひとつにはなる。

Vulcanizeは早めに検討する

HTML Importsを多用するとどうしてもネットワークコストが高くなるため、JavaScriptと同様にHTMLもひとつに結合したい。 そんな時にPolymerチーム製のVulcanizeというツールが必要になる。

ただしこれには条件があって、静的にURLが宣言されている場合に使用が限られる

つまり、柔軟にURLをあれこれするフレームワークをがっつり構築した後でVulcanizeの導入を検討すると泣くことになる。(しかもエラーメッセージがはっちゃけていて分かりにくい) しかも現状HTML Importsの結合ツールはこれしか無いので、完全に詰んでしまう。結合の可能性は最初から念頭に置いておきたい。

HTML Importのモジュール管理

hrefを工夫する

HTML Importを活用して、例えば、foo.htmlbar.htmlというモジュールを独立に作成して提供したいとする。 ここで、foo.htmlbar.htmlに依存しており、アプリケーションからの利用イメージは次のようであるとする。

Application => foo.html => bar.html

この場合、foo.htmlからbar.htmlへのリンクをどうしたら良いだろうか。 Polymerは次のようにしている

<link rel="import" href="../bar/bar.html"></link>

何をしているかというと、モジュールがフラットに展開されたときに相対パスが通るように記述されている。

│
├── bower_components
│  ├── foo
│  │   └─ foo.html
│  └── bar
│      └─ bar.html

ここでひとつ問題になるのが、Bowerあるいは他のパッケージマネージャがフラットにモジュールを展開することに依存している。

│
├── bower_components
│  ├── foo
│  │   └─ foo.html -- requires bar@1.0.0
│  ├── baz
│  │   └─ baz.html -- requires bar@0.8.0
│  └── bar
│      └─ bar.html

そうすると、フラットにならない例外があった場合や別のパッケージマネージャを使いたい場合に、おそらく困った事態になる。 この辺りもコミュニティが盛り上がらないと議論が進まない気がしているし、自分もあんまり考えたくないからES6モジュールでの提供に逃げたい気持ちでいる。

菱形依存によるスクリプトの重複実行を避ける

こちらは既にちらほら話題に上がっているのだが、例えば、foo.htmlbar.htmlの両方がjQueryに依存していた場合。 具体的には次のように菱形(ダイヤモンド)の依存関係になっている場合、

Application => foo.html => jquery.js
Application => bar.html => jquery.js

普通に双方からscriptタグでjQueryを読み込むと、2度スクリプトが実行されてしまう。 これを避けるためには、jquery.jsをHTMLでラップすると良い。

Application => foo.html => jquery.html => jquery.js
Application => bar.html => jquery.html => jquery.js

こうすることで、jquery.htmlは2度目のアクセス時からは読み込み済みという扱いになり、内部で呼ばれているjquery.jsの実行も1回で済む。

その他

Resource Timing APIが、HTML Importを通じて呼び出されるサブサブリソースの情報を拾わない気がする…。

まとめ

私的見解をぶちまけてみた。まぁどれも結論は出ていない。

もうとにかく使い方のバリエーションが沢山あって、ベストプラクティス欲しい!という感想。(←Polymer使え)

Object.observe() のコールバック実行タイミング

ざっくり分かるスクリプト

下のスクリプトを実行すると、どのようにalertが表示されるでしょう。

var obj = {};
setTimeout(function() {
  alert('D');
});
Object.observe(obj, function() {
  alert(obj.a);
  obj.a = 3;
});
alert('A');
obj.a = 1;
alert('B');
obj.a = 2;
alert('C');

結果(反転) ⇒ A B C 2 3 D
Chrome 36で確認)

即時 > Object.observe > setTimeout
の優先度で実行されるようです。

Haskellでコードゴルフするためのメモ

明日プロダクションコードで使える無駄知識。

変数を1文字にする

基本ですね。

空白を入れない

なるべく隣接させる

  • 各種記号
$ . + - * / : ++ !! |
  • クォーテーションの類
'' "" `` () [] {}
  • 数字の次
2in

改行しない

;で改行できます

型を書かない

型推論の恩恵に預かりましょう。

@を活用する

引数を同時に展開できます。

f a@(_,y)=y+g a

括弧の代わりに$を使う

基本ですね。

map f(g a)
map f$g a

カリー化

これも基本です。

f a=g a
f=g

もっとカリー化

ポイントフリーしていきましょう。

f a=g(h a)
f a=g.h a
f=g.h

何度も使う関数を別名にする

長い名前の関数ほど有効。

a=length

関数をpublicにする

whereの中に居る必要の無いものは外に出します。

mapよりもリスト内包表記

多くの場合、リスト内包表記の方が短くなります。

let-inよりもwhere

1文字分だけ稼げます。

f a=let x=g a in x
f a=x where x=g a

concatよりも>>=id

1文字分だけ稼げます。

concat["a","b"]
["a","b"]>>=id

1文字の結合にconsを使う

Stringは[Char]なので。

" "++a
' ':a

ガード節を使う

if-then-elseより短くなる。
ガード節は関数のトップレベルでも使用できる。
otherwiseの代わりにTrueが使える。

f a=if a>5 then 1 else 0
f a|a>5=1|True=0

Polymerで個人のホームページを構築してみた

Web ComponentsでWebサイトの作り方が変わる

ブログやSNSの登場以来ずっと放置していた自分のサイトを復活させてみた。(祝!)
一応このブログは技術ブログのような顔をしているので、コンテンツの中身についてはあまり言及しない代わりにアーキテクチャについて少し喋ろうかなと思う。


で、今回はPolymerというWeb Componentsのpolyfill+αなライブラリを使ってみたので感想を書いてみた。
Polymerというライブラリ自体の性能とか導入可能性について言いたいわけではなくて、あくまでWeb Componentsを使うとこんな感じでサイト構築できるんじゃないかというところ。

完成したページ


http://jinjor.herokuapp.com/

World Maker
世界の創造主を名乗って自作の音楽を公開。かれこれ11周年くらい。

Polymerを使ったカスタム要素の実例

Ploymer主にを使用した場所。

  1. 音楽プレイヤー
  2. 音量ツマミ
  3. 全体のレイアウト

以降、順番に見ていく。

音楽プレイヤー

YouTubeっぽい自作のプレイヤー(上のサムネイル画像の下の方)をカスタム要素化。
Web Componentsの一番オーソドックスな使い方だと思う。今まではこういったコンポーネント部品を共通化する方法がなくサーバサイドのロジックでこしらえていたのだが、それをクライアントサイドで独立して出来るようになったというのがポイント。
つまり、サーバサイドで動的なデータを扱う必要がなければ、静的なファイルを配信するだけで良い。実際に今回のサイト構築はgrunt-contrib-connectというモックサーバだけで事足りた。

以下は、カスタム要素の呼び出し元のコード。

<link rel="import" href="x-midi-viewer.html"></link>

...

<x-midi-viewer audio="music/2014/sakura.mp3" smf="music/2014/sakura.mid" delay="1500"></x-midi-viewer>

音楽ファイルとしてMP3ファイルとMIDI(SMF)ファイルを用意する。delayは両者のタイミングのずれを補正するもの。カスタム要素は事前にインポートしておく。
呼び出し先は長いので省略。

音量ツマミ

f:id:jinjor:20140224032516p:plain

もう少し凝った使い方として、子コンポーネントとモデル(≒可変なデータオブジェクト)を共有するといった事が出来る。
DOMに慣れていると「子コンポーネントからクリックイベントが飛んできたら目的のオブジェクトを更新して…」というアプローチになりがちだが、ここでは更新して欲しいオブジェクトを直接渡してしまうことにした。

呼び出し元。

<x-volume-controller gain="{{sequencer.gainNode}}"></x-volume-controller>

ここでgainNodeというのは、Web Audio APIに定義されているGainNodeを実装したインスタンスで、これを渡してやることでカスタム要素に音量の制御を委譲している。

呼び出し先の一部を抜粋。

<polymer-element name="x-volume-controller" attributes="gain"><!-- ★ -->
  <template>
    <style>
      ...
    </style>
    <button class="volume_icon" data-value="{{loudness}}"></button>
    <button class="volume_slider" on-mousedown="{{startMoving}}" on-mousemove="{{move}}">
      <div class="volume_slider_foreground" style="left:{{left}}px"></div>
    </button>
  </template>
  <script>
    Polymer('x-volume-controller', {
      ready: function() {
        ...
      },
      move: function(e){
        var self = this;
        var x = e.pageX - e.currentTarget.offsetLeft;
        if(self.moving){
          var left = Math.max(Math.min(x, 46),0);
          self.left = left - (firefox ? 3 : 0);
          self.gain.gain.value = left / 46;// ☆
          self.updateLoudness();
        }
      },
      ...
    });
  </script>
</polymer-element>

<template>と対応する<script>を合わせて記述する。
Polymer関数の第2引数は所謂ViewModelになっていて、このオブジェクトの状態がテンプレートに自動手反映される。また、テンプレートはイベントと関数との関連付けも同時に担う。
スライダーを動かしたときに音量調節する処理をmoveメソッドに記述している。★で受け取ったGainNodeを☆で操作。所々怪しげなコードが書いてあるが気にしたら負け。readyは初期化時に呼ばれる。

全体のレイアウト

メインフレームの外(=メニュー部分)を共通化したい場合の新しい選択肢。つまりこう。

  • インラインフレーム(一部を再描画)
  • サーバサイドで共通化(全体を再描画)
  • Ajaxで中身を書き換える(一部を再描画)
  • クライアントサイドで共通化(全体を再描画)← New

汎用部品だけではなく、アプリケーションの責務分離や共通化のためにWeb Componentsを使うのもありなのではないかと思う。

呼び出し元のコード。

  <body>
    <x-layout>
      <div>
      <h3>リニューアルしました</h3>
        ...
      </div>
    </x-layout>
  </body>

今までと違うのは、カスタム要素にコンテンツを丸ごと渡している点。

呼び出し先のコード。

<link rel="import" href="x-social.html">
<polymer-element name="x-layout" noscript>
  <template>
    <style>
      ...
    </style>
    <div id="container"><div>
      <x-social></x-social>
      <header>
        <a href="/"><h1>World Maker</h1></a>
        <p>音楽とプログラミングを主食にするジンジャーのホームページです</p>
      </header>
      <nav>
        <ul>
          ...
        </ul>
      </nav>
      <div id="main-contents">
        <div>
          <content><!-- ★ここに挿入される -->
          </content>
        </div>
      </div>
    </div></div>
  </template>
</polymer-element>

渡されたコンテンツは<content></content>の位置に挿入される。(複数の場合など、更に細かく指定することも出来る。)

ハマったところ

現時点で未解決の課題。単なる調査不足とも言う。

ページの構築が遅い

動的にロードしているのである程度は仕方ないのだが、従来のAjax同様に最初にページが表示されてからガクガクっと画面が動く。
今回は対策方法が見つからなかったので、とりあえず放置。結構簡単なページでもこれなのでレイアウト用に使うのはあまり向いていなかったりするのだろうか。

メタ情報などを共通化できなかった

これもレイアウト関連。Open Graphを使う際に<meta>タグを挿入する必要があったのだが、<head>から離れていたためにカスタム要素で上手く共通化することが出来なかった。

カスタム要素内のスタイルを外部ファイルに切り出せない

カスタム要素の<style>内に記述する必要がある。これがPolymer独自の制限なのかどうかは分かっていない。

カスタム要素内のid等の扱い

ハマったというより勉強不足できちんと理解していないので不安を募らせているところ。
例えばカスタム要素内のidは、次のようにして取り出すことが出来る。

<div id="screen"></div>
...
<script>
Polymer('x-midi-viewer', {
      ready: function() {
        var el = this.$.screen;
...
</script>

このidは外界のそれとは区別されるので、document#getElementByIdで取得することが出来ない。
で、今回はその辺の細かい仕様はなんとなくの理解で済ませていたのだが、大規模開発でコーナーケースにあたっても混乱無く出来るか自信は無い。あとjQueryなど既存のライブラリが不自由なく使えるかどうか。

Web Componentsと微妙に仕様が異なる

例えば、ViewModelのバインディングなどはPolymer独自の仕様。どこに差異があるのかをある程度把握しておかないと、すんなり移行というわけには行かなそう。
また、要素構築時のコールバックの一部が動かないなどの不具合があった。

最後に

いくつか不安要素はあったものの、現時点でもある程度のものは普通に作れるので期待して良いと思う。

関連する去年の記事。もし興味があれば。
GUIをツリーにする話(前篇) - ジンジャー研究室
GUIをツリーにする話(後篇) - ジンジャー研究室
GUIをツリーにする話(新篇) - ジンジャー研究室


以上!