浏览器存储机制

浏览器存储机制

我们在之前的章节当中梳理了 浏览器缓存机制 相关内容,乘热打铁,我们本章当中来看看浏览器存储机制的相关内容,主要涉及到 CookielocalStoragesessionStorageWeb SQLIndexedDB 相关内容,下面我们就一个一个来进行介绍

HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上,通常它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态,Cookie 主要用于以下三个方面

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

Cookie 的特点有下面这些

  • Cookie 的大小受限,一般为 4 KB
  • 同一个域名下存放 Cookie 的个数是有限制的,不同浏览器的个数不一样,一般为 20
  • Cookie 支持设置过期时间,当过期时自动销毁
  • 每次发起同域下的 HTTP 请求时,都会携带当前域名下的 Cookie
  • 支持设置为 HttpOnly,防止 Cookie 被客户端的 JavaScript 访问

Cookie 的原理可以如下图所示

第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会在响应头里面添加一个 Set-Cookie 选项,将 Cookie 放入到响应请求中,在浏览器第二次发请求的时候,会通过 Cookie 请求头部将 Cookie 信息发送给服务器,服务端会辨别用户身份,另外 Cookie 的过期时间、域、路径、有效期、适用站点都可以根据需要来指定

下面我们就看看如何使用代码来设置与获取 Cookie,先来看一个最为基本的简单用法

1
2
3
4
document.cookie = 'name=zhangsan'
document.cookie = 'age=18'

console.log(document.cookie) // name=zhangsan;age=18

下面的示例主要用于获取名为 test2Cookie

1
2
3
4
5
6
document.cookie = 'test1=Hello'
document.cookie = 'test2=World'

var myCookie = document.cookie.replace(/(?:(?:^|.*;\s*)test2\s*\=\s*([^;]*).*$)|^.*$/, '$1')

console.log(myCookie)

虽然 Cookie 使用起来很方便,但是它还是存在着一些缺陷,如下

  • Cookie 不够大,Cookie 的大小限制在 4KB 左右
  • Cookie 是紧跟域名的,同一个域名下的所有请求,都会携带 Cookie,过多的 Cookie 会带来巨大的性能浪费
  • 由于在 HTTP 请求中的 Cookie 是明文传递的,所以安全性成问题,除非用 HTTPS

另外我们还需要注意 Cookie 的安全性

属性 作用
value 如果用于保存用户登录态,应该将该值加密,不能使用明文的用户标识
http-only 不能通过 JavaScript 访问 Cookie,减少 XSS 攻击
secure 只能在协议为 HTTPS 的请求中携带
same-site 规定浏览器不能在跨域请求中携带 Cookie,减少 CSRF 攻击

HttpOnly 不支持读写,浏览器不允许脚本操作去更改 Cookie,所以为避免跨域脚本(XSS)攻击,通过 JavaScriptdocument.cookie 无法访问带有 HttpOnly 标记的 Cookie,它们只应该发送给服务端,如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记,如下

1
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2018 07:28:00 GMT; Secure; HttpOnly

标记为 SecureCookie 只应通过被 HTTPS 协议加密过的请求发送给服务端,但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障

localStorage

一种持久化的存储方式,也就是说如果不手动清除,数据就永远不会过期,它是采用键值对的方式存储数据,按域名将数据分别保存到对应数据库文件里,相比 Cookie 来说,它能保存更大的数据,localStorage 的特点有以下这些

  • 大小限制为 5MB ~ 10MB
  • 在同源的所有标签页和窗口之间共享数据
  • 数据仅保存在客户端,不与服务器进行通信
  • 数据持久存在且不会过期,重启浏览器后仍然存在
  • 对数据的操作是同步的

具体使用方式如下

1
2
3
4
5
6
7
8
9
10
11
// 通过 setItem() 增加一个数据项
localStorage.setItem('myName', 'zhangsan')

// 通过 getItem() 获取某个数据项
let me = localStorage.getItem('myName')

// 通过 removeItem() 移除某个数据项
localStorage.removeItem('myName')

// 移除所有数据项
localStorage.clear()

sessionStorage

与服务端的 Session 类似,sessionStorage 是一种会话级别的缓存,关闭浏览器时数据会被清除,需要注意的是 sessionStorage 的作用域是窗口级别的,也就是说不同窗口之间保存的 sessionStorage 数据是不能共享的,sessionStorage 的特点有以下这些

  • sessionStorage 的数据只存在于当前浏览器的标签页
  • 数据在页面刷新后依然存在,但在关闭浏览器标签页之后数据就会被清除
  • localStorage 拥有统一的 API 接口
  • 对数据的操作是同步的

具体使用方式如下

1
2
3
4
5
6
7
8
9
10
11
// 通过 setItem() 增加一个数据项
sessionStorage.setItem('myName', 'zhangsan')

// 通过 getItem() 获取某个数据项
let me = sessionStorage.getItem('myName')

// 通过 removeItem() 移除某个数据项
sessionStorage.removeItem('myName')

// 移除所有数据项
sessionStorage.clear()

