关键在于实测 setitem 是否成功:ios 9–10 safari 无痕模式下 localstorage 对象存在但 setitem 立抛 quotaexceedederror(code 22),需 try/catch 临时写入清理来识别,不能仅检测对象存在。

识别 Safari 无痕模式下直接抛出 QuotaExceededError(DOM Exception 22) 的关键,不在于“有没有 localStorage 对象”,而在于“调用 setItem 是否真正成功”。iOS Safari 在无痕模式中会伪装支持 storage——window.localStorage 存在、typeof 是 object、甚至 getItem 返回 null 都不报错,但只要一执行 setItem,立刻触发异常。这个行为从 iOS 9 持续到 iOS 10,iOS 11+ Safari 已修复,但 UC 等第三方 iOS 浏览器仍存在类似问题。
核心识别逻辑:必须实测 setItem,不能只检测对象存在
错误做法是仅判断 'localStorage' in window 或 !!window.localStorage,这在无痕模式下全为 true。正确方式是主动写入再清理:
- 选一个临时 key(如
'__storage_test__'),调用localStorage.setItem(key, 'test') - 立即执行
localStorage.removeItem(key) - 整个过程用
try/catch包裹,捕获code === 22或name === 'QuotaExceededError' - 注意:不要依赖
getItem返回值判断——无痕模式下它常返回null,但不抛错,不具备区分力
典型误判场景与表现差异
不同 iOS 版本 + 浏览器组合表现不一,容易漏判:
规划您的迪拜之旅 — 哈利法塔观景、沙漠探险、迪拜购物中心购物、棕榈岛度假村及黄金市场砍价。还提供支持...
- iOS 9–10 Safari:setItem 报错,getItem 返回 null,clear() 也报错
- iOS 11+ Safari:setItem 和 getItem 均正常(已修复)
- iOS 11+ UC 浏览器:setItem 不报错但实际未写入,getItem 始终返回 null
- 所有版本无痕模式下,sessionStorage 行为与 localStorage 一致,同样需单独测试
封装建议:用最小侵入方式做运行时兜底
识别到不支持后,避免全局降级为 Cookie(体积小、无过期管理、HTTP 头开销大)。更轻量的替代方案有:
- 内存缓存:用
Map或普通对象暂存,适合页面生命周期内的数据(如表单草稿、UI 展开状态) - window.name:可序列化存储约 2MB 数据,且跨页面保留(需手动
JSON.parse/JSON.stringify) - js-cookie:仅当必须持久化且数据极简(如用户偏好开关)时引入,避免全量迁移
- 不推荐 fallback 到 indexedDB:无痕模式下同样受限,且兼容性更复杂
开发阶段必须做的三件事
光识别不够,得让问题暴露得早、准、可控:
- 在项目入口处运行 storage 可用性探测,并将结果挂载到
window.__storageMode(如'native'/'memory'/'name'),方便调试和埋点 - 所有对 localStorage/sessionStorage 的调用,统一走封装层(如
safeSetItem),禁止直写原生 API - 在 error 监控系统中单独标记
QuotaExceededError,按设备型号、iOS 版本、浏览器 UA 统计占比,及时发现低版本用户聚集现象










