JSConf JP 2024 イベントレポート
JSConf JP 2024 イベントレポート
興味深かった内容と感想を書きます
リンク
LT: UI 開発における ヘッドレス UI ライブラリの重要性とデザインシステムへの取り入れ方
UI 開発における ヘッドレス UI ライブラリの重要性とデザインシステムへの取り入れ方 - Speaker Deck
-
UI を一から開発するのは多大な労力がかかるためヘッドレス UI を使うのがおすすめ
-
ヘッドレス UI
- スタイルを持たず振る舞い (クリックやボタンクリックでメニューが開くといった UI を組み合わせた操作など) のみ提供するライブラリ
- React Ariaなど
- UI ライブラリは !import だらけになったり困りごとがあったがスタイルを実装者が決められる
- 最低限の a11y が確保される
- スターターキットやshadcn/uiがコンポーネント設計の参考になる
- 一方で自由度は高いためチーム内のドキュメンテーションやガイドラインが重要
-
一言感想
- ヘッドレス UI のメリットや導入を学べた
- かなり良さを感じた一方でドキュメントやガイドラインをデザイナーと合わせて作っていく協力体制や余力がないと厳しそうに感じた
LT: ステップバイステップで進める Yahoo!知恵袋のフロントエンドリアーキテクト
ステップバイステップで進める Yahoo!知恵袋のフロントエンドリアーキテクト - Speaker Deck
-
問題がたくさんある
-
何をいつ解決するか
- 何: 開発効率や品質の影響が大きい問題
-
いつ: 解決に必要な時間/分割可能性に依存
-
コスト小 → スキマ時間に解決
- 例: AB テストの仕組み、feature flag、コーディング規約
-
コスト大/分割可能 → リファクタリングデー
- 例: デザインシステムのコンポーネントの実装
-
コスト大/分割不可能 → 専用の時間を確保
- 例: リアーキテクト、TypeScript 化、ユニットテスト改善
-
コスト小 → スキマ時間に解決
-
どのタイミングでどの問題を解決するか
- 問題の種類で解決タイミングを分類
- 大きな問題でも小さく分割できる場合は少しずつ進める
- 問題の種類で解決タイミングを分類
-
何をいつ解決するか
-
今回は「3. コスト大/分割不可能」の問題について議論
-
問題1: どこに書くべきかルールが決まっていない問題
- 特定の関数が肥大化しやすく、処理が追いづらい/品質が下がる
-
リアーキテクトで解決
-
具体的な対策
- ドキュメントとルールの整備
- リファレンス実装の追加
-
分割例
- controller: ルーティング・リクエストを元にレスポンスを返す、リクエストパラメーターのバリデーション
- applicationService: 各層との連携(処理の進行役)、データの加工/業務ロジック
- model: データとふるまいをまとめたオブジェクト
- utility: 繰り返し色々な箇所で利用する関数
-
具体的な対策
-
実施してみて
- コードの見通しが良くなった
- ユニットテストが書きやすくなった
-
問題2: バグ検知がしづらい問題
-
a. 単純なミスに実行時になるまで気づきにくい
- JavaScript で書かれているため
- タイプエラーやオブジェクトのプロパティの上書き
- TypeScript 化で解決
-
b. ユニットテストのメンテナンスが困難
- 問題点
- 1つのテストファイルが 27,000 行
- コードコピーや mock の乱用
- ユニットテストは通るが実行時にエラー
-
ユニットテスト実装 & 方針の変更で解決
- 対策
- テスト対象のコードを小さくする(リアーキテクトとセット)
- パラメタライズドテスト (引数を切り替えて同じテストを繰り返し実行) の導入
- mock 初期化用関数の作成
- 基本は input に応じた output を確認
- 副作用が発生する関数以外は mock を極力使わない
- 対策
- 問題点
-
実施してみて
- ユニットテストが書きやすく/読みやすくなった
- ユニットテストの不足箇所がわかりやすくなった
- 失敗してほしいテストが失敗するようになった
-
a. 単純なミスに実行時になるまで気づきにくい
-
一言感想
- どのタイミングでどの問題を解決するかに悩みがあったため参考にしたい
- どこに書くべきかのルールは最近決めたので親近感を覚えた
- ユニットテストの方針に賛同しすぎて首がもげそうだった。自身のチームメンバーにも共有したい
React への依存を最小にするフロントエンドの設計
React への依存を最小にするフロントエンド設計 - Speaker Deck
-
なぜ依存が最小化にするのか?
- エコシステムの変化に追随する負担を減らす
- プロダクトはフレームワークより長寿命になる可能性があるため追随が不可欠
- フレームワークを切り替えたい
- Vue/Nuxt に依存していたため旧版コードをそのまま持って来れないことがあった
- より良い設計にする
- 初期に React 初心者がやりがちなコードが量産されてしまった
- エコシステムの変化に追随する負担を減らす
-
技術選定で考慮したこと
- React に依存しないライブラリの選定をした
- VanillaJS で使えるライブラリにした
- 薄いフレームワークを選んだ
- 規約より API で定義されているようなもの
- API は参照箇所を追える
- 標準 API が尊重されている
- エスケープハッチがある(フレームワークのレールから外れたい時に対応できる)
- React に依存しないライブラリの選定をした
-
設計
- バックエンドの知見にならう
- 依存の最小化
- オブジェクト指向
- テスト駆動開発
- 依存性逆転の原則
- Jotai で関数を管理することで DI コンテナとして活用
- 腐敗防止層
- Date を使わず日付時刻を表す型を作成
- Java がかつて Date から API に移行した背景から学ぶ
- Date を使わず日付時刻を表す型を作成
- バックエンドの知見にならう
-
一言感想
- 長期のプロジェクトになると依存性をなくすという考えがより重要になる。当たり前だが、より意識していきたい
- 長期的な運用を見越した選択をすることが、今後地獄を見ないためにも必要なのだろう
React Compiler と Fine Grained Reactivity と宣言的 UI のこれから
-
仮想 DOM は純粋のオーバーヘッドである
- 差分検出自体のコストがかかる
- 仮想 DOM の構築自体にコストがかかる
-
React Compiler が 2. 仮想 DOM の構築自体にコストがかかる を解決する
- キャッシュによって作り直すかどうかを判断する
- useMemo がやっていたことを React Compiler が代わりに行う
-
仮想 DOM を使わない宣言的 UI が登場
-
Signals
- 状態を Signals で管理し、値が変更されると検知して該当する Signals が使われた DOM を更新する
-
SolidJS
- コンポーネントは一度しか実行されない
- 一度の実行で全ての状態を返す必要があるイメージになる
- 算出プロパティは関数で実装する
- 早期リターンができないため、状態分岐には独自のコンポーネント
Show
を使用する
-
Svelte5 と Vue Vapor
- JSX を使わないため SolidJS のような独自コンポーネントは不要
- JSX を使わないことによる課題も存在する
-
Signals
-
SolidJS と Svelte5 と Vue Vapor の特徴
- Fire-Graned Reactivity
- Signals がその起源とされる
- 仮想 DOM を使わないため、オーバーヘッドが発生しないという考え方
- Fire-Graned Reactivity
-
これからの宣言的 UI に必要な要素
- パフォーマンスが高い(フレームワークなしの動作に近いレベル)
- 開発体験が良い
- 既存のコードとの互換性がある
-
React Compiler や Fire-Graned Reactivity による新しい宣言的 UI
- 仮想 DOM が極端に遅いわけではないが、これらの技術が注目されている
-
一言感想
- 仮想 DOM と新たな宣言的 UI を学ぶことができた
- しばらくは仮想 DOM でも十分だが、新技術へのアンテナを張り続けることが大事と感じた
Yahoo! JAPAN トップページにおけるマイクロフロントエンド - 大規模組織における FE 開発を加速させるには
-
トップページでは多数のサービスが表示されているが、1つの画面を開発していた
-
マイクロソフトフロントエンドを検討し始めた
- 下記を実現できる方法を検討した
- 高速にリリース・ロールバックが可能
- 長期的な使用
- 親子間の分離
- パフォーマンス
- Next.js で使用可能
- 下記を実現できる方法を検討した
-
各候補の検討内容
-
Module Federation
- モジュール単位で分割された JavaScript ファイルを実行時に取得する
- モジュールハンドラー(Webpack など)が提供する機能
- 高速なリリースやロールバックが難しい
-
Web Components
- 再利用可能な HTML タグを作成し、
<script>
タグから読み取る - ブラウザが提供する機能
- React18 のサポートが完全ではないため、React との相性を懸念
- 再利用可能な HTML タグを作成し、
-
iframe
- HTML ページ内に別の HTML を表示する
- ブラウザが提供する機能
- HTML 全体を読み込むためパフォーマンスが悪い
- スクロールや横スワイプを親子間で連携させる必要がある
-
独自実装
- 指定した ID 要素に表示する実装を
<script>
タグで読み込む
- 指定した ID 要素に表示する実装を
-
Module Federation
-
採用した技術
- 独自実装を採用した
- 各サービスが直接開発可能になった
- Fragment 単体でビルド時間が短くなった
- 独自実装を採用した
-
一言感想
- 自分たちに合った技術選定をどう判断して選んだかが見えて良かった
幸せの形はどれも似ているが、不幸なプロジェクトはそれぞれの形がある
-
パフォーマンス課題の発生傾向
- 計測していない
- 一箇所で爆発的にパフォーマンスが悪くなる
- 安易なアンチパターンを採用している
- 非推奨を無視している
- 開発体験/DX/UX の悪化を過度に許容する文化
- 計測していない
-
幸せなプロジェクトと不幸なプロジェクト
- 幸せなプロジェクトはまだ気づいていないプロジェクト
- 不幸なプロジェクトは何らかの形で予算を食い潰した状態
- 現在のソフトウェアは複雑で想定は困難。計測するしかない
-
フロントエンドの計測
- Lighthouse を使ったフロントエンド計測
- ≒ アプリケーション全体(E2E)の推測
-
WebVitals の指標を見る
- FCP: 最初に意味のある要素が表示されるまでの時間 → 初期レスポンスが悪い
- LCP: 初期表示で一番大きな要素が確定したタイミング → RTT に問題
- TBT: ユーザーの CPU をブロックした時間 → CPU/JS バンドル処理の問題
- CLS: 累積レイアウトシフト。要素書き換えの回数 → クリティカルパスの CSS
- SI: 結果的な総計(※見る必要なし)
- Chrome DevTools を Lighthouse の推奨設定に合わせて計測
- Lighthouse を使ったフロントエンド計測
-
チューニングのコツ
- コードを読まない
- 先入観が生まれるため
- DevTools とソースコード解析を繰り返し問題を絞り込む
- プロダクションに近い環境で複数回(3 回以上)測定
- WebVitals の指標は参考値として活用
- コードを読まない
-
DevTools の活用
- 最新の資料は少なく、実際に試しながら学ぶ
- More tools を積極的に試す
- (詳しくは資料へ)
-
ソースコード解析と計測
- 問題を絞り込んで計測する
- CPU / Network / RTT に着目
- ソースコードを書き換えて再現する最小状態を探る
- ブランチを切って作業
- ボトルネック特定後、伸びしろを確認
- モックや処理スキップで確認
- 二分探索でさらに絞り込む
- 問題を絞り込んで計測する
-
パフォーマンス改善の意思決定
- 難易度が低く、伸びしろが大きく、仕様変更がないものから着手
- 同根の問題は干渉する
-
再発防止策
- @next/bundle-analyzer や vite-bundle-visualizer を活用
- バンドルサイズを可視化
- lighthouse-ci
- パフォーマンスバジェットにどれだけリソースを割くかを確認しておく
- @next/bundle-analyzer や vite-bundle-visualizer を活用
-
最後は仕様の「決め」の問題
- 使われていない機能を削除・隔離する提案を行う
- KPI 測定が不可欠
-
サーバーフロントと GMT の速度比率
- 組織のパワーバランスが影響
-
一言感想
- 「速度比率は組織のパワーバランス」という考えが刺さった
- 最近フロントとバックエンドが分業されているため、全体設計の最適化が難しいと感じる
- フロントは比較的安価に対応できるため、課題が集中しがち(チーム特有の問題?)
- 私たちのチームでは計測自体ができていないため、まずは
lighthouse-ci
を使って自動計測から始めるべきだと感じた
- 「速度比率は組織のパワーバランス」という考えが刺さった
ESLint のカスタムルールで治安維持活動をする
ESLint のカスタムルールで治安維持活動をする - JSConf JP 2024 | miidas_tech
-
ESLint ルールの開発
- AST Node Type の知識が必要
- AST Explorer を使って把握するのがおすすめ
- 独自のテスト機構があり、テストをかけることが可能
- AST Node Type の知識が必要
-
作成したカスタムルール
-
<Link>
のhref
の値が物理遷移すべき URL の場合は<a>
を使う-
<Link>
は仮想的な遷移になるため、前の情報が引き継がれてしまう - 攻撃を受けると遷移先がすり替えられる可能性があるため、仮想遷移を減らしたい
-
- FC (Functional Component) の中で
render
関数を定義することを禁止-
ClassComponent
を使っていた名残があるため - 別のコンポーネントへ切り出すきっかけを作りたかった
-
-
Immutable.Record
のプロパティにget
でアクセスすることを禁止-
Immutable.Record.property
で統一したい理由からget
を禁止した
-
-
-
一言感想
- 自分のチームは若手が多く、ESLint のカスタムルールを作りたいと感じることが多い
- ハードルを下げる方法として以下の案を検討しても良いのかもしれない
- 他のチームで似たカスタムルールがないか探す
- GitHub Copilot を活用してルール作成をサポートしてもらう
徹底解剖!医療業務システムの React コンポーネント設計
-
インタラクションが多めの SPA
- 一つの画面で複数の内容が表示される
- 医療系であるため、当たり前品質の担保が特に重要
-
ディレクトリとコンポーネントの分類
-
components
ディレクトリ配下を役割ごとに分類 - 複合コンポーネントは関心ごとの単位で分類
- コンポーネント名に規則を設ける
- 原則 {関心事}{状況}{ベースコンポーネント名} の形式で命名
-
page
はユースケースごとに分割
-
-
複合コンポーネントで分割する理由
- 肥大化の防止と保守性の向上
- 例: 登録ダイアログと編集ダイアログが異なる部分がある場合
- 見た目が似ているからといって共有化すると内部で分岐が多発する
- 別々の複合コンポーネントとして定義すると保守性が上がる
- フォームコンポーネントは仕様レベルで同じ部分を共通化する
-
機能単位ではなくレイヤー単位で分割する理由
- レビュー負荷の軽減
- 配置に悩まない
- 隣のコードをお手本にできる
-
Storybook 駆動開発
- コンポーネントテストで品質を担保
- デザインとのコラボレーションツールとして活用
-
複合コンポーネントの構造
- Container レイヤー: ネットワーク処理
-
try-catch
を含み、Presentation レイヤーからネットワーク処理を切り離す
-
- Presentation レイヤー: UI の関心に専念
- Container レイヤー: ネットワーク処理
-
データ処理の重複排除とキャッシュの活用
- 複数の複合コンポーネントが同じリソースを同時に参照する場合に重複リクエストが発生
- Tanstack Query や SWR などを利用して無駄なリクエストを抑止
-
Render Hooks を使ったリフトアップ状態のカプセル化
- ダイアログの開閉など、リフトアップが必要な状態を Render Hooks で実装
- 高いモジュール性を実現
-
テスト戦略
- テストケースレベルで重複しない自動テストの実装
- ユニットテスト: 純粋関数の振る舞いを検証
- コンポーネントテスト: コンポーネントの振る舞いを検証
- ビジュアルリグレッションテスト: 画像の差分を検証
- E2E テスト: 業務シナリオのハッピーパスを検証
- テストケースレベルで重複しない自動テストの実装
-
一言感想
- テスト戦略に同意。この方針をチームに共有する予定
- コンポーネントテストの範囲や具体的な進め方についてチーム内で認識を合わせることが重要
Discussion