移动端 Safari 的 localStorage 限制需从检测、预估、降级三步应对:先用 try-catch 探测可用性,再用 TextEncoder 精确估算 UTF-8 字节数并预留余量,超限时按前缀/过期/损坏精准清理,大体积数据应切换 IndexedDB 或 Cache API。

移动端 Safari 对 localStorage 的限制比其他浏览器更严格,且行为不一致——不是简单“容量小”,而是叠加了隐私策略、内存压力、无痕模式等多重干扰。处理它不能只靠扩容或兜底,得从检测、预估、降级三步入手。
先确认是否可用,别硬写
iOS Safari 在无痕模式下会直接禁用 localStorage,调用 setItem() 会抛 SecurityError;部分版本还要求用户首次交互后才允许写入。必须在操作前做轻量探测:
- 用
try { localStorage.setItem('test', 'x'); localStorage.removeItem('test'); }测试,捕获SecurityError和QuotaExceededError - 失败时降级到 sessionStorage(临时可用)或 URL 参数 + 内存缓存(如 Map 对象)
- 避免仅用
typeof localStorage !== 'undefined'判断,这在无痕模式下仍为 true,但实际不可写
真实容量要按 UTF-8 字节算,不是字符串长度
localStorage 按底层存储字节数计费,而 JavaScript 的 .length 返回 UTF-16 字符数。一个 emoji(如 ?)占 4 字节但 "?".length === 1,直接用 JSON.stringify(obj).length 会严重高估空间。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 正确估算:用
new TextEncoder().encode(str).length获取真实 UTF-8 字节数 - 预留至少 10% 余量——key 名、JSON 引号、空格都占字节
- iOS Safari 实测可用空间常低于 5 MB,内存紧张时可能骤降至 0,不能依赖固定值
超限时必须 try-catch + 清理重试,不能丢数据
setItem() 超限会同步抛出 QuotaExceededError,但浏览器不提供剩余空间 API,也无法提前预知。错误发生后,需主动清理再重试:
立即学习“Java免费学习笔记(深入)”;
- 所有 setItem() 必须包裹 try-catch,专门匹配
e.name === 'QuotaExceededError' - 清理策略要精准:按前缀(如
cache_)、过期时间(解析 value 后检查expiresAt)、损坏项(JSON.parse 失败即剔除)逐个删除 - 禁止用
clear()——会误删 token、主题设置等关键字段
真要存大体积数据,换 IndexedDB 不要硬扛
localStorage 是字符串-only、同步阻塞、容量封顶的简易方案。当单条数据 >100 KB 或总量逼近 2–3 MB 时,就该切换技术栈:
- IndexedDB 支持二进制、事务、异步、数十 MB 甚至 GB 级容量(Safari iOS 16+ 已稳定支持)
- 对图片/离线资源等,优先用 Cache API + Service Worker,更适合静态资产缓存
- 微信 X5 内核等 WebView 实测 localStorage 不足 1 MB,更应默认走 IndexedDB 降级路径

