Web SQL

Web SQL 数据库 API 实际上不是 HTML5 规范的一部分,而是一个单独的规范,它引入了一组 API 来使用 SQL 来操作客户端数据库

不过需要注意的是,HTML5 已经放弃 Web SQL 数据库

Web SQL Database 规范中定义的三个核心方法,如下

  • openDatabase,这个方法使用现有数据库或新建数据库来创建数据库对象
  • transaction,这个方法允许我们根据情况控制事务的提交或回滚
  • executeSql,这个方法用于执行真实的 SQL 语句

Web SQL 的特点(相比 CookieWebStorage 而言)

  • Web SQL 能方便进行对象存储
  • Web SQL 支持事务,能方便地进行数据查询和数据处理操作

具体使用方式如下

1
2
3
4
5
6
7
8
9
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024)

db.transaction(function (tx) {
// 执行查询操作
tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)')
// 执行插入操作
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")')
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")')
})

IndexedDB

IndexedDB 是一种底层 API,用于客户端存储大量结构化数据,包括文件、二进制大型对象,该 API 使用索引来实现对该数据的高性能搜索,虽然 Web Storage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太好用,IndexedDB 提供了一个解决方案,它的特点有以下这些

  • 键值对存储,IndexedDB 内部采用对象仓库(Object Store)存放数据,所有类型的数据都可以直接存入,包括 JavaScript 对象,对象仓库中,数据以键值对的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误
  • 数据操作是异步的,IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的,异步设计是为了防止大量数据的读写,拖慢网页的表现
  • 存储空间大,IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限
  • 支持二进制存储,IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)
  • 同源限制,IndexedDB 受到同源限制,每一个数据库对应创建它的域名,网页只能访问自身域名下的数据库,而不能访问跨域的数据库
  • 支持事务型,IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况

下面我们来看看如何使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var dbName = 'my_db'

var request = indexedDB.open(dbName, 2)

request.onerror = function (event) {
// 错误处理
}

request.onupgradeneeded = function (event) {
var db = event.target.result

// 建立一个对象仓库来存储我们客户的相关信息,我们选择 ssn 作为键路径(key path),因为 ssn 可以保证是不重复的
var objectStore = db.createObjectStore('customers', { keyPath: 'ssn' })

// 建立一个索引来通过姓名来搜索客户,名字可能会重复,所以我们不能使用 unique 索引
objectStore.createIndex('name', 'name', { unique: false })

// 使用邮箱建立索引,我们确保客户的邮箱不会重复,所以我们使用 unique 索引
objectStore.createIndex('email', 'email', { unique: true })

// 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
objectStore.transaction.oncomplete = function (event) {
// 将数据保存到新创建的对象仓库
var customerObjectStore = db.transaction('customers', 'readwrite').objectStore('customers')
customerData.forEach(function (customer) {
customerObjectStore.add(customer)
})
}
}

WebStorage、Cookie 和 IndexedDB 之间的区别

区别如下

特性 Cookie localStorage sessionStorage IndexedDB
数据生命周期 一般由服务器生成,可以以设置过期时间 除非被清理,否则一直存在 页面关闭就清理 除非被清理,否则一直存在
数据存储大小 4K 5M 5M 无限
与服务端通信 每次都会携带在 Header 中,对于请求性能影响 不参与 不参与 不参与

总结

正是浏览器存储、缓存技术的出现和发展,为我们的前端应用带来了无限的转机,近年来基于存储、缓存技术的第三方库层出不绝,此外还衍生出了 PWA 这样优秀的 Web 应用模型,简单总结一下上文涉及到的几个核心观点

  • Cookie 的本职工作并非本地存储,而是维持状态
  • Web StorageHTML5 专门为浏览器存储而提供的数据存储机制,不与服务端发生通信
  • Web SQL 数据库已经被废弃
  • IndexedDB 用于客户端存储大量结构化数据

另外除了上文当中所介绍到的一些方式,网上还有一些比较优秀的第三方主流数据库,但是这里就不详细展开了,想了解更多的话可以参考对应链接,列表如下

  • localForage,通过使用类似于 localStorage 的简单 API 来使用异步存储(IndexedDBWebSQL),进而改善你的 Web 应用程序的离线体验
  • PouchDB,一个浏览器内数据库,允许应用程序在本地保存数据,以便用户即使在离线时也可以享受应用程序的所有功能,另外数据在客户端之间是同步的,因此用户可以随时随地保持最新状态
  • Rxdb,一个 NoSQL 数据库,响应式的设计意味着你不仅可以查询当前状态,还可以订阅所有状态更改
  • NeDB,纯 JavaScript 实现,不依赖其它库,提供的 APIMongoDB API 的子集,重要的是它的速度非常快
  • Dexie.jsIndexedDB 的包装库,它提供了一套经过精心设计的 API,强大的错误处理,较强的可扩展性,此外它能够跟踪数据变化,支持 KeyRange(搜索不区分大小写,可设置匹方式和 OR 操作)

参考

# HTTP

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×