说明

随着 Web 应用程序的出现,直接在客户端存储用户信息的需求也随之出现。

这背后的想法是合理的:与特定用户相关的信息应该保存在用户的机器上。

无论是登录信息、个人偏好,还是其他数据,Web 应用程序提供者都需要有办法把它们保存在客户端。

对该问题的第一个解决方案就是 cookie,今天,cookie 只是在客户端存储数据的一个选项。

各种方式对比

cookie sessionStorage localStorage IndexedDB session
共同点 1、通过key value储存
2、同域名可用
储存位置 客户端 服务端
特点 随请求头每次提交 不随请求头提交,页面关闭后消失 不随请求头提交,可长期储存 安全
跨页/跨域 可跨页
不可跨域
不跨页
不可跨域
可跨页
不可跨域

=======================

cookie

说明

image.png

通常是服务器设置的,服务器发送给我们的响应里面,有个set cookie属性,浏览器发现有这个属性,就会帮我设置这些数据到浏览器中。
image.png
当发送请求的时候,浏览器默认会把cookie的内容加到请求中,发送给服务器,服务器就可以验证cookie的内容,从而判断发送的人是谁,或者发送的数据是否有效等等。

image.png
image.png

作用

image.png

原理示例

1、客户端发送请求(如登录等)
image.png

2、服务器验证信息,然后发回响应,响应里面就携带cookie,里面设置一些自定义的值
image.png

3、后续发送的请求都可以携带服务器发过来的cookie值,告诉服务器我是哪位
image.png

服务器创建cookie

image.png
image.png

类型

image.png

构成属性

名称 Name

唯一标识 cookie 的名称。cookie 名不区分大小写,因此 myCookie 和 MyCookie 是同一个名称。
不过,实践中最好将 cookie 名当成区分大小写来对待,因为一些服务器软件可能这样对待它们。cookie 名必须经过 URL 编码。

值 Value

存储在 cookie 里的字符串值。这个值必须经过 URL 编码。

作用域 Domain

image.png

过期时间 Expires

  1. //Expires=就是设置过期时间
  2. Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

表示何时删除 cookie 的时间戳(即什么时间之后就不发送到服务器了)。

默认情况下,浏览器会话结束后会删除所有 cookie。

不过,也可以设置删除 cookie 的时间。

