阿福在街角开了一家「云边杂货店」——一个每天都有人进进出出的网页应用。开业第一天,他买了一大卷便利贴,把「今日推荐」「常客昵称」「购物车草稿」随手往墙上一贴,方便又直观。这就是 Web Storage 的感觉:拿起来就写,看一眼就懂,但便利贴能写的内容有限,而且整面墙贴满也就那么大。生意越来越好,阿福的会员档案堆成了山,便利贴墙再也塞不下了。于是社区给他盖了一座「云边图书馆」——IndexedDB。图书馆里有一排排书架,能存放成千上万份带编号的档案盒,还有检索台可以按姓名、日期快速翻找。唯一的代价是:借书、还书都要走管理员(事务)的流程,比贴便利贴麻烦得多。 这篇内容,就是阿福的「开店手记」:什么时候该贴便利贴,什么时候该把档案送进图书馆,以及如何安全地管理这两套存储体系。你会弄清楚 localStorage、sessionStorage、IndexedDB 的区别,学会索引查询、跨域隔离和隐私模式的坑,最终为你的前端项目选对本地存储方案。
对 AI 从业者:AI 应用的前端(对话记录、用户画像、离线缓存)全靠本地存储撑起体验。选错方案,轻则丢失数据,重则页面卡死。读懂这一篇,你就能写出既轻快又能承载大数据量的智能应用。
目录
可视化演示
展示Web Storage与IndexedDB的存储层级架构、多维特性对比及读写性能基准测试。
本节配有交互动效(进入视口后自动加载,约 3KB)。
存储层级架构 四层存储:Memory→Session→Local→IndexedDB 容量↑ 速度↓ 持久性↑ 从内存到IndexedDB,容量递增速度递减的存储层级体系
本地持久存储:永久便利贴(localStorage)
阿福在店门口贴了一张「永久会员牌」——localStorage。这张便利贴有个神奇之处:不管晚上打烊、周末休息,还是顾客把电脑关机重启,牌子上写的字都原封不动地挂在那儿。它就像被涂了一层防水胶,除非阿福亲手撕掉,否则永不脱落。这正是 localStorage 最核心的性格:持久。
在浏览器世界里,localStorage 是一个全局存储对象,不需要请示服务器,前端代码可以直接读写。它采用「键 → 值」的映射模型:每个数据都有一个唯一的钥匙(key),对应一段内容(value)。因为不需要网络往返,存取几乎是瞬时的;但它同时是同步 API——写数据时页面会停下来等操作完成,就像阿福贴便利贴时必须先停下手中的活。
localStorage 的「墙」是有面积的,通常约 5MiB,换算下来是五百多万字节。它只能存储字符串:数字要转成字符串,对象要经过 JSON 序列化才能贴上去。读取时再反序列化回来。这个约束决定了它适合存放「短、少、需要长期记住」的信息,比如主题色、用户昵称、购物车草稿。
阿福后来总结:凡是想让用户「下次来还记得」的小纸条,就往这面永久墙上贴。但要是纸条越积越多,他就要考虑换一个更大的仓库了。
- 键值映射localStorage 把字符串键映射到字符串值,就像便利贴的标签对应正文:
- 容量上限整面便利贴墙最多容纳约五百万字节:
- 单位换算1MiB 等于 1024 乘以 1024 字节,所以 5MiB 不是简单的五百万字,而是 5,242,880 字节:
会话存储:当日便利贴(sessionStorage)
阿福很快发现,有些便利贴根本不该留到第二天。比如顾客这次来挑货时用的「临时购物袋」、填写到一半的「意见反馈表」——这些信息只在顾客待店期间有意义。顾客一出门,纸条就应该立刻销毁,否则墙上全是过期的垃圾信息。这便是 sessionStorage 存在的意义。
sessionStorage 的 API 和 localStorage 长得一模一样,但性格截然相反:它的生命周期绑定在「标签页」上。只要那个标签页还开着,刷新页面、跳转路由,数据都在;一旦标签页关闭,数据立刻被清空。更细节的是,即使用户开两个标签页访问同一个网站,两个页签之间的 sessionStorage 也互相看不见——每个页签都有自己的「当日便利贴」,互不共享。
这种「页签级隔离」让 sessionStorage 非常适合承载瞬时状态:表单草稿、页面内滚动位置、一次性登录令牌。它同样遵守同源策略,只能读取自己来源下的数据。从生命周期看,它就像一张从顾客进门到出门都有效的「当日便利贴」,时间一到,自动消失。
回扣到阿福的杂货店:当日便利贴要的就是「人来有用,人走不留」。把临时状态交给 sessionStorage,既方便又不会让墙上的信息越积越乱。
T_{\text{life}} = [t_{\text{open}},\ t_{\text{close}}], \qquadt_{\text{close}} - t_{\text{open}} = \text{标签页存活时长}- 生命周期区间sessionStorage 的有效期是从打开到关闭的闭区间:
- 存活时长数据的存活时长等于关闭时刻减去打开时刻:
- 区间端点含义只要标签页未关闭,即使是刷新页面,数据依然在区间内,不会丢失。
存储API与存储事件
阿福把便利贴操作练成了一组标准手势:贴(set)、看(get)、撕(remove)、整墙清除(clear)。这套手势在 localStorage 和 sessionStorage 上完全通用。最常用的写法是 localStorage.setItem('key', 'value') 写入,localStorage.getItem('key') 读取,返回 null 表示没有这张便利贴。
写入时有一条铁律:value 必须是字符串。初学者最爱踩的坑是直接存对象:setItem('bag', {苹果: 2}),结果读出来是 "[object Object]"。正确的做法是先序列化再写入:
另一个有趣的能力是「存储事件」。当 A 标签页修改了 localStorage 的数据,同源的其他标签页会收到一个 storage 事件,事件对象里带着新值、旧值和被修改的键。这让多个标签页之间可以像对讲机一样传递消息:阿福在前台改了一个会员备注,收银台立刻听到广播并更新显示。
阿福把这套「一手贴一手撕」的功夫记得滚瓜烂熟。但他心里清楚:贴便利贴再熟练,也解决不了仓库不够大的问题。真正的大文件,得靠图书馆。
- 写入先序列化任何值 v 在写入前都要经过 serialize 变成字符串:
- 读取再解析读取时把字符串通过 parse 还原成原始对象:
- 字符串因果setItem 只接受字符串作为第二个参数,这正是 serialize 和 parse 必须成对出现的原因。
localStorage.setItem('bag', JSON.stringify({ apple: 2, juice: 1 }));const bag = JSON.parse(localStorage.getItem('bag'));
数据库基础:社区图书馆(IndexedDB)
会员档案堆到一万份时,便利贴墙彻底崩溃了——不是贴不下,而是找一张「三年前张姐的订单」要翻遍整面墙。社区给阿福盖了座图书馆:IndexedDB。它不存小纸条,只存「档案盒」。每个档案盒里可以放任意结构的数据:对象、数组、Blob、文件,统统能收。
图书馆的核心是「书架」,学名叫对象存储(object store)。每一本书上都有一个手写编号,这个编号叫 keyPath,是档案盒的身份证号。库管员(事务)负责所有存取动作:他保证一批操作要么全部完成、要么全部不执行,绝不会出现「书放进去了但登记簿没记」的中间状态。这就是事务的原子性。
IndexedDB 的另一大性格是「异步」。所有读写请求都返回 Promise 或 IDBRequest 对象,不会阻塞页面渲染。这对阿福是巨大的解脱:贴便利贴时页面会卡顿一下,但在图书馆里存取档案,店员照常接待顾客,后台悄悄完成工作。缺点也随之而来:API 冗长、概念多、还要处理回调,学习曲线比便利贴陡峭得多。
阿福把一万个会员档案送进图书馆后,长舒一口气。现在他面临新问题:如何在数千本书里一秒找到某个作者的著作?这就要用到索引检索了。
- 记录结构每条记录是键值对,键 k 等于值 v 的 keyPath 字段:
- 对象值v 可以是任意 JavaScript 对象,这正是与 localStorage 只能存字符串的本质区别:
- 主键来源keyPath 字段在对象内部,相当于档案盒自带编号,不需要额外生成。
索引查询:检索台与目录卡
图书馆虽然大,但如果没有检索台,找书依然是大海捞针。IndexedDB 提供了一整套「检索系统」:索引、范围查询和游标。索引相当于一本按姓名、日期或任意字段排列的目录卡。你在建库时声明 createIndex('author', 'author'),浏览器就默默维护一张「作者 → 档案位置」的映射表。
有了索引,查询就快得惊人。范围查询(range query)可以一次性捞出某个区间内的所有记录:比如查询所有姓「张」的作者,就是设置下限 '张'、上限 '赵',一个 IDBKeyRange.bound('张', '赵') 就搞定。它不需要把书架从头到尾翻一遍,而是先查索引卡,再精确定位到对应的档案盒,速度接近二分查找。
如果需求更灵活,比如「遍历所有 2024 年的订单做统计」,就可以用游标(cursor)。游标像一辆沿书架滑行的小推车,一格格扫过记录,每经过一条就把数据交给你处理。它适合批量操作和聚合计算。对比之下,localStorage 连「按字段查询」都做不到——每次只能读一个已知 key,想找某类数据只能遍历全部键名,相当于把整面便利贴墙一张张揭下来看。
阿福在检索台前感慨:这就是为什么会员档案必须进图书馆。便利贴上写一百个人的名字就能把他累坏,而图书馆一声令下,几秒就能把所有「张」姓会员请到面前。
R = \{\, v \mid \text{lower} \le v[\text{idx}] \le \text{upper} \,\}, \qquadO(\log n)- 范围条件结果只保留索引字段在闭区间内的记录:
- 对数复杂度利用索引树查询,不必扫描全部 n 条记录:
- 对比线性扫描没有索引时只能遍历全部记录,复杂度退化为 O(n),这就是「一格格翻书架」和「查目录卡」的差距。
选型对比:便利贴还是图书馆
现在阿福已经清楚两种存储的脾气,他开始琢磨:到底什么时候用便利贴,什么时候用图书馆?答案藏在「容量—复杂度—场景」三把尺子中。
容量上,localStorage 只有约 5MiB,而 IndexedDB 通常能达到数百 MB 甚至 GB 级,上限取决于磁盘剩余空间和浏览器总配额。复杂度上,Web Storage 的 API 是两行代码的事,IndexedDB 要处理打开数据库、建对象存储、建事务、监听回调,明显笨重。场景因此分化:登录态、主题色、购物车数量这类「KB 级小数据」交给 localStorage;聊天记录、离线文件、商品目录这类「MB 级结构化数据」交给 IndexedDB。
性能上还有一个微妙差异:localStorage 是同步的,写入 5MiB 数据会让页面肉眼可见地卡顿;IndexedDB 是异步的,即使写入几十 MB,界面也能保持流畅。开发体验也要纳入权衡:小项目用 IndexedDB 属于杀鸡用牛刀,大项目用 localStorage 则是牛刀杀鸡。阿福的经验法则,一句话:小纸条上墙,大档案入库。
阿福把这张对比表贴在收银台后面,每次选型都先问一句:数据大概多大?需不需要按字段查?答完这两问,方案自然浮出水面。
- 固定上限Web Storage 的容量被浏览器限制在 5MiB:
- 远小于符号
\ll表示容量数量级上的巨大差距: - 弹性上限IndexedDB 的量级受磁盘空间约束,是弹性的:
安全与限制:每面墙的围墙
阿福的便利贴墙虽然方便,但有一个外人看不到的「围墙」——同源策略。浏览器规定:只有协议(scheme)、域名(host)、端口(port)三者完全相同的两个页面,才能共享各自的 localStorage 和 IndexedDB。隔壁老王开了家「云边小卖部」,哪怕和阿福共用一台电脑,他的便利贴墙与阿福的墙也完全隔离,谁也读不到谁的字。
隐私模式(无痕窗口)是另一重限制。很多人以为隐私模式下浏览器完全不存数据,实际上浏览器会提供一块「用完即焚」的临时存储:你在隐私窗口里照样能用 localStorage 和 IndexedDB,但一旦关闭窗口,这些数据连同数据库文件被立刻焚毁,不留痕迹。此外,如果用户禁用了 cookie,部分浏览器也会连带禁用 Web Storage。前端代码读取时就会抛异常,必须做好兜底。
存储配额像一个看不见的天花板。lolerStorage 固定 5MiB;IndexedDB 则受「浏览器总配额」约束——Chrome 通常按磁盘剩余空间的一定比例划分给所有来源共享,一个域名占用太多,另一个域名就会申请失败。所以即使 IndexedDB 空间很大,也绝不是无限仓库。
阿福在围墙上挂了块警示牌:「本墙数据仅限本店使用」。他明白,便利贴墙和图书馆都有围墙,安全的第一课是尊重围墙,而不是想办法翻墙。
- 三元组定义源由网络协议 scheme、域名 host、端口 port 共同决定:
- 任意一项不同即跨源哪怕域名相同但端口不同,两个页面也属于不同的源:
- 隔离边界同源策略把存储数据锁在源的围墙内,跨源读取一律拒绝。
最佳实践:老店主的经验手册
开店三年,阿福总结出一本「存储经验手册」,第一条是统一封装。绝不在业务代码里裸调 localStorage.getItem,而是封装一层安全读写函数,内部处理 JSON 序列化、缺省值、异常捕获。这样将来从 localStorage 迁移到 IndexedDB,只改封装内部,业务代码一行不用动。
第二条是优雅降级与错误处理。隐私模式、配额已满、用户禁用存储,都可能导致读写失败。生产环境必须把 setItem 包在 try-catch 里,失败时提示用户或改用内存存储。写入大对象前先预估体积,超过 4.5MiB 就把数据引流到 IndexedDB。所有 IndexedDB 操作都要监听 onerror 事件,数据库打开失败时给出重试提示。
第三条是数据迁移要有剧本。从 localStorage 搬到 IndexedDB 时,先读旧数据、转换结构、写入新库,最后删掉旧键。迁移过程要设计「一次性执行」的标识,防止用户每次打开页面都跑一遍迁移逻辑。还可以借助 requestAnimationFrame 或空闲回调分批写入,避免一次性写太多对象导致界面卡顿。手册的核心理念:把存储当成基础设施来设计,而不是临时的贴纸条。
阿福把手册复印了三份,一份贴在收银台,一份放进图书馆,一份交给学徒。他终于明白:存储方案没有绝对的好坏,只有合适与否。把规则写进流程,数据才不会在某个深夜悄悄丢失。
- 命中分支当键存在于存储中,直接返回其值:
- 缺省分支当键不存在,返回调用者指定的默认值,避免程序崩溃:
- 分段函数含义读取操作被封装成永不失败的分段函数,这就是工具函数的核心契约。
AI 应用场景
场景一:AI 对话应用的历史记录持久化
阶段:数据持久化层设计
解决的问题:传统做法把对话历史存在内存里,刷新页面就全部丢失;若每次翻历史都向后端请求,又慢又费流量。用 IndexedDB 在本地存全量对话记录,配合索引按会话 ID、时间戳快速翻页,体验接近原生 App。
具体案例:一个检索增强生成(RAG)聊天工具,把用户每条消息的文本、向量摘要、引用来源存入 IndexedDB 的 messages 对象存储,并按 sessionId + timestamp 建立复合索引。用户回看历史时,用一次 IDBKeyRange.bound 范围查询直接捞出整个会话,毫秒级渲染。
场景二:AI 工具的偏好设置与临时状态
阶段:前端状态管理
解决的问题:用户选择的模型、温度参数、主题模式属于「短小且需长期记住」的信息,用 localStorage 最合适;而「当前正在编辑但未发送的提示词草稿」属于瞬时状态,应放入 sessionStorage,避免标签页误关后残留污染下一次输入。
具体案例:一个 AI 绘画平台记住用户上次选择的模型 stable-diffusion-xl 和采样步数 30,同时把「正在创作但未完成」的描述词存入 sessionStorage。用户误关标签页后,只要页面没关,刷新就能恢复草稿。
场景三:离线优先的 AI 知识库客户端
阶段:离线数据缓存与同步
解决的问题:AI 知识库在弱网环境下无法访问,体验极差。用 IndexedDB 作为客户端缓存层,把文档、词向量、索引快照存在本地,查询时先查本地,命中即返回,未命中再请求服务端,并利用事务保证缓存写入的原子性。
具体案例:一个医疗 AI 助手 App,首次联网时把常用药品数据库(约 200MB)写入 IndexedDB,之后在飞机、地铁等离线场景中,医生依然能按药品名索引快速检索本地档案。存储配额超出时自动清理最久未访问的缓存记录。
Python 演示
"""教学演示:用 Python 模拟 Web Storage 与 IndexedDB 的核心行为。只做教学用,并非真实浏览器 API 实现。"""import jsonimport time class QuotaExceededError(Exception): pass class WebStorage: """模拟 localStorage(持久) 与 sessionStorage(过期)""" def __init__(self, lifetime=None): self._store = {} self._lifetime = lifetime self._created_at = time.time() def set_item(self, key, value): self._store[key] = str(value) # 值必须转成字符串 if len(json.dumps(self._store, ensure_ascii=False)) > 5 * 1024 * 1024: raise QuotaExceededError("便利贴墙已满: 超过 5MiB") def get_item(self, key): return self._store.get(key) def remove_item(self, key): self._store.pop(key, None) def clear(self): self._store.clear() @property def expired(self): if self._lifetime is None: return False return time.time() - self._created_at > self._lifetime class IndexedDBStore: """简化版 IndexedDB: 对象存储 + 索引范围查询""" def __init__(self, index_name="author"): self._records = [] self._index_name = index_name def add(self, obj): self._records.append(obj) def query(self, low=None, high=None): """按索引字段做范围查询, 返回列表""" idx = self._index_name result = [] for rec in self._records: v = rec.get(idx) if low is not None and v < low: continue if high is not None and v > high: continue result.append(rec) return result if __name__ == "__main__": # 便利贴: 永久保存主题设置 sticky = WebStorage() sticky.set_item("theme", "night") sticky.set_item("model", "gpt-4o") print("localStorage 读取:", sticky.get_item("theme")) # 当日便利贴: 标签页关闭即过期 temp = WebStorage(lifetime=5) temp.set_item("draft", "未完成的提示词") print("sessionStorage 过期前:", temp.get_item("draft")) time.sleep(6) print("sessionStorage 过期后:", "已清空" if temp.expired else temp.get_item("draft")) # 图书馆: 存对象 + 按作者索引查询 db = IndexedDBStore("author") db.add({"id": 1, "title": "JavaScript 指南", "author": "张三"}) db.add({"id": 2, "title": "CSS 权威", "author": "李四"}) db.add({"id": 3, "title": "HTML 入门", "author": "张三"}) hits = db.query("张三", "张三") print("按作者=张三查询:", [r["title"] for r in hits])
self._store[key] = str(value)模拟 Web Storage 的铁律:一切值先转字符串,对象需自行序列化。if len(json.dumps(self._store)) > 5 * 1024 * 1024模拟 5MiB 配额检查,超限就抛异常。@property def expired模拟 sessionStorage 的过期判断,到期后数据视为不可用。db = IndexedDBStore("author")模拟在 IndexedDB 中声明按author字段建立的索引。for rec in self._records:模拟没有索引树时的线性扫描;真实场景中 IndexedDB 用 B+ 树将复杂度降到 O(log n)。if low is not None and v < low模拟范围查询的下界过滤,对应IDBKeyRange.bound(low, high)的语义。
常见误区
误区我以为 localStorage 和普通对象一样,可以直接 `setItem('bag', { apple: 2 })`,结果读出来是 `"[object Object]"`,还以为浏览器出 bug 了。
正解localStorage 的值必须是字符串。存储对象前必须先 JSON.stringify,读取后再 JSON.parse。可以把 localStorage 想象成便利贴:它只能写文字,贴不上一整只碗。你只能把碗画在纸上(序列化),需要时再照着纸复原(反序列化)。
为何常见:因为日常写代码时 JavaScript 对象随手即用,很容易忽略「存储层传输格式」的约束,只有读取到 [object Object] 才意识到问题。
误区我以为 sessionStorage 和 localStorage 的主要区别是「关浏览器就没了」,直到我关掉一个标签页调试时发现数据也没了,才发觉不对。
正解sessionStorage 的生命周期绑定的是「标签页」,不是浏览器。关闭产生它的标签页,数据立刻清除;即使浏览器没关,只要标签页关闭,数据就没了。反过来,标签页开着时刷新页面,数据反而还在。它更像是「跟顾客走的当日便利贴」,而不是「跟店走的永久牌」。
为何常见:很多人望文生义,把「session(会话)」理解为浏览器会话,而忽略了浏览器对「会话」的精确定义是「标签页级别的上下文」。
误区我刚接触 IndexedDB 时按 localStorage 的习惯直接 `db.add(record)`,发现后面代码马上执行了,数据却还没写进去,调试了半天。
正解IndexedDB 从设计上就是异步 API,所有操作都要监听成功/失败回调或使用 Promise。这就像图书馆的管理员:你把书交给他,他会登记入库,但不会让你的脚步停下来等。绝不能假设调用完函数数据立即落盘,否则会读到旧数据。
为何常见:因为 Web Storage 的同步体验先入为主,人们容易用同步思维理解数据库操作,忽略了大数据量场景下同步阻塞页面渲染的代价。
误区我看 localStorage 用着挺爽,就把一整年的订单日志都塞进去,直到页面保存时卡了好几秒,最后报错配额超限。
正解localStorage 大约只有 5MiB,而且写入是同步的,一次性写入大字符串会阻塞主线程,导致页面卡顿甚至崩溃。把它当作「放少量重要纸条的墙面」,而不是「仓库」。真正的大块头(文件、日志、大量结构化数据)应该交给 IndexedDB。
为何常见:因为现代浏览器给人「内存很大」的错觉,而 localStorage 的 5MiB 上限不会在使用中主动提醒,只有撑爆时才发现。
误区我以为开了无痕窗口,浏览器就变成了一个完全失忆的人,所有 localStorage 和 IndexedDB 都不能用。
正解隐私模式会提供一块「用完即焚」的临时存储空间:在无痕窗口内,localStorage 和 IndexedDB 照常可读可写,只是数据被隔离在临时分区中,窗口全部关闭后立即销毁。所以代码里不能假设隐私模式下读取必然失败,而是把「可能失败」作为默认前提来兜底。
为何常见:人们把「不留下浏览记录」误解为「完全禁用存储」,忽略了隐私模式的实现方式是「隔离 + 短生命周期」,而不是「禁用功能」。
面试题
Q1. localStorage 能存储的数据类型是什么?
- A.只能是字符串← 正确答案
- B.只能是数字
- C.任意 JS 对象
- D.数组与对象
答案:A。localStorage 的值必须是字符串,存对象前要 JSON.stringify,读取时再 JSON.parse。这就像便利贴只能写字,贴不上一只碗。选 B 错在数字也会被转成字符串存储;选 C、D 错在对象不能直接存储。
Q2. sessionStorage 的数据在以下哪种情况下被清除?
- A.标签页关闭即清← 正确答案
- B.浏览器重启仍存
- C.数据永久保留
- D.刷新页面即清
答案:A。sessionStorage 的生命周期绑定标签页,关闭标签页即清除。刷新页面数据仍然保留,所以 D 错;B、C 描述的是 localStorage 的特性。这就像「当日便利贴」,顾客(标签页)出门就销毁。
Q3. localStorage 的常见容量上限约为多少?
- A.5 MB 左右← 正确答案
- B.500 KB 左右
- C.50 MB 左右
- D.无任何限制
答案:A。规范建议约 5MiB,即 5 × 1024 × 1024 字节。B 是旧版 cookie 的量级;C 超标;D 错误,IndexedDB 才有弹性空间,localStorage 有明确配额。
Q4. 关于 IndexedDB 的说法,哪些是正确的?
- A.可存结构化对象← 正确答案
- B.支持索引查询← 正确答案
- C.同步阻塞页面
- D.基于事务机制← 正确答案
答案:ABD。IndexedDB 的对象存储可直接保存任意结构化数据,支持建索引和范围查询,并通过事务保证一致性。C 错在 IndexedDB 是异步 API,不会阻塞页面渲染,这正是它适合大数据量的原因。
Q5. 同源策略中,「源」的组成不包括以下哪项?
- A.网络协议类型
- B.主机域名地址
- C.端口号配置
- D.页面路径地址← 正确答案
答案:D。源 = 协议 + 域名 + 端口,这是存储隔离的边界围栏。页面路径不属于源的组成部分:同一个域名下 /a.html 和 /b.html 是同源,可以互相读取 localStorage。
Q6. storage 事件在以下哪种情况下触发?
- A.同页签内存修改
- B.其他页签同源修改← 正确答案
- C.服务端数据变更
- D.每次刷新页面时
答案:B。storage 事件仅在「其他标签页」修改同源数据时触发,且只有 localStorage 触发,sessionStorage 不触发。同页签修改自己会直接拿到值,不会收到事件。它像店里的广播:收银台改备注,前台听到更新。
Q7. IndexedDB 索引查询的优势有哪些?
- A.避免全表扫描← 正确答案
- B.支持范围查询← 正确答案
- C.数据自动加密
- D.无需维护索引
答案:AB。索引把查询从 O(n) 降到 O(log n),并支持 IDBKeyRange 范围查询。C 错在索引与加密无关;D 错在索引需要开发者在建库时显式创建,浏览器只是负责维护。
Q8. 关于隐私模式下的本地存储,下列说法正确的是?
- A.完全禁用存储
- B.存储临时且有隔离← 正确答案
- C.数据与正常模式互通
- D.关闭后仍长期保存
答案:B。隐私模式提供「用完即焚」的临时存储:能读写,但隔离在临时分区中,窗口关闭即销毁,与正常模式互不相通。A 错在它仍可用;C、D 与隔离和销毁的事实相反。
Q9. 需要存储 200MB 离线数据时,选择 IndexedDB 而非 localStorage 的核心原因是什么?
- A.容量大且异步处理← 正确答案
- B.同步读写更简单
- C.无需编写任何代码
- D.能自动同步服务器
答案:A。200MB 远超 localStorage 的 5MiB 配额,且 IndexedDB 的异步 API 不会阻塞 UI。B 的同步反而是 Web Storage 在大数据量下的缺点;C、D 都不属于 IndexedDB 的能力,它不会自动同步服务器。
Q10. 在多个标签页之间同步前端本地数据,哪些方案是可行的?
- A.storage 事件监听← 正确答案
- B.BroadcastChannel← 正确答案
- C.读取他人本地存储
- D.使用共享内存变量
答案:AB。storage 事件本身就是为跨标签页同步设计的;BroadcastChannel 是更通用的跨文档消息通道。C 违反同源策略,无法读取其他源的数据;D 错在 JavaScript 的内存变量无法跨标签页共享,每个页签都是隔离的。