
2025-04-18 日报 Day160

今日的鸡汤
别让人生就停留在当下,从现在开始,改变朋友圈、改变位置、改变心态和思维,去发现更好的生活,塑造更优秀的自己。
今日学习内容
1、JS 红皮书 P759-771 第二十五章:客户端存储
今日笔记
1、Web Storage: Web Storage 的目的是解决通过客户端存储不需要频繁发送回服务器的数据时使用 cookie 的问题。
Web Storage 规范最新的版本是第 2 版,这一版规范主要有两个目标:
提供在 cookie 之外的存储会话数据的途径;
提供跨会话持久化存储大量数据的机制。
Web Storage 的第 2 版定义了两个对象:localStorage 和 sessionStorage。localStorage 是永久存储机制,sessionStorage 是跨会话的存储机制。这两种浏览器存储 API 提供了在浏览器中不受页面刷新影响而存储数据的两种方式。
- storage类型: Storage 类型用于保存名/值对数据,直至存储空间上限(由浏览器决定)。Storage 的实例与其他对象一样,但增加了以下方法。
clear():删除所有值;不在 Firefox 中实现。
getItem(name):取得给定 name 的值。
key(index):取得给定数值位置的名称。
removeItem(name):删除给定 name 的名/值对。
setItem(name, value):设置给定 name 的值。
注意 Storage 类型只能存储字符串。非字符串数据在存储之前会自动转换为字符串。注意,这种转换不能在获取数据时撤销。 - sessionStorage对象: sessionStorage 对象只存储会话数据,这意味着数据只会存储到浏览器关闭。这跟浏览器关闭时会消失的会话 cookie 类似。存储在 sessionStorage 中的数据不受页面刷新影响,可以在浏览器崩溃并重启后恢复。
因为 sessionStorage 对象与服务器会话紧密相关,所以在运行本地文件时不能使用。存储在sessionStorage 对象中的数据只能由最初存储数据的页面使用,在多页应用程序中的用处有限。
因为 sessionStorage 对象是 Storage 的实例,所以可以通过使用 setItem()方法或直接给属性赋值给它添加数据。下面是使用这两种方式的例子:
// 使用方法存储数据
sessionStorage.setItem(“name”, “Nicholas”);
// 使用属性存储数据
sessionStorage.book = “Professional JavaScript”;
所有现代浏览器在实现存储写入时都使用了同步阻塞方式,因此数据会被立即提交到存储。具体API 的实现可能不会立即把数据写入磁盘(而是使用某种不同的物理存储),但这个区别在 JavaScript 层面是不可见的。通过 Web Storage 写入的任何数据都可以立即被读取。
老版 IE 以异步方式实现了数据写入,因此给数据赋值的时间和数据写入磁盘的时间可能存在延迟。对于少量数据,这里的差别可以忽略不计,但对于大量数据,就可以注意到 IE 中 JavaScript 恢复执行的速度比其他浏览器更快。这是因为实际写入磁盘的进程被转移了。在 IE8 中可以在数据赋值前调用begin()、之后调用 commit()来强制将数据写入磁盘。比如:
// 仅适用于 IE8
sessionStorage.begin();
sessionStorage.name = “Nicholas”;
sessionStorage.book = “Professional JavaScript”;
sessionStorage.commit();
以上代码确保了”name”和”book”在 commit()调用之后会立即写入磁盘。调用 begin()是为了保证在代码执行期间不会有写入磁盘的操作。对于少量数据,这个过程不是必要的,但对于较大的数据量,如文档,则可以考虑使用这种事务性方法。
对存在于 sessionStorage 上的数据,可以使用 getItem()或直接访问属性名来取得。下面是使用这两种方式的例子:
// 使用方法取得数据
let name = sessionStorage.getItem(“name”);
// 使用属性取得数据
let book = sessionStorage.book;
可以结合 sessionStorage 的 length 属性和 key()方法遍历所有的值:
for (let i = 0, len = sessionStorage.length; i < len; i++){
let key = sessionStorage.key(i);
let value = sessionStorage.getItem(key);
alert(${key}=
${value}`);
}
要从 sessionStorage 中删除数据,可以使用 delete 操作符直接删除对象属性,也可以使用removeItem()方法。下面是使用这两种方式的例子:
// 使用 delete 删除值
delete sessionStorage.name;
// 使用方法删除值
sessionStorage.removeItem(“book”);
sessionStorage 对象应该主要用于存储只在会话期间有效的小块数据。如果需要跨会话持久存储数据,可以使用 globalStorage 或 localStorage。 - localStorage对象: 在修订的 HTML5 规范里,localStorage 对象取代了 globalStorage,作为在客户端持久存储数据的机制。要访问同一个 localStorage 对象,页面必须来自同一个域(子域不可以)、在相同的端口上使用相同的协议。
因 为 localStorage 是 Storage 的实例,所以可以像使用 sessionStorage 一样使用localStorage。比如下面这几个例子:
// 使用方法存储数据
localStorage.setItem(“name”, “Nicholas”);
// 使用属性存储数据
localStorage.book = “Professional JavaScript”;
// 使用方法取得数据
let name = localStorage.getItem(“name”);
// 使用属性取得数据
let book = localStorage.book;
两种存储方法的区别在于,存储在 localStorage 中的数据会保留到通过 JavaScript 删除或者用户清除浏览器缓存。localStorage 数据不受页面刷新影响,也不会因关闭窗口、标签页或重新启动浏览器而丢失。 - 存储事件: 每当 Storage 对象发生变化时,都会在文档上触发 storage 事件。使用属性或 setItem()设置值、使用 delete 或 removeItem()删除值,以及每次调用 clear()时都会触发这个事件。这个事件的事件对象有如下 4 个属性。
domain:存储变化对应的域。
key:被设置或删除的键。
newValue:键被设置的新值,若键被删除则为 null。
oldValue:键变化之前的值。
可以使用如下代码监听 storage 事件:
window.addEventListener(“storage”,
(event) => alert(‘Storage changed for ${event.domain}’));
对于 sessionStorage 和 localStorage 上的任何更改都会触发 storage 事件,但 storage 事件不会区分这两者。 - 限制: 与其他客户端数据存储方案一样,Web Storage 也有限制。具体的限制取决于特定的浏览器。一般来说,客户端数据的大小限制是按照每个源(协议、域和端口)来设置的,因此每个源有固定大小的数据存储空间。分析存储数据的页面的源可以加强这一限制。
不同浏览器给 localStorage 和 sessionStorage 设置了不同的空间限制,但大多数会限制为每个源 5MB。
2、IndexedDB: Indexed Database API 简称 IndexedDB,是浏览器中存储结构化数据的一个方案。IndexedDB 背后的思想是创造一套 API,方便 JavaScript 对象的存储和获取,同时也支持查询和搜索。
IndexedDB 的设计几乎完全是异步的。为此,大多数操作以请求的形式执行,这些请求会异步执行,产生成功的结果或错误。绝大多数 IndexedDB 操作要求添加 onerror 和 onsuccess 事件处理程序来确定输出。 - 数据库: IndexedDB 是类似于 MySQL 或 Web SQL Database 的数据库。与传统数据库最大的区别在于,IndexedDB 使用对象存储而不是表格保存数据。IndexedDB 数据库就是在一个公共命名空间下的一组对象存储,类似于 NoSQL 风格的实现。
使用 IndexedDB 数据库的第一步是调用 indexedDB.open()方法,并给它传入一个要打开的数据库名称。如果给定名称的数据库已存在,则会发送一个打开它的请求;如果不存在,则会发送创建并打开这个数据库的请求。这个方法会返回 IDBRequest 的实例,可以在这个实例上添加 onerror 和onsuccess 事件处理程序。举例如下:
let db,
request,
version = 1;
request = indexedDB.open(“admin”, version);
request.onerror = (event) =>
alert(Failed to open: ${event.target.errorCode}
);
request.onsuccess = (event) => {
db = event.target.result;
}; - 对象存储: 建立了数据库连接之后,下一步就是使用对象存储。
- 事务: 创建了对象存储之后,剩下的所有操作都是通过事务完成的。事务要通过调用数据库对象的transaction()方法创建。
- 插入对象: 拿到了对象存储的引用后,就可以使用 add()或 put()写入数据了。
每次调用 add()或 put()都会创建对象存储的新更新请求。如果想验证请求成功与否,可以把请求对象保存到一个变量,然后为它添加 onerror 和 onsuccess 事件处理程序:
// users 是一个用户数据的数组
let request,
requests = [];
for (let user of users) {
request = store.add(user);
request.onerror = () => {
// 处理错误
};
request.onsuccess = () => {
// 处理成功
};
requests.push(request);
}
创建并填充了数据后,就可以查询对象存储了。 - 通过游标查询: 使用事务可以通过一个已知键取得一条记录。如果想取得多条数据,则需要在事务中创建一个游标。游标是一个指向结果集的指针。与传统数据库查询不同,游标不会事先收集所有结果。相反,游标指向第一个结果,并在接到指令前不会主动查找下一条数据。
- 键范围: 使用游标会给人一种不太理想的感觉,因为获取数据的方式受到了限制。使用键范围(key range)可以让游标更容易管理。
- 设置游标方向: openCursor()方法实际上可以接收两个参数,第一个是 IDBKeyRange 的实例,第二个是表示方向的字符串。
- 索引: 对某些数据集,可能需要为对象存储指定多个键。例如,如果同时记录了用户 ID 和用户名,那可能需要通过任何一种方式来获取用户数据。为此,可以考虑将用户 ID 作为主键,然后在用户名上创建索引。
- 并发问题: IndexedDB 虽然是网页中的异步 API,但仍存在并发问题。如果两个不同的浏览器标签页同时打开了同一个网页,则有可能出现一个网页尝试升级数据库而另一个尚未就绪的情形。有问题的操作是设置数据库为新版本,而版本变化只能在浏览器只有一个标签页使用数据库时才能完成。
- 限制: IndexedDB 的很多限制实际上与 Web Storage 一样。首先,IndexedDB 数据库是与页面源(协议、域和端口)绑定的,因此信息不能跨域共享。这意味着 www.wrox.com 和 p2p.wrox.com 会对应不同的数据存储。
其次,每个源都有可以存储的空间限制。当前 Firefox 的限制是每个源 50MB,而 Chrome 是 5MB。移动版 Firefox 有 5MB 限制,如果用度超出配额则会请求用户许可。
Firefox 还有一个限制——本地文本不能访问 IndexedDB 数据库。Chrome 没有这个限制。因此在本地运行本书示例时,要使用 Chrome。
3、小结: Web Storage 定义了两个对象用于存储数据:sessionStorage 和 localStorage。前者用于严格保存浏览器一次会话期间的数据,因为数据会在浏览器关闭时被删除。后者用于会话之外持久保存数据。
IndexedDB 是类似于 SQL 数据库的结构化数据存储机制。不同的是,IndexedDB 存储的是对象,而不是数据表。对象存储是通过定义键然后添加数据来创建的。游标用于查询对象存储中的特定数据,而索引可以针对特定属性实现更快的查询。
有了这些存储手段,就可以在客户端通过使用 JavaScript 存储可观的数据。因为这些数据没有加密,所以要注意不能使用它们存储敏感信息