这个值是 GMT 格式(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定删除 cookie 的具体时间。

这样即使关闭浏览器 cookie 也会保留在用户机器上。

把过期时间设置为过去的时间会立即删除 cookie。

secure / HttpOnly 属性

还有一种叫作 HTTP-only 的 cookie。

HTTP-only 可以在浏览器设置,也可以在服务器设置,但只能在服务器上读取,这是因为 JavaScript 无法取得这种 cookie 的值。
image.png

  1. Set-Cookie: name=value; domain=.wrox.com; path=/; secure

image.png

JS操作cookie

在 JavaScript 中处理 cookie 比较麻烦,因为接口过于简单,只有 BOM 的 document.cookie 属性。

获取

获取cookie的值不是很直观,因为直接获取的就是所有的cookie信息,需要自己弄个函数操作

  1. console.log(document.cookie)
  2. // 如,会得到这样的格式:"PSTM=1597907860; BD_UPN=12314753; BIDUPSID=DC5EB694E14FC353969330D1A6DD2556;"

所有名和值都是 URL 编码的,因此必须使用 decodeURIComponent()解码

设置

在设置值时,可以通过 document.cookie 属性设置新的 cookie 字符串。

这个字符串在被解析后会添加到原有 cookie 中。

设置 document.cookie 不会覆盖之前存在的任何 cookie,除非设置了已有的cookie。

  1. document.cookie = "name=Nicholas";
  2. // 虽然这样直接设置也可以,因为不需要在名称或值中编码任何字符,但最好还是使用 encodeURIComponent()对名称和值进行编码
  3. document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas");

删除

无法直接删除,只能重新设置max-age过期时间,让它直接过期
image.png

Js-cookie

第三方库,很方便的操作cookie
https://www.yuque.com/yejielin/mypn47/vgmuig#NnaU3

cookie 安全和局限

1、浪费流量
因为所有 cookie 都会作为请求头部由浏览器发送给服务器,所以在 cookie 中保存大量信息可能会影响特定域浏览器请求的性能。

保存的 cookie 越大,请求完成的时间就越长。即使浏览器对 cookie 大小有限制,最好还是尽可能只通过 cookie 保存必要信息,以避免性能问题。

因此cookie 逐渐被淘汰,被Web Storage 和 IndexedDB取代(见下)。

2、不安全
不要在 cookie 中存储重要或敏感的信息,因为都是明文传输,不会加密,除非你自己手动加密。

cookie 数据不是保存在安全的环境中,因此任何人都可能获得。

应该避免把信用卡号或个人地址等信息保存在 cookie 中。

3、大小限制
每次传输的大小大概是4kb,不大

4、客户端限制
目前浏览器是支持cookie,但是在其他程序不一定能支持,比如App、小程序等等。

=======================

Web Storage

说明

这个规范中的草案最终成为了 HTML5 的一部分,后来又独立成为自己的规范。

Web Storage 的目的是解决通过浏览器客户端存储信息,不需要频繁发送回服务器的数据时使用 cookie 的问题。

因为cookie每次都会跟着请求头提交给服务器,影响性能且没必要。

主要有两个目标:
1、提供在 cookie 之外的存储会话数据的途径;
2、提供跨会话持久化存储大量数据的机制。

两个对象:localStorage 和 sessionStorage。

localStorage 是永久存储机制,
sessionStorage 是跨会话的存储机制,关闭浏览器就消失(准确来说是关闭浏览器标签)。

只能存储字符串。非字符串数据在存储之前会自动转换为字符串,获取时也是字符串。

sessionStorage 对象

sessionStorage 对象只存储会话数据,浏览器关闭后消失。

存储在 sessionStorage 中的数据不受页面刷新影响,可以在浏览器崩溃并重启后恢复。

因为 sessionStorage 对象与服务器会话紧密相关,所以在运行本地文件时不能使用。

存储在sessionStorage 对象中的数据只能由最初存储数据的页面使用,跨标签页无法共用。

localStorage 对象

在修订的 HTML5 规范里,localStorage 对象可以浏览器长期存储数据,不会因为关闭浏览器消失。

要访问同一个 localStorage 对象,页面必须来自同一个域(子域不可以)、在相同的端口上使用相同的协议,可以跨页面共享数据。
image.png协议、ip、端口,3个相同的情况下才能访问

通用操作

sessionStorage 对象 和 localStorage 对象,都有的方法:

  1. // ================== 1、储存 ==================
  2. // 使用方法存储数据
  3. localStorage.setItem("name", "Nicholas");
  4. // 使用属性存储数据
  5. localStorage.book = "Professional JavaScript";
  6. // ================== 2、获取 ==================
  7. // 使用方法取得数据
  8. let name = localStorage.getItem("name");
  9. // 使用属性取得数据
  10. let book = localStorage.book;
  11. // 根据索引获取
  12. let name1 = localStorage.key(0)
  13. // ================== 3、遍历 ==================
  14. // 可以通过length 属性和 key(索引)方法遍历所有的值,也可以通过for in方法
  15. for(let i=0;i<localStorage.length;i++){
  16. console.log(localStorage.key(i))
  17. }
  18. // ================== 4、删除 ==================
  19. // 使用 delete 删除值
  20. delete sessionStorage.name;
  21. // 使用方法删除值
  22. sessionStorage.removeItem("name");
  23. // 清空所有
  24. sessionStorage.clear();

调试查看

在浏览器中,可以按F12(谷歌浏览器)打开DevTools,在Application这个标签里面可以看到。
image.png

image.png

DOM储存事件 storage

每当 Storage 对象发生变化时,都会在文档上触发 storage 事件。

使用属性或 setItem()设置值、使用 delete 或 removeItem()删除值,以及每次调用 clear()时都会触发这个事件。

这个事件的事件对象有如下 4 个属性。
 domain:存储变化对应的域。
 key:被设置或删除的键。
 newValue:键被设置的新值,若键被删除则为 null。
 oldValue:键变化之前的值。

  1. window.addEventListener("storage", (event) => alert('Storage changed for ${event.domain}'));

限制

与其他客户端数据存储方案一样,Web Storage 也有限制。

具体的限制取决于特定的浏览器。一般来说,客户端数据的大小限制是按照每个源(协议、域和端口)来设置的,因此每个源有固定大小的数据存储空间。

不同浏览器给 localStorage 和 sessionStorage 设置了不同的空间限制,但大多数会限制为每个源 5MB。

只能储存字符串类型的数据,其他的会转换成字符串 (包括JSON)

=======================

IndexedDB

说明

image.png

IndexedDB 用于代替目前已废弃的 Web SQL Database API。

优势

1、性能
IndexedDB 背后的思想是创造一套 API,方便 JavaScript 对象的存储和获取,同时也支持查询和搜索,性能比 sessionStorage 和 localforage 更好。

2、异步
IndexedDB 几乎完全是异步的,大多数操作以请求的形式执行,这些请求会异步执行,产生成功的结果或错误。

3、事务操作
image.png

事务:对数据库操作的一个操作单元

比如银行转账,A转100元给B,那么:
1、A的账户-100元
2、B的账户+100元
以上2条一起合并为1个操作单元,一起执行。如果其中一个操作失败了,那么全失败,全部回退到执行前。

4、储存空间
储存空间很大,不像sessionStorage 和 localforage 大概只有5M的储存空间。

而且不止可以储存字符串,可以储存对象、二进制数据(图片、文件等)

操作

1、数据库建立连接

  1. let db,
  2. request,
  3. version = 1;
  4. // 1、打开数据库,参数为数据库名称,和版本(整数)。
  5. // 如果给定名称的数据库已存在,则会发送一个打开它的请求;
  6. // 如果不存在,则会发送创建并打开这个数据库的请求。
  7. request = indexedDB.open("admin", version);
  8. // event.target 和 request 指向同一个对象

2、错误事件和成功事件

定义数据库打开的2个事件,一个打开成功,一个打开失败

  1. // 2、要定义 onerror 方法,如果出错则调用这个方法
  2. request.onerror = (event) => alert(`Failed to open: ${event.target.errorCode}`); // event.target.errorCode为错误码
  3. // 2、要定义 onsuccess 方法,成功打开数据库后调用,成功后取得数据库实例
  4. request.onsuccess = (event) => { db = event.target.result}; // event.target.result 数据库(IDBDatabase)实例
  5. // event.target 和 request 指向同一个对象

3、首次运行/版本更新事件

数据库第一次打开,或者版本version有变化,就会调用 onupgradeneeded 方法,见下

4、数据表

  1. // 3、数据库第一次打开,或者版本version有变化,就会调用这个方法
  2. request.onupgradeneeded = (event) => {
  3. // 下面是例子,表示打开数据库后,检查名为users的表是否存在,存在就删除,不存在就创建
  4. // 例子里的几个objectStore属性和方法,只能在onupgradeneeded这个事件里面运行
  5. // objectStoreNames是列出数据库里所有的数据表,contains是是否包含某个字符串
  6. if (db.objectStoreNames.contains("users")) {
  7. // 4、deleteObjectStore 是删除数据表
  8. db.deleteObjectStore("users");
  9. }
  10. // 4、createObjectStore 创建数据表,
  11. // 第一个参数是数据表名
  12. // 第二个参数是一个option对象,
  13. // 对象的属性keyPath是我们自己定义的key的名称
  14. // 对象的属性autoIncrement 表示这个key是否可以自动增加,默认是false
  15. db.createObjectStore("users", { keyPath: "id" });
  16. };

image.png

5、事务(准备操作数据)

创建了对象存储之后,剩下的所有操作都是通过事务完成的。

事务要通过调用数据库对象的transaction()方法创建。任何时候,只要想要读取或修改数据,都要通过事务把所有修改操作组织起来。

第一个参数,是要操作的数据表,默认是所有数据表

  1. // 如果不指定参数,则对数据库中所有的对象存储有只读权限。
  2. let transaction = db.transaction();
  3. // 只访问数据表 users 对象存储的信息
  4. let transaction = db.transaction(["users", "anotherStore"]);
  5. // 同时访问多个数据表
  6. let transaction = db.transaction(["users", "anotherStore"]);

第二个参数,是操作的权限,”readonly”(只读)、”readwrite”(读和写)或”versionchange”(变更版本)。

  1. // 可以读和写数据表users
  2. let transaction = db.transaction("users", "readwrite");
  3. // 选择一个数据表
  4. let store = transaction.objectStore("users")

6、操作数据

对数据进行增删改查。查会比较麻烦。

  1. // 2、要定义 onsuccess 方法,成功打开数据库后调用,成功后取得数据库实例
  2. request.onsuccess = (event) => {
  3. db = event.target.result
  4. // 5、
  5. let transaction = db.transaction("users", "readwrite");
  6. let store = transaction.objectStore("users")
  7. // 6、然后进行store.操作
  8. // 增
  9. store.add({"id": 1,"name": "YJL"})
  10. store.add({"id": 2,"name": "XJ"})
  11. store.add({"id": 33,"name": "111"})
  12. // 删
  13. store.delete(2)
  14. // 改
  15. store.put({"id": 3,"name": "111"})
  16. // 查
  17. let request2 = store.get(1)
  18. request2.onsuccess = (e)=>{
  19. console.log(request2.result)
  20. }
  21. };
  22. // event.target.result 数据库(IDBDatabase)实例
  23. // event.target 和 request 指向同一个对象

image.png

7、操作事件

增删改查操作会有各种事件

  1. // 操作成功
  2. request2.onsuccess = (event) => alert(event.target.result);
  3. // 操作错误或取消
  4. request2.onerror = (event) => { };
  5. // 操作已全部完成
  6. request2.oncomplete = (event) => { };

第三方库

第三方库,方便操作IndexedDB,可以通过SQL的方式增删改查,而且结果都是Promise的

Dexie.js
https://github.com/dfahlander/Dexie.js
https://dexie.org/

localforage
https://localforage.docschina.org/#api-getitem