ジンジャー研究室

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

Custom Elements を正しく実装するのはとても難しい

f:id:jinjor:20191221055521p:plain

React みたいなコンポーネント作る系フレームワークだと思って Custom Elements を使おうとすると、たちまち死んでしまう。まだ色々試している最中なのでアウトプットはないんだけど、とりあえず今考えてることを書いておく。役立たないし刺されたら困るからポエム宣言しとこうか。ポエムです。

Custom Elements やっていきたい

Custom Elements の良さは特定のフレームワークに依存しないところだと思う。例えば React とか Vue とかだとそれぞれのフレームワークの世界にどっぷり浸かってしまい互換性がないが、 Custom Elements ならば普通の要素の延長線上でどこに持って行っても使える。 npm とか使わなくても script タグで CDN とかから持ってくればすぐに動く。夢のよう。もちろん、データフローはアプリケーション固有のものになるだろうから Custom Elements が力を発揮するのはせいぜい末端要素だろうとは思う。それでもよく使いそうな末端要素を埋めてくれるのは便利。

だけど、実装を始めると途端に難易度が高いことに気づく。もちろん、チュートリアル通りにやればすぐに動くものはできるのだが、「ちゃんと」動くものを作るのはめちゃくちゃハードルが高い。「ちゃんと」というのは言い換えると「普通の DOM らしく動作する」という意味。別に使い方は自由だと思うけど、変な動きをしたら驚くし、せっかくだからちゃんとしたものを作りたいという気持ちがある。

属性とプロパティの二重管理

色々難しい点はあるが、その中でも特に、難易度を劇的に高めているのは「属性とプロパティの二重管理」だと思う。

まず、属性に関しては静的に検査されている訳でもなんでもないので、適当な文字列がどんどん入ってくる。省略される可能性があるので、デフォルト値の定義も必須だ。おかしな値が入っていても例外を投げることもできない。例えば <input type="number" min="10" max="0" value="5"> などと書くことも可能だ。この場合どうなるかというと、最初は 5 が入っているがインクリメントすると値が 10 に飛び、デクリメントすると値が 0 に飛ぶ。0 のまま送信しようとすると「値は 10 以上にする必要があります」と怒られ、10 で送信しようとすると「値は 0 以下にする必要があります」と怒られる。おかしいのだが無難な動きにはなっている。 また、min="hoge" のような明らかに意味のない値を入れると無視される。無視されるが属性が消える訳ではなく、"hoge" という属性はセットされたままになる。

対してプロパティは JS から操作されるためにあり、文字列以外に数値やブール値を許容している。とは言っても、max なら数値かと思えば文字列が返ってくる。MDN によると date-time が入りうるからということらしい(たとえ type="number" でも)。対して maxLength はどうかというと、こちらは数値が返ってくる。属性を指定しない場合は、それぞれ ""-1 が返る。 null ではない。値をセットした時の挙動も属性とは微妙に違って、例えば minLength = 10 の時に maxLength = 0 という値を入れようとすると「 10 より小さくするな」と例外を投げてくる。文字列で "100"を入れると 100 と解釈される。"hoge" を入れると 0true1 。というわけで、型に関しては指定されているものの違っても怒られずなんらかの解釈をするようだ。

次に属性とプロパティの関係について。最初「属性を変更したらプロパティも変更される」という一方通行だと思っていたが、どうも双方向らしい。つまり、どちらかを変更した時にもう片方を呼ぶような実装にしていると無限ループになる。共通部分を切り出す必要がある。だいたい思うように動くが、変な値を入れると妙な動きをする。属性 maxlength"hoge" とすると、属性値は "hoge" に、プロパティは -1 になる。今度はプロパティを "hoge" にすると、属性値は "0" に、プロパティは 0 になる。別にこの通りの動きにする必要はないと思うが。

また、ややこしいことに、value のような属性は双方向ではなく属性には反映されない。頻繁に変更されるものに関しては逐一属性値を書き換えたくないようだ。その辺の細かい作法みたいなのが、以下の "Custom Element Best Practices" に書いてある。

developers.google.com

正直「ベストプラクティス」と書いてはいるが、「お前らちゃんと正しく実装しろよ」ということだと思う。いや、できることならそうしたいけど難しくないっすか。他には例えば「プロパティに値をセットした時にイベントを発火するのは避けるべし」とある。理由は「プログラムから値を変更したんだからどんな値が入ってるかは知ってるでしょ」とのこと。うーん...そう言われればそうな気もするし、そうじゃないような気もする。

感想

とりあえず、「 Custom Elements を使えばアプリがスイスイ作れるぜ」みたいな生易しいものじゃないので万人に勧められないし、開発チームに丸投げしたらコンポーネントがホイホイ上がってくるようなものでもなさそう。いや、上がってくるかもしれないが。本当に一部の実力者みたいな人がちまちま作るか、上のベストプラクティスをうまいことまとめたツールキットなりテストなり、なんらかの仕組みが必要そう。 Custom Elements のチュートリアルに「ユーザーはどんな使い方をするか分からないからね!」とあるんだけど、ほとんどの開発現場では世界中の人に使ってもらうわけではなく、限られた使い方だけを網羅すればいいはずなので。そして限られた使い方しかしないのであれば、まあ React とかでいいよねという話に戻ってしまう。その辺はなんか立場の違いというか、今まで実現できなかったことをボトムアップに構築していく集団と、今までできてたことをいかに高効率でやってくかっていう集団があって、それは両方あっていいと思う。ただ、効率厨が軽い気持ちで触ると難しさに音を上げるという話。覚悟を持って挑もう。覚悟と気合い。

よいお年を。