【红宝书笔记精简版】第二十五章 客户端存储

目录

25.1 cookie

25.1.1 限制

 25.1.2 cookie 的构成

25.1.3 JavaScript 中的 cookie

25.1.4 子 cookie

25.1.5 使用 cookie 的注意事项

25.2 Web Storage

25.2.1 Storage 类型

25.2.2 sessionStorage 对象

25.2.3 localStorage 对象

25.2.4 存储事件

25.2.5 限制

25.3 IndexedDB

25.3.1 数据库

25.3.2 对象存储

25.3.3 事务

25.3.4 插入对象

25.3.8 索引

25.3.9 并发问题

25.3.10 限制

25.4 小结


25.1 cookie

HTTP cookie 通常也叫作 cookie,最初用于在客户端存储会话信息。这个规范要求服务器在响应 HTTP 请求时,通过发送 Set-Cookie HTTP 头部包含会话信息。例如,下面是包含这个头部的一个 HTTP 响应:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value

25.1.1 限制

cookie 是与特定域绑定的。设置 cookie 后,它会与请求一起发送到创建它的域。这个限制能保证 cookie 中存储的信息只对被认可的接收者开放,不被其他域访问。

通常,只要遵守以下大致的限制,就不会在任何浏览器中碰到问题:

 不超过 300 个 cookie;
 每个 cookie 不超过 4096 字节;
 每个域不超过 20 个 cookie;
 每个域不超过 81920 字节。

每个域能设置的 cookie 总数也是受限的,但不同浏览器的限制不同。

如果 cookie 总数超过了单个域的上限,浏览器就会删除之前设置的 cookie。IE 和 Opera 会按照最近 最少使用(LRU,Least Recently Used)原则删除之前的 cookie,以便为新设置的 cookie 腾出空间。Firefox 好像会随机删除之前的 cookie,因此为避免不确定的结果,最好不要超出限制。

 25.1.2 cookie 的构成

