この記事で扱うこと
ハイブリッドブロックエディタを整えながら、4つの層が重なった: プロダクションバンドルがすべてのページで似たように重い理由、複数のブロックに跨るテキスト選択・削除、選択ツールバーがスクロール後にずれる問題、目次のように本文全体を読む UI が毎タイピングごとに再レンダーされる問題。各作業がどんな状況で何を目標にどう解かれ、何を学んだかを作業ごとに整理する。
一日のまとめ
| 作業 | 何をしたかったか | やったこと | 結果 |
|---|---|---|---|
| バンドル | ルートごとに重いチャンクを分離 — shared vendor が全ページを引かないように | splitChunks catch-all から chunks:"all" + 固定 name を除去、chunks:"initial"·maxSize 調整、static→dynamic import | 重いエディタ・チャートライブラリが初回ページから外れた |
| クロスブロック | 範囲を state に置かず演算時点で Selection から派生 | 単一ブロックは guest、クロスのみ document handler — 境界 split を一箇所に集める | delete·mark が境界 split を再利用、owning PM なしで安全 |
| フローティング UI | 開いている間 scroll·resize ごとに anchor を再測定 — DnD 後の stuck を防止 | selectionchange で UI トリガー、scroll(capture) + resize で座標更新 | スクロール·リサイズ後もツールバーが選択とずれない |
| Context | 頻繁に変わる document tree を広い runtime Context から分離 | TOC·派生 UI 用の専用 Context で購読範囲を縮小 | 一文字のタイピングで目次·統計まで再レンダーされていたのを遮断 |
1. Webpack shared vendor の罠
背景知識(概念)
- コードスプリッティング: ルートごとに必要な JS だけを分けてロードすること。
dynamic importで重いライブラリを遅く読み込める。
どんな状況だったか
プロダクションビルド後、複数ルートの First Load JS が似たように大きい。コードスプリッティングをしたのに、重いエディタ・チャートライブラリが初回ページに一緒に載る。
主な作業
splitChunks catch-all vendor に chunks: "all" と固定 name があると、コードスプリットが無力化されうる。chunks: "initial"·maxSize·固定 name の除去とともに、static import チェーンを dynamic import に変えてこそ重いライブラリが外れる。結果として、重いエディタ・チャートライブラリが初回ページから外れた。
教訓
- すべてのルートが一緒に重いなら、shared vendor チャンクをまず疑う。
chunks: "all"+ 固定 name は dynamic import を無力化しうる。
→ Webpack splitChunks vendor shared-chunk trap
2. クロスブロックのテキスト演算
背景知識(概念)
- Guest エディタ: フォーカスされたブロック一つだけをリッチテキストで編集するパターン。(focused-guest-editor 参照)
どんな状況だったか
ブロックごとに guest エディタが別々にあるので、一つのブロックの末尾から次のブロックの先頭までのドラッグ選択が native Selection としてのみ存在する。「どの PM が主か」がない。
主な作業
範囲を state に置かず演算時点で Selection から派生し、ブロック境界 split を一箇所に集めて delete·mark が再利用するようにした。単一ブロックは guest、クロスのみ document handler に分ける。結果として、owning PM なしでも delete·mark が境界 split を再利用して安全に動作した。
教訓
- クロスブロック範囲を React state に置かないこと — Selection API から派生し、ブロック境界処理は一箇所に集める。
→ Cross-block text range operations
3. フローティング UI 座標
背景知識(概念)
- フローティング UI:
position: fixedで選択領域の近くに浮かぶツールバー・ポップオーバー。
どんな状況だったか
テキストを選んだ後にツールバー座標を一度だけ取ると、スクロール·リサイズ後にツールバーが選択とずれる。
主な作業
position: fixed の1回の座標キャプチャだけでは不十分だ。UI が開いている間は scroll capture と resize で anchor を再測定する。選択 UI のトリガーは mousedown/mouseup より selectionchange が DnD 後の stuck を避けやすい。結果として、スクロール·リサイズ後もツールバーが選択とずれなかった。
教訓
- fixed 座標の1回のキャプチャだけでは不十分だ。スクロールコンテナが動いたら anchor を測り直す。
mousedown/mouseupよりselectionchangeが DnD 後の選択 UI に有利な場合が多い。
→ Fixed-position floating UI and scroll tracking · selectionchange for floating selection UI
4. Context 購読の分離
背景知識(概念)
- 派生 UI: TOC·統計のように本文ツリー全体を読んで作る UI。document が一文字変わるたびに一緒に再レンダーされやすい。
どんな状況だったか
本文ツリーが一文字変わるたびに、目次·統計まで一緒に再レンダーされる。
主な作業
TOC のように本文全体を読む派生 UI は、頻繁に変わる document を広い runtime Context に入れず専用 Context に分離して memo·render count を守った。結果として、一文字のタイピングで目次·統計まで再レンダーされていたのを遮断した。
教訓
- 本文全体を読む派生 UI は、広い Context に document tree を入れない。
→ React Context render granularity