前端的「最佳實踐」文章有個系統性問題:規範保證的行為、瀏覽器實際實作、以及部落格流傳的 hack,全部用同一種肯定語氣寫。結果是你照抄一段程式碼,在自己的裝置上正常,上線後在某個平台崩掉,而原文完全沒提那個平台不支援。
這一篇把本專欄的主要主張逐條分級。
分級定義:
- 【規範保證】:CSS/HTML/Web API 規範明文定義的行為,實作若不符即為 bug。
- 【Baseline 可用】:規範定義且四大引擎皆已實作,可直接使用。
- 【部分支援】:主流引擎中至少一個未實作或行為不同,必須 feature-detect + 降級。
- 【實務共識】:規範沒規定,但業界做法一致且有可驗證的理由。
- 【平台相依/未文件化】:依賴特定瀏覽器的實作細節,可能隨版本改變。
一、視口與單位
| 主張 | 分級 | 依據與但書 |
|---|---|---|
vh 等於 lvh,即工具列收起時的高度 | 規範保證 | CSS Values 4;這正是 100vh 在行動裝置「超出」可視區的原因 |
svh/lvh/dvh 可直接使用 | Baseline 可用 | Chrome/Edge 108+、Firefox 101+、Safari 15.4+;2025-06 起 Baseline Widely available |
dvh 的更新會被節流,不保證 60fps | 規範保證 | CSS Values 4 明示 UA 可節流;web.dev 補充部分瀏覽器會依手勢完全 debounce |
position: fixed; inset: 0 比 100dvh 更適合遊戲主容器 | 實務共識 | 推論自上一條:dvh 的階梯式跳動會傳導成 canvas 重建。規範未規定何者「更貼合」 |
env(safe-area-inset-*) 需要 viewport-fit=cover 才非零 | 規範保證 | CSS Env 1 / WebKit 官方說明;未設 cover 時瀏覽器已自動縮排,故 inset 為 0 |
max(基準值, env(...)) 是安全區的正確寫法 | 實務共識 | WebKit 官方部落格〈Designing Websites for iPhone X〉即以此為範例 |
interactive-widget=resizes-content 可讓鍵盤縮小佈局視口 | 部分支援 | Chrome 108+、Firefox 132+;Safari 尚未支援(見於 STP 功能旗標),Chrome on iOS 亦不支援(WebKit 核心) |
user-scalable=no 能阻止縮放 | 錯誤 | iOS Safari 自 10 起忽略此設定;且違反 WCAG 2.1。正確工具是 touch-action |
二、Canvas 與縮放
| 主張 | 分級 | 依據與但書 |
|---|---|---|
設定 canvas.width/height 會清空 bitmap 並重設 drawing state | 規範保證 | HTML 規範明定;設成相同值亦然,故需 if 守衛 |
backing store 要乘 devicePixelRatio 才不模糊 | 規範保證 | 由 CSS 像素與裝置像素的定義推導;MDN devicePixelRatio 頁的正式範例 |
devicePixelRatio 受頁面縮放影響、不受捏合縮放影響 | 規範保證 | MDN 明載 |
| letterbox/cover/expand 三選一是幾何必然 | 規範保證 | 純數學:長寬比不等時,等比縮放無法同時滿足「填滿」與「不裁切」 |
object-fit: contain/cover 等同 letterbox/cover | 規範保證 | CSS Images 3 的定義 |
| CSS transform 縮放比 resize backing store 快 | 實務共識 | MDN canvas 最佳化明確建議,並註明「只適合放大,不適合縮小」;實際差距依 GPU 而異 |
device-pixel-content-box 給出整數實體像素 | 部分支援 | 規範定義明確,但屬後加選項;不支援的瀏覽器對未知 box 值丟 TypeError,必須 try/catch |
| render scale 0.75 的畫質下降多數人察覺不到 | 實務共識 | 主機/PC 遊戲動態解析度的普遍經驗;沒有針對網頁的公開受試者研究,數字應視為起點而非結論 |
| Safari 的 canvas 記憶體上限會導致畫面消失 | 平台相依/未文件化 | 只有社群實測值,Apple 未公開單一 canvas 面積與行程總量的上限,且隨版本變動 |
三、事件與重算
| 主張 | 分級 | 依據與但書 |
|---|---|---|
ResizeObserver 回呼在版面計算後、繪製前執行 | 規範保證 | Resize Observer 規範定義的 observation loop 時機 |
| 在回呼內改變被觀察元素尺寸會使通知被丟棄 | 規範保證 | 規範定義的深度限制與 error event;這正是 undelivered notifications 警告的來源 |
orientationchange/resize 讀到的尺寸可能是舊值 | 部分支援/實作差異 | 規範未規定觸發時機與版面的相對順序,各瀏覽器不一。這是「要轉兩次才對」的成因 |
(orientation: portrait) 比對視口而非裝置姿勢 | 規範保證 | Media Queries 4 定義為 height >= width |
visualViewport 可用於推算鍵盤高度 | Baseline 可用(API)/實務共識(推算法) | API 自 2021-08 Baseline;但「innerHeight − vv.height − vv.offsetTop 等於鍵盤高度」是推論,非規範保證,> 100px 門檻更是經驗值 |
讀取 offsetHeight/getBoundingClientRect() 會觸發同步版面計算 | 實務共識 | 所有主流引擎皆如此,但屬實作行為而非規範要求 |
四、全螢幕與沉浸
| 主張 | 分級 | 依據與但書 |
|---|---|---|
requestFullscreen() 需要 transient user activation | 規範保證 | Fullscreen API 規範 |
| iPhone Safari 的 Fullscreen API 不可依賴 | 部分支援 | caniuse 標記 iOS Safari 12–最新版皆為 partial;macOS Safari 自 16.4 完整支援。部分版本需開啟功能旗標 |
screen.orientation.lock() 需先進入全螢幕、且只在行動裝置可用 | 部分支援 | MDN 明載「通常只在行動裝置且全螢幕時啟用」,並標為 Limited availability;Safari 不支援 |
| PWA standalone 是 iPhone 上取得無工具列滿版的可行方案 | 實務共識 | Apple 的 apple-mobile-web-app-capable 有官方文件,但「這是 iPhone 上唯一可靠方案」是歸納,非 Apple 的表述 |
manifest 的 orientation 欄位 iOS 不遵守 | 平台相依 | 廣泛實測結果,Apple 未文件化其忽略行為 |
touch-action: none 會傷害無障礙 | 規範保證(警告) | MDN 明確指出違反 WCAG 2.0 SC 1.4.4;manipulation 是較安全的選擇 |
| Wake Lock 切到背景會自動釋放 | 規範保證 | Screen Wake Lock 規範;需安全上下文與頁面可見 |
五、本專欄的設計選擇(屬於主張,不是事實)
以下是我在寫作時做的取捨,讀者完全可以有不同結論:
- 「遊戲邏輯不該知道螢幕尺寸」 是一條架構紀律,不是技術限制。有些遊戲(無限捲動、程序生成)確實需要依螢幕尺寸產生內容,那時該做的是把「視野尺寸」當成明確的輸入參數傳進去,而不是讓遊戲各處直接讀
window.innerWidth。 - 推薦 expand 作為現代手遊的預設策略,前提是你的遊戲不受視野差異影響公平性。競技類請退回 letterbox 或 cover + 嚴格安全框。
- DPR 夾在 2–2.5 的建議是效能與畫質的折衷點,數字來自像素量的平方關係與常見裝置分布,不是實驗結論。畫質優先的產品可以放寬到 3。
- 建議把文字放 DOM 層 在文字量大時成立;若你的遊戲只有幾個數字,canvas 內畫反而省掉一整層合成。
六、延伸資源(皆為一手來源)
規範與參考
- MDN:
env()、<length>視口單位、meta name="viewport" - MDN:
Window.devicePixelRatio、VisualViewport、ResizeObserver.observe() - MDN:
Element.requestFullscreen()、ScreenOrientation.lock() - MDN:
touch-action、overscroll-behavior - MDN:Optimizing canvas
官方說明文章
- web.dev:The large, small, and dynamic viewport units
- WebKit:Designing Websites for iPhone X
- Chrome for Developers:Prepare for viewport resize behavior changes coming to Chrome on Android
支援度查詢
- caniuse.com:逐版本的支援矩陣與 known issues
- Baseline(web-platform-dx):判斷一個特性是否已「四引擎皆可用」
怎麼自己驗證一條主張
- 先查 MDN 的 Browser compatibility 表——它由
browser-compat-data自動生成,比部落格可靠。 - 交叉查 caniuse 的 Known issues 分頁——MDN 只記錄「有無支援」,caniuse 才會記錄「支援但有 bug」。
- 看規範原文(W3C/WHATWG)確認是「規範要求」還是「實作巧合」。這一步能過濾掉大部分會在下個版本消失的 hack。
- 實機驗證——工具列行為、safe-area、鍵盤、DPR 上限這四類,DevTools 的裝置模擬都不完整。
回到總覽 → 全響應式最佳實踐