cookie 在浏览器中是由以下参数构成的。
名称:唯一标识 cookie 的名称。cookie 名不区分大小写,因此 myCookie 和 MyCookie 是同一 个名称。不过,实践中最好将 cookie 名当成区分大小写来对待,因为一些服务器软件可能这样 对待它们。cookie 名必须经过 URL 编码。
:存储在 cookie 里的字符串值。这个值必须经过 URL 编码。
:cookie 有效的域。发送到这个域的所有请求都会包含对应的 cookie。这个值可能包含子域(如 www.wrox.com),也可以不包含(如.wrox.com 表示对 wrox.com 的所有子域都有效)。如果不明 确设置,则默认为设置 cookie 的域。
路径:请求 URL 中包含这个路径才会把 cookie 发送到服务器。例如,可以指定 cookie 只能由 http://www.wrox.com/books/访问,因此访问 http://www.wrox.com/下的页面就不会发送 cookie,即 使请求的是同一个域。
过期时间:表示何时删除 cookie 的时间戳(即什么时间之后就不发送到服务器了)。默认情况下, 浏览器会话结束后会删除所有 cookie。不过,也可以设置删除 cookie 的时间。这个值是 GMT 格 式(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定删除 cookie 的具体时间。这样即使关闭 浏览器 cookie 也会保留在用户机器上。把过期时间设置为过去的时间会立即删除 cookie。
安全标志:设置之后,只在使用 SSL 安全连接的情况下才会把 cookie 发送到服务器。例如,请 求 https://www.wrox.com 会发送 cookie,而请求 http://www.wrox.com 则不会。

这些参数在 Set-Cookie 头部中使用分号加空格隔开,比如:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com
Other-header: other-header-value

这个头部设置一个名为"name"的 cookie,这个 cookie 在 2007 年 1 月 22 日 7:10:24 过期,对 www.wrox.com 及其他 wrox.com 的子域(如 p2p.wrox.com)有效。 安全标志 secure 是 cookie 中唯一的非名/值对,只需一个 secure 就可以了。比如:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; domain=.wrox.com; path=/; secure
Other-header: other-header-value

这里创建的 cookie 对所有 wrox.com 的子域及该域中的所有页面有效(通过 path=/指定)。不过, 这个 cookie 只能在 SSL 连接上发送,因为设置了 secure 标志。

25.1.3 JavaScript 中的 cookie

25.1.4 子 cookie

为绕过浏览器对每个域 cookie 数的限制,有些开发者提出了子 cookie 的概念。子 cookie 是在单个 cookie 存储的小块数据,本质上是使用 cookie 的值在单个 cookie 中存储多个名/值对。最常用的子 cookie 模式如下:

name=name1=value1&name2=value2&name3=value3&name4=value4&name5=value5

cookie 的格式类似于查询字符串。这些值可以存储为单个 cookie,而不用单独存储为自己的名/ 值对。结果就是网站或 Web 应用程序能够在单域 cookie 数限制下存储更多的结构化数据。

25.1.5 使用 cookie 的注意事项

还有一种叫作 HTTP-only 的 cookie。HTTP-only 可以在浏览器设置,也可以在服务器设置,但只能 在服务器上读取,这是因为 JavaScript 无法取得这种 cookie 的值。 因为所有 cookie 都会作为请求头部由浏览器发送给服务器,所以在 cookie 中保存大量信息可能会影 响特定域浏览器请求的性能。保存的 cookie 越大,请求完成的时间就越长。即使浏览器对 cookie 大小有 限制,最好还是尽可能只通过 cookie 保存必要信息,以避免性能问题。

25.2 Web Storage

Web Storage 的第 2 版定义了两个对象:localStorage 和 sessionStorage。localStorage 是 永久存储机制,sessionStorage 是跨会话的存储机制。这两种浏览器存储 API 提供了在浏览器中不受页面刷新影响而存储数据的两种方式。

25.2.1 Storage 类型

Storage 类型用于保存名/值对数据,直至存储空间上限(由浏览器决定)。Storage 的实例与其他 对象一样,但增加了以下方法。

 clear():删除所有值;不在 Firefox 中实现。
 getItem(name):取得给定 name 的值。
 key(index):取得给定数值位置的名称。
 removeItem(name):删除给定 name 的名/值对
 setItem(name, value):设置给定 name 的值。

25.2.2 sessionStorage 对象

sessionStorage 对象只存储会话数据,这意味着数据只会存储到浏览器关闭。这跟浏览器关闭时 会消失的会话 cookie 类似。存储在 sessionStorage 中的数据不受页面刷新影响,可以在浏览器崩溃 并重启后恢复。(取决于浏览器,Firefox 和 WebKit 支持,IE 不支持。)

因为 sessionStorage 对象是 Storage 的实例,所以可以通过使用 setItem()方法或直接给属 性赋值给它添加数据。下面是使用这两种方式的例子:

// 使用方法存储数据
sessionStorage.setItem("name", "Nicholas");
// 使用属性存储数据
sessionStorage.book = "Professional JavaScript";

可以结合 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 对象应该主要用于存储只在会话期间有效的小块数据。如果需要跨会话持久存储 数据,可以使用 globalStorage 或 localStorage。 

25.2.3 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 数据不受页面刷新影响,也不会因关闭窗口、标签页或重新启动浏览 器而丢失。

25.2.4 存储事件

每当 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 事 件不会区分这两者。

25.2.5 限制

与其他客户端数据存储方案一样,Web Storage 也有限制。具体的限制取决于特定的浏览器。一般 来说,客户端数据的大小限制是按照每个源(协议、域和端口)来设置的,因此每个源有固定大小的数据存储空间。分析存储数据的页面的源可以加强这一限制。

25.3 IndexedDB

IndexedDB 的设计几乎完全是异步的。为此,大多数操作以请求的形式执行,这些请求会异步执行, 产生成功的结果或错误。绝大多数 IndexedDB 操作要求添加 onerror 和 onsuccess 事件处理程序来确 定输出。

25.3.1 数据库

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;
}; 

