こんにちは、さるまりんです 🐒🔧
以前の記事では、
JavaScriptでURLパラメータを安全に扱う方法を整理しました。
今回はその続きとして、
安全に扱えても、載せすぎると迷子になる
という話をしたいと思います。
Vue.jsで画面を作っていると、
「URLに状態を残したい」という気持ちが、だんだん強くなってきます。
作っていくうちに状態が増え、
気づいたら——
自分がどこで何を管理しているのか分からなくなる。
結局、自分はこの画面で
何をどうしたかったんだっけ?
という状態になりました。
URLは便利。でも、なんでもできるわけではない
URLに状態を入れると、確かに便利です。
- リロードしても状態が戻る
- URLを共有できる
- デバッグがしやすい
この便利さがあるので、
つい、いろんなものを載せたくなります。
- 表示モード(mode)
- データ種別(type)
- タブ状態(tab)
- フィルタ条件(filter)
- ソート条件(sort)
結果、URLはこんな姿に。
/list?mode=edit&type=a&tab=detail&filter=active&sort=desc
うーん……何をしようとしているんだ?
一応、読み解いてみます。
「一覧画面」で
「編集モード」で
「詳細タブ」を開いて
「有効なものだけ」絞り込んで
「降順で並べる」
……あれ?
- 一覧って編集できたっけ?
- 詳細タブって何?
- type=a って何の a?
この時点で、
「今この画面、どういう状態なんだっけ?」
と、自分で分からなくなりました。
迷子になった!!
この頃のVue側のコードは、こんな感じでした。
watch: {
mode() { this.syncUrl() },
type() { this.syncUrl() },
tab() { this.syncUrl() },
filter() { this.syncUrl() },
sort() { this.syncUrl() },
}
見た目は「ちゃんとしてそう」です。
画面の中で状態が変わると、
URLにも反映する——という意図でした。
でも、実際に起きていたのは:
- どの変更がURLに影響するのか分からない
- 変更 → URL更新 → 再描画 → watch発火、の連鎖
- URLが正なのか、Vueのstateが正なのか分からない
はい。
完全に 状態の迷子 です。
「何が悪かったのか」を整理してみる
あとから冷静に考えると、
問題は意外とシンプルでした。
問題① URLに役割を持たせすぎていた
URLが、
- 状態の保存場所
- 状態の変更トリガー
- 状態の正解(source of truth)
全部を兼ねてしまっていたのです。
問題② Vueの責務が曖昧だった
- data
- computed
- watch
それぞれの役割が、URLと混ざっていました。
結果として、
URL / state / UI の境界が曖昧になって壊れていた
これが一番の原因でした。
「誰が何をするのか」を分けるって、
やっぱり大事なんですね。
改めて考えた「役割分担」
そこで、考え直しました。
URLに向いているもの
- ページ番号
- フィルタ条件(共有したいもの)
- 表示モード(戻ってきたい状態)
👉 「画面を再現するための最低限」
URLに向いていないもの
- 一時的なUI状態
例:モーダルが開いているか、ツールチップの表示状態 - タブの内部状態
例:一覧タブの中で、どの行を一時的に選択しているか - 画面操作の途中経過
例:入力途中のフォーム内容、ドラッグ中の位置、未確定の検索文字
👉 「今その場だけの状態」
Vue側の整理イメージ
Vue側で「何をどこに持つか」を、
かなり単純に考えるようにしました。
- URL ← 共有したい状態
- data ← 画面の今
- computed ← 状態の派生
- watch ← URLとstateをつなぐ最小限の橋
watchは「全部見る場所」ではなく、
URLと同期が必要なものだけを見る場所
にします。
迷子にならないための自分ルール
今回の反省から、
こんなルールを作りました。
- URLは「地図」
- Vueのstateは「現在地」
- URLパラメータは少なめで!
- watchが増えたら黄色信号
これを意識するだけで、
コードの見通しがかなり良くなりました。
おわりに
URLはとても強力です。
でも、やりすぎは禁物。
載せすぎると、自分が迷子になります。
安全に扱えるようになった次のステップは、
どこまで載せるかを決めること
でした。
同じように
「なんか複雑になってきたな……」
と思っている人が、
一度立ち止まって考えるきっかけになれば嬉しいです。
読んでくださってありがとうございました。
それではまた! 🐒🔧