写点什么

一文搞懂 Cookie、Storage、IndexedDB

用户头像
执鸢者
关注
发布于: 2021 年 02 月 28 日
一文搞懂Cookie、Storage、IndexedDB

关注公众号“执鸢者”,回复“资料”获取 500G 资料(各“兵种”均有),还有专业交流群等你一起来潇洒。(哈哈)


随着前端技术的发展,存储变的越来越重要,就目前来看,浏览器主要支持三类存储:Cookie、Storage、IndexedDB,下面分别介绍这三类存储方式。

一、Cookie

1.1 定义

Cookie 是一个保存在浏览器中的简单的文本文件,该文件与特定的 Web 文档关联在一起,保存了该浏览器访问这个 Web 文档时的信息,当浏览器再次访问这个 Web 文档时这些信息可供该文档使用。(HTTP 是无状态的协议,即 HTTP 协议本身不对请求和响应之间的通信状态进行保存,为了实现期望的保存状态功能,引入了 cookie 技术)

1.2 Cookie 组成

在了解 Cookie 组成之前先了解一下 Cookie 的整个请求流程,这个流程分为类:一类是没有 Cookie 信息状态下的请求,另一类是存有 Cookie 状态下的请求。


>通过上面的流程图可以看出,Cookie 是在服务端生成的,经过查询资料了解到其是在从服务端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,响应报文中有该首部字段则通知客户端保存 Cookie,则 Cookie 的组成则跟 Set-Cookie 可以设置哪些值相关,目前主要有以下几类:


  1. NAME=VALUE

Cookie 的名称和值,其中 NAME 是唯一标识 cookie 的名称,不区分大小写;VALUE 是存储在 Cookie 里的字符串值,该值必须经过 URL 编码。


  1. Domain=域名

Cookie 有效的域,发送到这个域的所有请求都会包含对应的 Cookie。(若不指定则默认为创建 Cookie 的服务器的域名)


  1. Path=PATH

请求 URL 中包含这个路径才会把 Cookie 发送到服务器(若不指定则默认为文档所在的文件目录)


  1. Expires=DATE

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


  1. Secure

设置后仅在 HTTPS 安全通信时才会发送 Cookie


  1. HttpOnly

设置后只能在服务器上读取,不能再通过 JavaScript 读取 Cookie


const express = require('express');
const app = express();
app.get('/', (req, res) => { res.cookie('myCookie', 'myCookie', { expires: new Date(Date.now() + 900000), secure: true, httpOnly: true }); res.send('get请求已经被处理');})app.listen(8090, () => { console.log('8090端口已经启动!!!');});
复制代码

通过请求 http://127/.0.0.1:8090 来看看其结果:

  1. 第一次返回的 Cookie 结果

  1. 后续请求所带的 Cookie 信息

1.3 Cookie 特点

  1. 每个 Cookie 不超过 4096 字节;

  2. 每个域中 Cookie 个数有限制,就拿最新版来说:IE 和 Edge 不超过 50 个;Firefox 不超过 150 个;Opera 不超过 180 个;Safari 和 Chrome 没有限制;

  3. Cookie 超过单个域的上限,浏览器会删除之前设置的 Cookie;

  4. 创建的 Cookie 超过最大限制,该 Cookie 会被静默删除;

  5. 可设置失效时间,没有设置则会话结束会删除 Cookie;

  6. 每个请求均会携带 Cookie,若 Cookie 过来会带来性能问题;

  7. 受同源策略限制

1.4 Cookie 的操作

Cookie 存储到浏览器端之后仍然可以对其进行读、写、删除,由于 js 对 Cookie 操作的支持并不是很友好,所以需要进行一些简单的封装。

class CookieUtil {    // 获取Cookie中的对应属性    static get(name) {        const cookies = document.cookie;        const cookiesArr = cookies.split(';');        for (let index = 0; index < cookiesArr.length; index++) {            const presentCookieArr = cookiesArr[index].split('=');            if (presentCookieArr[0] === name) {                return presentCookieArr[1];            }        }
return null; }
// 设置对应的Cookie值 static set(name, value, expires, path, domain, secure) { let cookieText = `${name}=${value}`; if (expires instanceof Date) { cookieText += `; expire=${expires.toGMTString()}`; } if (path) { cookieText += `; path=${path}`; } if (domain) { cookieText += `; domain=${domain}`; } if (secure) { cookieText += `; secure`; } document.cookie = cookieText; }
// 删除对应的Cookie static deleteCookie(name) { CookieUtil.set(name, '', new Date(0)); }}
复制代码

二、Web Storage

Web Storage 的目的是解决通过客户端存储不需要频繁发送回服务器的数据时使用 cookie 的问题,其提供了 cookie 之外的存储会话数据的途径和跨会话持久化存储大量数据的机制,其主要有两个对象:localStorage 和 sessionStorage,localStorage 是永久存储机制,sessionStorage 是跨会话的存储机制。

2.1 sessionStorage

sessionStorage 是跨会话的存储机制,具有以下特点:

  1. sessionStorage 对象值存储会话数据,其生命周期会存储到浏览器关闭。(在该过程中刷新页面其数据不受影响)

  2. 浏览器在实现存储写入时使用同步阻塞方式,数据会被立即提交到存储。

  3. 独立打开同一个窗口同一个页面或一个 Tab,sessionStorage 也是不一样的。

  4. 存储空间大小限制为每个源不超过 5M。

// 使用方法存储数据sessionStorage.setItem('sessionName1', 'value1');// 使用属性存储数据sessionStorage.sessionName2 = 'value2';
// 使用方法取得数据const sessionValue1 = sessionStorage.getItem('sessionName1');console.log('sessionValue1的值为:', sessionValue1);// 使用属性取得数据const sessionValue2 = sessionStorage.sessionName2;console.log('sessionValue2的值为:', sessionValue2);
// 循环遍历sessionStaragefor (let index = 0; index < sessionStorage.length; index++) { // 使用key()方法获得指定索引处的名称 const key = sessionStorage.key(index); const value = sessionStorage.getItem(key); console.log('循环遍历结果:', key, value);}
// 使用方法删除值sessionStorage.removeItem('sessionName1');// 使用delete删除值delete sessionStorage.sessionName2;
// 使用clear()方法清空sessionStoragesessionStorage.clear();
复制代码

2.2 localStorage

localStorage 是永久存储机制,具有以下特点:

  1. 生命周期是永久的,除非被清除,否则永久保存。

  2. 存储空间大小限制为每个源不超过 5M。

  3. 受同源策略限制。

  4. 浏览器存储时采用同步存储方式。

// 使用方法存储数据localStorage.setItem('localName1', 'value1');// 使用属性存储数据localStorage.localName2 = 'value2';
// 使用方法取得数据const localValue1 = localStorage.getItem('localName1');console.log('localValue1的值为:', localValue1);// 使用属性取得数据const localValue2 = localStorage.localName2;console.log('localValue2的值为:', localValue2);
// 循环遍历localStaragefor (let index = 0; index < localStorage.length; index++) { // 使用key()方法获得指定索引处的名称 const key = localStorage.key(index); const value = localStorage.getItem(key); console.log('循环遍历结果:', key, value);}
// 使用方法删除值localStorage.removeItem('localName1');// 使用delete删除值delete localStorage.localName2;
// 使用clear()方法清空localStoragelocalStorage.clear();
复制代码

三、IndexedDB

3.1 IndexedDB 整个结构

对于整个 IndexedDB 为上述图中所示:

  1. 一个域名下可以包含多个数据库;

  2. 一个数据库中包含多个对象仓库,就类似与 Mysql 一个库中有多张表一样。

  3. 每个对象仓库中包含多条数据记录。

3.2 主要特点

IndexedDB 是浏览器中存储结构化数据的一个方案,其设计几乎是完全异步的,主要有以下特点:

  1. 键值对存储

在对象仓库中,数据以“键值对”形式保存,每个数据记录都有独一无二的主键。


  1. 异步

IndexedDB 操作时不会锁死浏览器,用户依然可以进行其它操作。


  1. 支持事务

一些列操作步骤之中只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。


  1. 受同源策略限制

只能访问自身域名下的数据库,不能跨域访问数据库。


  1. 存储空间大

每个源都有存储空间的限制,而且这个限制跟浏览器有关,例如 Firefox 限制每个源 50MB,Chrome 为 5MB。


  1. 支持二进制存储

不仅可以存储字符串,还可以存储二进制数据(ArrayBuffer 和 Blob)

3.3 数据库操作

IndexedDB 像很多其它数据库一样有很多操作,下面就通过实战的方式一起了解这些操作。

3.3.1 初始化数据库

第一步是初始化数据库,传入创建的数据库名和版本,获取对应的数据库操作实例。

class IndexedDBOperation {  constructor(databaseName, version) {      this.atabaseName = databaseName;      this.version = version;      this.request = null;      this.db = null;  }
// 数据库初始化操作 init() { this.request = window.indexedDB.open(this.databaseName, this.version); return new Promise((resolve, reject) => { this.request.onsuccess = event => { this.db = event.target.result; console.log('数据库打开成功'); resolve('success'); }; this.request.onerror = event => { console.log('数据库打开报错'); reject('error'); }; this.request.onupgradeneeded = event =>{ this.db = event.target.result; console.log('数据库升级'); resolve('upgradeneeded'); }; }); }}
复制代码

3.3.2 对象仓库操作

数据是在对象仓库中存储的,创建好数据库后则需要创建所需的数据仓库

class IndexedDBOperation {  // ……  // 创建数据仓库  createObjectStore(objectStoreName, options) {      let objectStore = null;      if (!this.db.objectStoreNames.contains(objectStoreName)) {          objectStore = this.db.createObjectStore(objectStoreName, options);      }      return objectStore;  }}
复制代码

3.3.3 数据操作

不管是关系型数据库还是非关系型数据库,CURD 肯定是必不可少的,谁让我们是“CURD 工程师”呢!!!

class IndexedDBOperation {  // ……  // 新增内容  add(objectStore, content) {      objectStore.add(content);  }    // 获取内容  get(objectStore, id) {      const request = objectStore.get(id);      return new Promise((resolve, reject) => {          request.onsuccess = resolve;          request.onerror = reject;      });  }    // 更新内容  update(objectStore, content) {      const request = objectStore.put(content);      request.onsuccess = event => {          console.log('更新成功');      };      request.onerror = event => {          console.log('更新失败');      };  }    // 删除内容  remove(objectStore, deleteId) {      const request = objectStore.delete(deleteId);      request.onsuccess = event => {          console.log('删除成功');      };      request.onerror = event => {          console.log('删除失败');      };  }}
复制代码

3.3.4 遍历内容

提到 IndexedDB 数据库,不得不提其的游标操作。

class IndexedDBOperation {  // ……  // 打印全部数据  printAllDataByCursor(objectStore) {      const cursorRequest = objectStore.openCursor();      cursorRequest.onsuccess = event => {          const cursor = event.target.result;          if (cursor) {              console.log(`利用游标打印的内容,id为${cursor.key}, 值为${cursor.value}`);              // 移动到下一条记录              cursor.continue();          }      };  }}
复制代码

3.3.5 调用代码

上面写了一个数据库的类,但是仍然不知道怎么调用呀,下面就用一个 demo 讲述其调用。

const indexedDBOperation = new IndexedDBOperation('dbName1', 1);indexedDBOperation.init().then(type => {    const objectStoreName = 'testObjectStore';    if (type === 'upgradeneeded') {        indexedDBOperation.createObjectStore(objectStoreName, {            keyPath: 'id'        });    }    const transaction = indexedDBOperation.db.transaction([objectStoreName], 'readwrite');    const objectStore = transaction.objectStore(objectStoreName);
indexedDBOperation .get(objectStore, 1) .then(event => { if (event.target.result) { indexedDBOperation.update(objectStore, { id: 1, name: '执鸢者+纸鸢' }); console.log('数据库中已经存在', event.target.result, ',则进行更新操作'); } else { indexedDBOperation.add(objectStore, { id: 1, name: '执鸢者' }); console.log('数据库中不存在,则进行添加'); } }) .catch(console.log);
indexedDBOperation.printAllDataByCursor(objectStore);
transaction.onsuccess = event => { console.log('事务操作成功'); }; transaction.onerror = event => { console.log('事务操作失败'); }; transaction.oncomplete = event => { console.log('整个事务成功完成'); }}).catch(console.log);
复制代码

参考文献

  1. 浏览器数据库 IndexedDB 入门教程

  2. [javascript 高级程序设计 4]()

  3. IndexedDB API


1.如果觉得这篇文章还不错,来个分享、点赞吧,让更多的人也看到


2.关注公众号执鸢者,领取学习资料(前端“多兵种”资料),定期为你推送原创深度好文


发布于: 2021 年 02 月 28 日阅读数: 52
用户头像

执鸢者

关注

让前端知识变的简单可依赖。 2019.09.05 加入

以脑图分享前端知识,让知识变的简单可依赖。

评论

发布
暂无评论
一文搞懂Cookie、Storage、IndexedDB