一、背景介绍
第一个Web存储的技术叫做Cookie,它是网站的身份证。是网站为了辨别用户身份,进行session(服务端的session)跟踪而存储在用户本地终端上的数据,也就是说它是存在电脑硬盘上的,一个很小的txt类型的文件。Cookie每次都会跟随http请求发送到服务端,也就是说每一个http请求都会带上我们的cookie数据,因此它存在一个安全性的问题。
cookie本身也是有很大的局限性的,首先它很小,主流的浏览器最大支持 4096 字节,除了最大字节的限制,每个网站的cookie个数(也就是每一个first每一个域)也是有限制的,一般浏览器是20个。除此之外,cookie还会默认跟随所有http请求发送,即使不需要使用这个cookie来鉴别用户但是它也是会跟随http请求发送的,这样就会造成一个网络资源的浪费。然后部分的浏览器还限制了总的cookie个数300个。
在cookie的诸多局限性下,Web Storage应运而生。Web Storage 解决了很多问题:
比如它支持存储大量数据,支持复杂的本地数据库,而且也不会默认跟随http请求。Web Storage主要是有四个:
- SessionStorage
- LocalStorage
- WebSQL
- indexedDB
文章图片
Cookie是HTML4的一个标准,它一般不需要考虑兼容。它是网站的一个身份证,服务器可以针对不同用户,做出不同的响应。cookie存储在用户的机器上是一个纯文本,就是一个txt文件并不是一个脚本,它不能执行东西只负责记录。浏览器每次请求都会带上当前网站的cookie。
Cookie分为两种类型,一种呢是会话cookie,也就是临时性的cookie,退出浏览器或者是关闭即删除;
另一种叫持久cookie,它会一直存在,存在的时间由特定的过期时间或者是有效期来决定。
Cookie的域 Domain决定了当前的一个cookie的权限,哪一个域可以使用这个cookie。
Cookie的路径 Path,下面一个简单的例子:
www.baidu.comid="123456" domain="www.baidu.com"
www.baidu.com/userid="123456" user="eric" domain="www.baidu.com" path="/user/"www.baidu.com/searchid="123456";
www.baidu.com/user/searchid="123456" user="eric";
如上www.baidu.com设置了一个id等于123456,domain是www.baidu.com,然后另外一个跟第一个一样多设置了一个user,id相同,但是多了一个user=“Eric”,它的domain设置成了www.baidu.com,path就到了user下面。这两者设置完成之后,当我们访问www.baidu.com/search时百度只能拿到id,因为user="Eric"是属于user这个域下面的,也就是说在search下面是获取不到的,但是在www.baidu.com/user/search这个时候我们就可以获取到名叫Eric的user。Path也是一种权限的控制只是相较于域domain是低一级的。
Cookie的安全secure,如果这个属性为TRUE,那么网站只有在https的请求下面才会携带当前的cookie。
Cookie的HttpOnly这个属性如果为TRUE,那么就不允许JavaScript操作cookie。
因为cookie是存储在客户端一个独立的文件,因此服务器是无法分辨用户和攻击者的。关于cookie的目的分为两种:一种是跨站点脚本攻击,一种是跨站请求伪造。
三、SessionStorage
文章图片
key-value的键值对,是HTML5新增的一个会话存储对象。
SessionStorage是临时保存在同一窗口,也就是同一标签页的数据。如果当前标签页关闭了,那么SessionStorage也就失效了。这也是SessionStorage最显著的一个特点:单页标签限制。
除此之外,它还有的一些特点有:
- 同源策略,也就是在同一协议,同一主机名和同一端口下的同一tab
- 只在本地存储,不会跟随http请求发送到服务器
- 存储方式采用key-value键值对,这里面的value只能存字符串类型,如果存其他的会自动转换成字符串。
- 存储上线限制达到了5MB,如果当前存储超出上限新的内容会把旧的内容覆盖但不会报错。
- sessionStorage.length - 键值对数量
- sessionStorage.key(int index) -> null
- sessionStorage.getItem(string key) -> null
- sessionStorage[string key]
- sessionStorage.setItem(string key, string value)
- sessionStorage.removeItem(string key)
- sessionStorage.clear()
- JSON.stringify()
- JSON.parse()
文章图片
LocalStorage也是在浏览器的Application下面有一个Local Storage,它和SessionStorage是十分相似的,同样是key-value键值对,也是HTML5的新增存储对象,它与SessionStorage的特点不同之处在于没有标签页的限制和在浏览器的无痕模式下LocalStorage是不允许读取的,永久性的存储,然后SessionStorage超出限制是覆盖不会报错而LocalStorage超出会报错。
特点:
- 同源策略,也就是在同一协议,同一主机名和同一端口下的同一tab
- 没有标签页的限制
- 只在本地存储,不会跟随http请求发送到服务器
- 存储方式采用key-value键值对,这里面的value只能存字符串类型,如果存其他的会自动转换成字符串。
- 存储上线限制达到了5MB,如果当前存储超出上限会报错。
- 无痕模式下不可读取
- 永久性存储
- sessionStorage.length - 键值对数量
- sessionStorage.key(int index) -> null
- sessionStorage.getItem(string key) -> null
- sessionStorage[string key]
- sessionStorage.setItem(string key, string value)
- sessionStorage.removeItem(string key)
- sessionStorage.clear()
另外一个在IOS浏览器中不可重复setItem,如果重复会报错,然后这个时候我们需要先removeItem再添加item。
监听storage的变化 【cookie|Web 存储技术】监听storage包括SessionStorage和LocalStorage。然后这里需要提到两个概念:同源和监听同源网页。
- 同源:协议、域名、端口三者相同,同源的情况下我们可以共享SessionStorage和LocalStorage。
同源策略还禁止不同源执行任何脚本。
http://localhost:63342/simpleApp/app/index.html#/ (协议)(域名)(端口)(路径)
- 监听同源网页,但是同一网页是无效的
window.addEventListener("storage", function (event) { console.log(event.key); console.log(event.oldValue); console.log(event.newValue); console.log(event.url); console.log(event.storageArea); });
五、IndexedDB
IndexedDB 背景
- Storage(Storage指的是SessionStorage和LocalStorage)不适合存储大量的数据
- Storage不能提供搜索功能
- Storage不能建立索引,存储的内容也比较少
- IndexedDB扩大了web存储的容量,可以达到250MB以上
IndexedDB的特点也是和Storage是一样的:
- 键值对储存 ,但是允许所有类型,不允许主键重复报错
- 是一个异步操作, 不阻塞浏览器线程
- 支持事务,事务是SQL数据库的一个概念,也就是说我们进行任何的增删改查都要在某一个事务下面进行,提供了一个回滚功能,一系列操作有一步失败, 数据库回滚到事务发生之前的状态,这样为了避免操作中途出现失败,影响整个数据库的状态
- 同源限制
- 支持二进制储存
- IDBDatabase - 数据库
- IDBObjectStore - 对象仓库
- IDBIndex - 索引
- IDBTransaction - 事务
- IDBRequest - 操作请求
- IDBCursor - 指针
- IDBKeyRange - 主键集合
文章图片
IDBDatabase IDB是IndexedDB的缩写,它呢就是数据库,数据库也叫作数据的一个容器。每一个源(同源策略)可以建立很多数据库。Database有一个版本的概念,版本对应着数据库表,同一时刻只能存在一个版本。比如:新增一个表,然后我们需要把database的版本加一,表里面要新增一列,这时同样需要把数据库版本加一。
注意:1 同一时刻只能有一个版本存在 2 修改数据库结构只能通过升级数据库版本
- 打开数据库
/* databaseName不存在则创建 */ /* version为整数, 新建时为1 */let database; let userStore; const request = window.indexedDB.open(databaseName, version); /* 成功打开数据库 */ request.onsuccess = event => { database = request.result; }/* 打开数据库失败 */ request.onerror = error => { console.log(error); }/* 版本号大于当前数据库版本 */ request.onupgradeneeded = event => { database = event.target.result; }
IDBObjectStore(数据库表) 创建表,最好是在upgradeneeded下执行;在创建数据库表的时候需要指定主键,主键代表了唯一的标识,比如 keyPath:‘id’;如果不指定主键,我们可以指定一个autoIncrement:true,自增的一个概念,也就是不指定主键数据库会自动添加主键而且这个主键就是数字,依次递增的。
const createStore = () => {
//如果当前的objectStoreNames.contains包含user,如果不包含user这个表,然后就用这个database.createObjectStore创建了一个表,这个表的名字就叫做user,然后主键就是下面的id
if(!db.objectStoreNames.contains('user')) {
userStore = database.createObjectStore('user', { keyPath: 'id' });
}
}
指定索引:
const createStore = () => {
if(!database.objectStoreNames.contains('user')) {
userStore = database.createObjectStore('user', { keyPath: 'id' });
userStore.createIndex('name', 'name', { unique: true });
}
}
IDBTransaction(事务) 创建完之后需要往里面添加数据,添加数据我们就需要使用到事务。
事务涉及到数据库的增删改查,它有三个状态:
- complete
- error
- abort
- IDBTransaction.db 当前数据库
- IDBTransaction.mode 模式,使用模式分为readonly和readwrite
- IDBTransaction.objectStoreNames 当前数据库涉及到的哪几个数组表
- IDBTransaction.error 回调
新增数据(add)
分为两种情况:一种是使用自增的数据库的id或者是自增的一个键值,如果已经创建主键,那么新增必须包含主键和另一种已创建主键但主键不可重复。
const add = () => {
/* 创建事务 */
/* 使用某个数据库 */
/* add新增 */
transactionRequest = database.transaction(['user'], 'readwrite')
.objectStore('user')
.add({ id: 100, name: 'Eric', age: 28, email: 'Ericlee00@163.com' });
/* 成功 */
transactionRequest.onsuccess = event => {
console.log('数据写入成功', event);
};
/* 失败 */
transactionRequest.onerror = error => {
console.log('数据写入失败', error);
}
}
读取数据(get)
const read = () => {
/* 创建事务 */
transaction = database.transaction(['user']);
/* 选择数据库表 */
table = transaction.objectStore('user');
/* 读取数据 */
transactionRequest = table.get(2);
/* 成功 */
transactionRequest.onerror = event => {
console.log('数据读取失败', event);
};
/* 失败 */
transactionRequest.onsuccess = event => {
if (transactionRequest.result) {
console.log('数据读取成功', transactionRequest.result);
} else {
console.log('未读取到数据');
}
};
}
更新数据(put)
更新不存在的数据时会新建,也就是说在新增数据时如果相同,往往会出错,但是在更新数据时不会出错。如果数据不存在就会新建,如果存在就会一直更新。
const update = () => {
transactionRequest = database.transaction(['user'], 'readwrite')
.objectStore('user')
.put({ id: count, name: 'David', age: 35, email: 'David@xiakedao.com' });
transactionRequest.onsuccess = function (event) {
console.log('更新数据成功', event);
};
transactionRequest.onerror = error => {
console.log('更新数据失败', error);
}
}
删除数据(delete)
const delete = () => {
transactionRequest = database.transaction(['user'], 'readwrite')
.objectStore('user')
.delete(2);
transactionRequest.onsuccess = function (event) {
console.log('删除数据成功', event);
};
transactionRequest.onerror = error => {
console.log('删除数据失败', error);
}
}
清空数据(clear)
IDBCursor(指针) 提供了一种遍历数据的可能。
const readAll = () => {
table = database.transaction('user').objectStore('user');
table.openCursor().onsuccess = () => {
let cursor = event.target.result;
if (cursor) {
console.log('数据遍历', cursor);
cursor.continue();
} else {
console.log('数据遍历完成');
}
};
}
关闭IndexedDB数据库连接
const closeDataBase = () => {
database.close();
}
删除IndexedDB数据库前,须先关闭数据库连接
const deleteDataBase = () => {
indexedDB.deleteDatabase('first_database');
}
六、WebSQL
基本概念:并不是 HTML5 的规范 , 只能算是一个独立的规范;使用WebSQL是完完全全的SQL 语句,使用SQL语句来操作客户端数据库;它一共有三个比较重要的概念,分别是:openDatabase 打开数据库,可以是使用现有数据库或者新建数据库;transaction 事务,所有的数据库都支持事务;executeSql 执行SQL语句。
openDatabase(打开数据库) 相比于IndexedDB的概念稍微多一点,主要是有数据库名称、版本号(在IndexedDB里面版本号都是整数,但是在WebSQL里面它可以是小数)、描述文本(介绍数据库是干什么的)、数据库大小和创建回调(function,只在第一次创建的时候才会调用)。
const database = openDatabase('my_database', '1.0', 'first', 2 * 1024 * 1024, function() {});
Transaction(事务)
- 创建表
const createTable = () => { database.transaction(function (content) { content.executeSql('CREATE TABLE IF NOT EXISTS USER (id unique, name)'); }); }
- 添加数据
const addData = https://www.it610.com/article/() => { database.transaction(function (content) { content.executeSql('INSERT INTO USER (id, name) VALUES (1, "Eric")'); }); }
- 查询数据
const searchData = https://www.it610.com/article/() => { database.transaction(function (content) { content.executeSql('SELECT * FROM USER'); }); }
- 更新数据
const updateData = https://www.it610.com/article/() => { database.transaction(function (content) { content.executeSql('UPDATE USER SET name=\'David\' WHERE id=1'); }); }
- 删除数据库表
const deleteDataBase = () => { database.transaction(function (content) { content.executeSql('DROP TABLE USER'); }); }
推荐阅读
- Python爬虫笔记|Python爬虫学习笔记_DAY_17_Python爬虫之使用cookie绕过登录的介绍【Python爬虫】
- 网络知识|cookie 和 session的区别
- java学习中cookie原理
- javascript|原生JS关于cookie 的操作
- javascript|前端面试每日 3+1 —— 第165天
- html5|【译】客户端存储(Client-Side Storage)
- Cookie/Session机制详解