URL 就是你的狀態(Your URL is your state)
核心結論:URL 不只是位址,它本身就是一份可分享、可還原的狀態容器。把「別人點開這個連結該不該看到相同畫面」的狀態放進 URL,就能免費得到瀏覽器前一頁/後一頁、書籤、分享等能力。相關筆記:Create Search Params、Remix Loader Function。
為什麼 URL 適合當狀態
URL 天生能記住很多東西,並且很好地被瀏覽器的上一頁/下一頁串起來。核心問題是:如果我需要「還原」某個網頁的狀態,該怎麼做?答案往往是把狀態編碼進 URL。
現代 URL 可以包含許多資訊,例如:
- GitHub 用 fragment 自動高亮特定行數:
.../XeePhotoshopLoader.m#L108-L136 - Google Maps 把座標與縮放大小放進路徑:
.../@22.443842,-74.220744,19z
哪些狀態該放進 URL
在討論實作前,要先釐清「並非所有狀態都該放進 URL」。
適合放進 URL 的狀態:
- 搜尋查詢與篩選器
- 分頁與排序
- 檢視模式(列表/網格、深色/淺色)
- 日期範圍與時間段
- 已選項目或當前啟用的分頁
- 影響內容呈現的 UI 配置
- 功能開關與 A/B 測試版本
不適合放進 URL 的狀態:
- 敏感資訊(密碼、token、個人識別資訊)
- 臨時 UI 狀態(modal 開關、下拉選單展開)
- 進行中的表單輸入(尚未儲存的變更)
- 極其龐大或複雜的巢狀資料
- 高頻瞬態(滑鼠位置、滾輪位置)
判斷準則: 如果其他人點擊這個 URL,他們是否應該看到相同的狀態?該 → 放進 URL;不該 → 用其他狀態管理方式。
最佳實踐
1. 不要把預設值塞進 URL
// Bad: URL 被預設值塞滿
?theme=light&lang=en&page=1&sort=date
// Good: 只放非預設值
?theme=dark // light 是預設,所以省略預設值應在程式中處理:
function getTheme(params) {
return params.get('theme') || 'light'; // 預設值在 code 裡處理
}2. pushState 還是 replaceState?
pushState:希望產生新的歷史紀錄。用於更改搜尋條件、切換到新分頁等,讓使用者能快速返回上一個狀態。replaceState:不希望每次都新增歷史紀錄。例如搜尋框輸入的即時更新。
Anti-pattern
- 把敏感資訊(如 password)放進 URL
- 過長的 URL
- 用
replaceState破壞了原先的瀏覽器返回行為:
// 錯誤地取代狀態
history.replaceState({}, '', newUrl); // 這裡其實該用 pushState我的回顧
回到最一開始的問題:要不要「還原」狀態,取決於這個狀態是否需要分享出去、是否值得保留。例如表單輸入到一半的狀態,其實就不適合保留在 URL 中。