25.3.2 对象存储

建立了数据库连接之后,下一步就是使用对象存储。

假设要存储包含用户名、密码等内容的用户记录。可以用如下对象来表示一条记录:

let user = {
 username: "007",
 firstName: "James",
 lastName: "Bond",
 password: "foo"
};

25.3.3 事务

创建了对象存储之后,剩下的所有操作都是通过事务完成的。事务要通过调用数据库对象的 transaction()方法创建。任何时候,只要想要读取或修改数据,都要通过事务把所有修改操作组织 起来。最简单的情况下,可以像下面这样创建事务:

let transaction = db.transaction(); 

如果想要访问多个对象存储,可以给第 一个参数传入一个字符串数组:

let transaction = db.transaction("users", "readwrite"); 

25.3.4 插入对象

拿到了对象存储的引用后,就可以使用 add()或 put()写入数据了。这两个方法都接收一个参数, 即要存储的对象,并把对象保存到对象存储。这两个方法只在对象存储中已存在同名的键时有区别。这 种情况下,add()会导致错误,而 put()会简单地重写该对象。更简单地说,可以把 add()想象成插入 新值,而把 put()想象为更新值。因此第一次初始化对象存储时,可以这样做:

// users 是一个用户数据的数组
for (let user of users) {
 store.add(user);
} 

每次调用 add()或 put()都会创建对象存储的新更新请求。如果想验证请求成功与否,可以把请求 对象保存到一个变量,然后为它添加 onerror 和 onsuccess 事件处理程序:

// users 是一个用户数据的数组
let request,
 requests = [];
for (let user of users) {
 request = store.add(user);
 request.onerror = () => {
 // 处理错误
 };
 request.onsuccess = () => {
 // 处理成功
 };
 requests.push(request);
} 

25.3.5 通过游标查询

25.3.6 键范围

25.3.7 设置游标方向

25.3.8 索引

对某些数据集,可能需要为对象存储指定多个键。例如,如果同时记录了用户 ID 和用户名,那可能 需要通过任何一种方式来获取用户数据。为此,可以考虑将用户 ID 作为主键,然后在用户名上创建索引。

25.3.9 并发问题

IndexedDB 虽然是网页中的异步 API,但仍存在并发问题。如果两个不同的浏览器标签页同时打开了同一个网页,则有可能出现一个网页尝试升级数据库而另一个尚未就绪的情形。有问题的操作是设置 数据库为新版本,而版本变化只能在浏览器只有一个标签页使用数据库时才能完成。

25.3.10 限制

IndexedDB 的很多限制实际上与 Web Storage 一样。首先,IndexedDB 数据库是与页面源(协议、域 和端口)绑定的,因此信息不能跨域共享。这意味着 www.wrox.com 和 p2p.wrox.com 会对应不同的数据 存储。 其次,每个源都有可以存储的空间限制。当前 Firefox 的限制是每个源 50MB,而 Chrome 是 5MB。 移动版 Firefox 有 5MB 限制,如果用度超出配额则会请求用户许可。 Firefox 还有一个限制——本地文本不能访问 IndexedDB 数据库。Chrome 没有这个限制。因此在本 地运行本书示例时,要使用 Chrome。

25.4 小结

Web Storage 定义了两个对象用于存储数据:sessionStorage 和 localStorage。前者用于严格 保存浏览器一次会话期间的数据,因为数据会在浏览器关闭时被删除。后者用于会话之外持久保存数据。 IndexedDB 是类似于 SQL 数据库的结构化数据存储机制。不同的是,IndexedDB 存储的是对象,而 不是数据表。对象存储是通过定义键然后添加数据来创建的。游标用于查询对象存储中的特定数据,而 索引可以针对特定属性实现更快的查询。 有了这些存储手段,就可以在客户端通过使用 JavaScript 存储可观的数据。因为这些数据没有加密, 所以要注意不能使用它们存储敏感信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值