コンポーネントに期待していたことと分かったことについて考える - 2023秋(3)
- コンポーネントに期待していたことと分かったことについて考える - 2023秋(1) (2023-10-03) | あーありがち
- コンポーネントに期待していたことと分かったことについて考える - 2023秋(2) (2023-10-04) | あーありがち
の続き。
LitとWeb Componentsの可能性を探る
VS Code拡張
実際に Custom Elements を書くに当たり、以下の二つを考慮する必要がある
- Lit など Custom Elements そのものを書く際の支援
- 定義済みの Custom Elements を利用して HTML を書いていく際の支援 1
Custom Elementsそのものを書く
いくつか拡張があるが、
がよさそう。古いものだと css``
の中の CSS の記述をシンタックスハイライトできない場合がある。
定義済みの Custom Elementsを利用してマークアップを書く
がよさそう。 以下で触れる Custom Elements Manifest を project local のコードや node_modules/ 以下から読み取っていい具合にカタログとして保持して補完させることができる。
実際、Shoelace: A forward-thinking library of web components. は Custom Elements Manifest が配布パッケージに同梱されており、この拡張を入れるだけで、sl-
から始まる独自要素を何の違和感もなく補完させることができる。
Storybookと組み合わせる
Storybook: Frontend workshop for UI development
Web Components で UI の実現を考え、attribute で状態を与えるとする場合、Storybook があると開発しやすそう
と予想できる。そこで Storybook を試してみた。2
- セットアップと動作についてはなんとかしているものとする
- 残念ながら Storybook 7.4.4 時点で Vite と組み合わせた init が最新の安定稼働の状態をすぐ実現できるわけではない。頑張れ。
- CSF 3 で書くものとする
- 各 story の export default で
tags: ['autodocs']
か./storybook/main.js
でdocs: { autodocs: true }
を指定しておく - JSDoc をいい具合に書く
- Custom Elements Manifest Analyzer で manifest を生成する
- Manifest を Storybook の config で読み込むようにする
これで、いちいち一つずつ story を書かなくても Storybook を整備できる。これはすごい。Storybook は Manifest の情報をもとにコンポーネントの情報をリスト化し、状態変更などの UI を組み立ててくれる。
Storybook 上のコンポーネントは見た目に割り切って property を決めてデザイン、状態の反映を組み込んでいくことになるが、これはなかなか軽快に行える。適切に「状態」の抽出、定義が行えるのであれば、本機能とデザインの両方の組み込みを同時に行うよりずっとよい体験が味わえる。
よくある開発のアンチパターンとしては機能を作り終わってないからデザインがうまく組み込めないとか、デザインは作り終わっていたつもりだったけど状態の想定が足りないとか、そんなことが起きがちだが、Storybook を利用すれば状態を外側からすぐに与えられるので、本機能を作り込みきっていなくてもデザイン側はデザイン側だけで磨き込める。
ただし、複数のコンポーネント技術を混ぜて運用することはできなさそう。その場合は複数 Storybook を立てて見た目だけ reverse proxy みたいな感じで実現するっぽい。
※ ちなみに Vue コンポーネントに対して Storybook を利用すると Vue DevTools が動作しないという情報がチラホラ見つかるが、Storybook 7.4.4 時点ではなんとなく怪しい挙動はあるが、使えるには使える状態であることが確認できた。ツールバー上のアイコンは “Vue.js not detected” と言っていたが、DevTools を開くと機能している。
Custom Elements Manifestとは
一言で言うと Custom Elements に関する JSON Schema. Custom Elements のライブラリに何を使ってどのように実装しているかではなく、どんな属性があってどんなイベントがあって、といったスペックを統一的なフォーマットで表現したもの。
これのおかげで上記の拡張や Storybook など 3rd party のツールは Custom Elements そのものに対する知識がなくても Custom Elements に対して適切な振る舞いを実現できる。
今のところ
に従って JSDoc で記述するのがデファクトっぽい。個人的に元々 JSDoc は書く派だからかもしれないが、とてもよいと思う。JSDoc を書いておくと Custom Elements Manifest Analyzer がいい具合に情報を吐き出してくれる。それを
- Custom Elements Language Server - Visual Studio Marketplace
- storybook/code/addons/docs/web-components at next · storybookjs/storybook
などが利用する格好のようだ。
SSRを試す
※ 今回これがハマった。以前 Nuxt で実践したことがあるだけで、SSR のなんたるかをよく分かっていなかった。
Server-side rendering (SSR) – Lit
Web Components はいくら Web 標準で軽量だと言ってもクライアントサイドで JavaScript が解釈し終わった後にしか、本来望んでいたデザインで表示することができない。未対応のタグなので中身を素通しで表示することはできるが、デザインは適用できない。つまり、Flash of Unstyled Content が起きる。
伝統的な SEO としては Heading など意味を含むコンテンツが HTML 上に存在していればペナルティはないが、ページの表示速度まで考慮される場合は明らかに完全に静的な HTML だけの場合に比べて不利になるし、そうでなくてもユーザーの体験としてはよろしくない。
そこで「アプリ」全振りでない場合にはやはり SSR を考慮した方がよさそうと予想が立つ。
あんまりストレートなサンプルがなかったが、
Lit SSR with global rendering - StackBlitz
が参考になった。いろいろ試してみて注意点を自分用にメモしておく。
Componentの作り方
- browser でしか動かない機能は入れないか、ちゃんと isServer で分岐するか lifecycle callback を利用する
残念ながら Shoelace や Spectrum といったコンポーネント群は完全には SSR できなさそうだ…。
Server側
- Custom Elements に対してのみ SSR 用の render を通す
- SSR の必要な Element 以外を SSR 用の render に通してはいけない
- ページ全体を SSR にするような何かを作りたいなら HTML になんらかのマーキングが必要そう(Nuxt 1 はそんな感じだった気がする)
- Node.js 以外のサーバサイドアプリから出力する際は Web Components 用の helper か何かを作って、そいつが SSR 用の Server に投げつけて返事を返す、必要ならキャッシュも活かす、ような感じがよさそう
Client側
hydrate !hydrate !!hydrate !!!
Lit で SSR しただけの場合、
- Declarative Shadow DOM に展開された HTML になる
- これに対して hydrate を行わないと Custom Elements のタグに反応した JS が同様の Shadow DOM をもう一度作り、要素が二重になって表示される、かつ SSR した要素は JS に反応しない(例えばボタンの click などは拾えない)
という状態になってしまう。
VDOMコンポーネントと組み合わせる
軽くやってみたけど、まぁ確かにできますね、以上の感想はありません。もっと使い込もうとすると何か思うところが出てくるかもしれぬ。
詳しくやっているところを見つけたので追記。ANDPAD さん最近露出増えたなぁ。
ANDPAD のデザインシステム「Tsukuri」の Web 向け実装について - UI コンポーネントの開発 - ANDPAD Tech Blog
最後に
とにかく「似たようでちょっと違うコンポーネント」が生まれて、その度に機能に落ち度がないか、デザインが期待通りかをテストするコストが、正直ちょっとバカバカしくなってきていた。コンポーネントベースはコスパが悪いなと感じていた。特に VDOM コンポーネントは初手も作っている途中もコスパがよくない。HTML, CSS だけの時はレビュー、テストもそんなに重くないが、JS が絡むとより慎重に取り組む必要がある。できあがったコードについてはまぁ悪くはないが、これだけ手間を掛けてこの程度なのかという気もする。
LitElement の書き方はそれほど難しくなく、基本的な JS と Shadow DOM への理解はある程度要求するが、UI だけを作ることに集中すればそこそこコンパクトに実現できそうな印象を持てる。Storybook と組み合わせて「状態」を反映したデザインや動作を実現していくのはコーディング体験としてなかなかよい。少なくとも機能の実装に自信がない人でもデザインの実装は自信を持って取り組め、品質の作り込みもしっかり行えそうだ。Shadow DOM を活用する CSS の知識については特定の JavaScript ライブラリの知識よりも間違いなく寿命が長く、デザインをコードに落とす人にも安心して勧められる。
Custom Elements の中に機能を実装しないと決めてかかれば、再利用性も自然と高めることができる。
ということで
- UI は Custom Elements で
- 機能は Custom Elements 以外で
を実践していくのは、「アプリ」全振りでない Web においてそれなりに現実的な選択肢であり、最初期の学習コストはともかく、やり始めれば再利用性が徐々にコストカットと品質確保に効いてきそうな手応えを感じることができた。