2020年2月1日土曜日

Web components の最適化プロセスへの考察

新しい Microsoft Edge がリリースされ、template / slot タグがついに IE 以外でサポートされました。 いずれ死にゆく IE は polyfill で十分とするなら、ついに Web components で事足りるかも知れない時代が来ている。

これまでは Web components という概念がなかったので、jQuery で DOM を簡単に弄ることから始まり、 テンプレートとして Virtual DOM という概念が生まれました。 Virtual DOM だけなら Vanilla で実装すれば良いと私は思うのですが、 簡便性を求めて数多くの JavaScript Framework が生まれました。

しかし今や JavaScript Framework を使わずに Web components だけで作れます。 まずは容量がすごく減ります。 どれくらい減るかは、web-components-todo というわかりやすいサイトがあるので引用すると、単純な ToDo アプリでは いま人気の React / Vue / Angular の 1/10 以下になります。 ついでに 速度も早くなる引用したサイトは、様々な JavaScript Framework の実装例も載っているので、とても参考になる。 このような利点はわかっているのですが、実際に実装するとどんな欠点があるか、少しだけ考えてみました。


レンダリングブロックに注意

template / slot の仕組みは、真っ先に気付いた問題点が 1 つあります。 それは template / slot で定義するのは良いけど、JavaScript のパースを伴うので遅くないかということです。 既存のシステムは first view (above the fold) は CSS のパースだけで済ませ、JavaScript は body の最後に実行することで速度を早めます。 template / slot に完全依存するとレンダリングブロックされてしまうので遅くなるのではないか、と思いました。

ゼロから検証するのは面倒なので色々探してみると、Bootstrap の機能を Web components でほぼ実装できているっぽいフレームワーク smart-bootstrap (当時の話で今は機能が変わっている) というものを見つけたので、これを使って検証しました。 サンプルページ を、ローカルサーバ上で確認したところ…案の定、遅い! head で 480KB (圧縮して 70KB) の CSS/JavaScript があり、明らかにレンダリングブロックしています。 未使用部分は削除できるとしても、JavaScript のブロックがキツ過ぎる。 つまり、Bootstrap のような CSS フレームワークの Web components 化は、思っているよりは簡単ではないです。 ガチガチにメンテするなら何とかなるかもだけど、フレームワークや手法がメジャーになる前にはやりたくはないかなあ。

特に above the fold との行き来をどうするかの問題はあると思います。 Web components に完全に頼ると、最適化してもレンダリングはおそらく遅くなるでしょうから、 above the fold はベタ書きして上書きするのが間違いなく最速でしょう。

no-JavaScript な人のためには多用は良くない

JavaScript 動かないようにしている人を最低限サポートするなら、Web components は多用し過ぎないほうが良いかと思います。 そのためあらゆるタグを Web components 化するのはあまり現実的ではない。 SPA はガン無視してるとこ多いけど、あんま良くない。

CSS は管理しやすくなるが肥大化しやすくなるかも

Web Components ベースのコンポーネントはページすべてに使うのは速度的にアレな感じがするけど、思想自体はとても良いものです。 例えばブログに Bootstrap のコードを載せたいなと思っても、CSS リセットが入っているから、ライブラリをロードするとページが崩壊してしまうんですよね。 使うとこだけ make して style で貼り付けて…とか、変なことを色々やって、ようやく使えます。 Web components ベースなら CSS のスコープが閉じているので、コードを載せたいコンポーネント部分をインポートするだけで使えます。 これは超絶に楽です。

ただよくよく考えてみると、そんなに話は簡単じゃない。 Shadow DOM もドキュメントレベルのスタイルから継承を利用するため、 様々なライブラリを組み合わせるときには、CSS リセットを想定しないといけない。

完璧な汎用性を目指すために世の中の CSS は CSS リセットを想定します。 しかし CSS リセットをしてしまうと、コンポーネントとしての完全性が失われる。 色々なリセットが混在したコンポーネントの同一性を担保するのは、限界がある。 そして Shadow DOM 内のスコープが閉じているから CSS の使いまわしが効かない以上、うまくやらないと CSS が肥大化することが予想できます。 最適化の観点からは基本的にかなり不利になりそう。

JavaScript Framework は Native が戦場に

Web components は真面目に考えるなら遅延表示されるコンテンツにのみ使うべきものとわかりました。 遅延表示されるコンテンツならフレームワークはそれほど問題ではなく、React, Vue, Svelte, Angular、Web components、何を使っても実際良いように感じる。 というのもレンダリングブロックにさえ気を付ければ、template 内に style, script が遅延ロードされるからです。 フレームワークのラップの読み込み時間くらいの差しかない。 とはいえ機能差がないなら速度を求めるのが信条なので、私は Web components を使うと思う。

JavaScript Framework はどちらかと言えばラップのほうが重要で、ラップするからこそ Native 方面を抽象化できている意味合いがある。 だからこそ巷では Vue Native, React Native, Flutter, Blazor がみんなの興味の気がする。 ただ Flutter をなんとなーく触ってみようとしたのですが、フレームワークがでかいでかい。 アップデートをかけようとしたらメモリ 4GB のマシンが落ちました。 現状では Native のための仕組みだなと痛感しました。

あくまで Web の世界から Native の世界を見るなら、分厚いラップをかけた後に頑張って Web に変換し直すのは、そんなにいいかなあ、という感じ。 Native 側も乱立してきて考えるのが面倒だし、まだまだ大きな変化はありそうです。 どちらかと言えば CSS Framework の柔軟性をそのままに、 Web components からお手軽に変換できるような bindings を作って欲しいなと思ってる。

0 件のコメント: