前端为什么要进行数据存储?
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,在浏览器端存储数据对我们是很有用,这相当于赋予浏览器记忆的功能,可以纪录用户的所有状态信息,增强用户体验。比如当记录用户的登陆状态时,存储一些固定的信息等。
1. 前端存储的方案有哪些?
- Cookie
- Web Storage
- Localstorage
- Sessionstorage
- IndexDB
- WebSQL
- Application Cache
2. Cookie
Cookie 的本职工作并非本地存储,而是“ 维持状态 ”。
在 Web 开发的早期,人们亟需解决的一个问题就是状态管理的问题:HTTP 协议是一个无状态协议,服务器接收客户端的请求,返回一个响应,故事到此就结束了,服务器并没有记录下关于客户端的任何信息。那么下次请求的时候,如何让服务器知道“我是我”呢?在这样的背景下,Cookie 应运而生。
Cookie 说白了就是一个存储在浏览器里的一个小小的文本文件,它附着在 HTTP 请求上,在浏览器和服务器之间“飞来飞去”。它可以携带用户信息,当服务器检查 Cookie 的时候,便可以获取到客户端的状态。
- Cookie 缺点
- Cookie 不够大(一般为 4KB)
- Cookie 是紧跟域名的
- 同一个域名下的所有请求,都会携带 Cookie
因此为了弥补 Cookie 的局限性,让“专业的人做专业的事情”,WebStorage 出现了。
3. Web Storage
Web Storage 是 HTML5 专门为浏览器存储而提供的数据存储机制,一般它的存储容量再 5-10M 之间,具体由浏览器厂家决定;另外它只存在于客户端,不会与服务器进行通信。它又分为 Local Storage 与 SessionStorage。
-
主要区别
- 生命周期:Local Storage 是持久化的本地存储,存储在其中的数据是永远不会过期的,使其消失的唯一办法是手动删除;而Session Storage 是临时性的本地存储,它是会话级别的存储,当会话结束(页面被关闭)时,存储内容也随之被释放。
- Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特别的一点在于,即便是相同域名下的两个页面,只要它们不在同一个浏览器窗口中打开,那么它们的 Session Storage 内容便无法共享。
-
核心 API
- 存储数据:
localStorage.setItem(key,value)
- 读取数据:
localStorage.getItem (key)
- 删除某一键名对应的数据:
localStorage.removeItem(key)
- 清空数据记录
localStorage.clear()
- 存储数据:
-
缺点
- Web Storage 是一个从定义到使用都非常简单的东西。它使用键值对的形式进行存储,这种模式有点类似于对象,却甚至连对象都不是,它只能存储字符串。要想得到对象,我们还需要先对字符串进行一轮解析。并且只能存储少量简单数据,当遇到大规模的结构数据时,它就爱莫能助了。
4. IndexDB
IndexDB 是一个运行在浏览器上的非关系型数据库。既然是数据库了,那就不是 5M、10M 这样小打小闹级别了。理论上来说,IndexDB 理论上是没有存储上限的一般来说不会小于 250M)。它不仅可以存储字符串,还可以存储二进制数据。
- 兼容性
-
主要特点
- 键值对储存IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
- 异步 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
- 支持事务IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
- 同源限制IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
- 支持二进制储存IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象。
- 储存空间大IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。储 存 在 电 脑 上 中 的 位 置 为 C:\Users\当 前 的 登 录 用 户\AppData\Local\Google\Chrome\User Data\Default\IndexedDB
-
基本概念
- 数据库(IDBDatabase 对象)数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。但是它版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
- 对象仓库(IDBObjectStore 对象)每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。
- 索引(IDBIndex 对象)为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。
- 事务(IDBTransaction 对象)数据记录的读写和删改,都要通过事务完成。事务对象提供error、abort和complete三个事件监听操作结果。
- 操作请求(IDBRequest 对象)。
- 指针 (IDBCursor 对象)。
- 主键集合 (IDBKeyRange 对象)。
-
操作步骤
- 定义变量
var db = null; var db_table = null; var databaseName = 'indexDB'; var version = 1; var data = [{ id:1, name:'张一', age: 1, email:'zhangsan@example.com' },{ id:2, name:'张二', age: 2, email:'zhangsan@example.com' },{ id:3, name:'张三', age: 3, email:'zhangsan@example.com' },{ id:4, name:'张四', age: 4, email:'zhangsan@example.com' },{ id:5, name:'张五', age: 5, email:'zhangsan@example.com' }]
- 打开数据仓库
/* *@databaseName 数据仓库的名字 *@version 数据仓库的版本 *@databaseName 数据仓库的名字 */ var request = window.indexedDB.open(databaseName, version); /* *数据仓库打开失败 */ request.onerror = function (error){ console.log('IndexDB打开失败',error); } /* *数据仓库打开成功 */ request.onsuccess = function (res){ console.log('IndexDB打开成功',res); db = res.target.result; } /* *数据仓库升级事件(第一次新建库是也会触发,因为数据仓库从无到有算是升级了一次) */ request.onupgradeneeded = function (res){ console.log('IndexDB升级成功',res); db = res.target.result; db_table = db.createObjectStore('group', { keyPath: 'id' }); db_table.createIndex('indexName', 'name', { unique: false }); }
- 新建表和创建索引
request.onupgradeneeded = function (res){ console.log('IndexDB升级成功',res); /* *返回indexDB对象 */ db = res.target.result; /* *创建数据仓库 *@params 数据仓库名 *@params 数据仓库配置项 keypath:主键,也可以 autoIncrement: true 自动生成 */ db_table = db.createObjectStore('group', { keyPath: 'id' }); /* *创建数据索引 *@params 索引名称 *@params 索引所在的属性 *@params 配置对象(说明该属性是否包含重复的值) */ db_table.createIndex('indexName', 'name', { unique: false }); }
- 新增数据 新增数据需要通过事务来完成, 写入数据需要新建一个事务, 新建时必须指定表格名称和操作模式("只读"或"读写")。
/* *新建事务 *@params 数据仓库的数组 *@params 写入模式 */ var store = db.transaction(['group'], 'readwrite').objectStore('group'); /* *add方法添加数据 *@params 需要添加的数据信息 */ var request = store.add({ id:new Date().getTime(), name:'王二', age:12, email:'XXXX@xxx.com' }); /* *添加成功 */ request.onsuccess = function (event) { console.log('数据添加成功',event); }; /* *添加失败 */ request.onerror = function (event) { console.log('数据添加失败',event); };
- 读取数据 读取数据也是通过事务完成。
/* *新建事务 *@params 数据仓库的数组 */ var store = db.transaction(['group']).objectStore('group'); /* *get方法获取数据 *@params 数据的主键 */ var request = store.get(1); /* *获取成功 */ request.onsuccess = function (event) { if(event.target.result){ console.log('数据获取成功',event); } else{ console.log('未获取到数据'); } }; /* *获取失败 */ request.onerror = function (event) { console.log('数据获取失败',event); };
- 更新数据 更新数据要使用 IDBObject.put()方法
/* *新建事务 *@params 数据仓库的数组 *@params 写入模式 */ var store = db.transaction(['group']).objectStore('group'); /* *put方法根据主键更新数据 *@params 数据的主键 */ var request = store .put({ id:1, name:'张一'+Math.random(), age: 24, email:'zhangsan@example.com' }); /* *更新成功 */ request.onsuccess = function (event) { console.log('数据更新成功',event); }; /* *更新失败 */ request.onerror = function (event) { console.log('数据更新失败',event); };
- 删除数据
/* *新建事务 *@params 数据仓库的数组 */ var store = db.transaction(['group']).objectStore('group'); /* *delete方法删除数据 *@params 数据的主键 */ var request = store.delete(1); /* *删除成功 */ request.onsuccess = function (event) { console.log('数据删除成功',event); }; /* *删除失败 */ request.onerror = function (event) { console.log('数据删除失败',event); };
- 使用索引 索引的意义在于,可以让你搜索任意字段,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能搜索主键(即从主键取值)。
/* *新建事务 *@params 数据仓库的数组 */ var store = db.transaction(['group']).objectStore('group'); /* *index方法获取索引对象 *get方法获取数据 *@params 数据的索引 */ var request = store.index('indexName').get('张四'); /* *获取成功 */ request.onsuccess = function (event) { console.log('通过索引获取数据成功',event); }; /* *获取失败 */ request.onerror = function (event) { console.log('通过索引获取数据失败',event); };
- 遍历数据
遍历数据要使用指针对象 IDBCursor。指针对象的
openCursor()
方法是一个异步操作,所以要监听success
事件。
/* *新建事务 *@params 数据仓库的数组 */ var store = db.transaction(['group']).objectStore('group'); /* *openCursor方法获取指针对象 *get方法获取数据 *@params 数据的索引 */ var request = store.openCursor();; /* *指针对象打开成功 */ request.onsuccess = function (event) { var cursor = event.target.result; if(cursor){ console.log('数据遍历',cursor.value); cursor.continue(); } else{ console.log('没有更多数据了'); } }; /* *指针对象打开失败 */ request.onerror = function (event) { console.log('数据遍历失败'); };
-
总结
- 在 IndexDB 中,我们可以创建多个数据库,一个数据库中创建多张表,一张表中存储多条数据——这足以 hold 住复杂的结构性数据。IndexDB 可以看做是 LocalStorage 的一个升级,当数据的复杂度和规模上升到了 LocalStorage 无法解决的程度,我们毫无疑问可以请出 IndexDB 来帮忙。
5.WebSQL
WebSQL 是浏览器端的关系行数据库,引入了一组使用 SQL 操作客户端数据库的 API。
- 三个核心方法
openDatabase
:使用数据库或新建数据库来创建数据库对象transaction
:允许我们根据情况控制事务提交或回滚executeSql
:这个方法用于执行真实的 SQL 查询
6.Application Cache
WebSQL Application Cache 是 HTML5 中定义的一种离线存储技术标准。这种技术可以让开发者明确地指定页面中哪些静态资源可以在第一次访问网页的同时缓存到本地,并且在下次访问该网页时向服务器询问本地缓存的资源是否需要更新。