什么是缓存?
当我们第一次访问网站的时候,比如 juejin.cn,电脑会把网站上的图片和数据下载到电脑上,当我们再次访问该网站的时候,网站就会从电脑中直接加载出来,这就是缓存。
缓存的优点和应用场景
Web缓存种类:indexDB、localStorage、sessionStorage。
-
缓解服务器压力,不用每次都去请求某些数据了。
-
提升性能,打开本地资源肯定会比请求服务器来得快。
-
减少带宽消耗,当我们使用缓存时,只会产生很小的网络消耗,至于为什么打开本地资源也会产生网络消耗,下面会有说明。
indexDB
介绍
首先indexDB是一个运行在浏览器的非关系型数据库,作为一个数据库,它存储的数据量就是没有上线的,同时它不仅可以存储字符串,还可以存储二进制数据。
主要特点
- 键值对存储:indexDB内部采用对象仓库存放数据。所有类型的数据都可以直接存入,包括Javascript对象。在仓库中,数据以键值对的形式进行保存,每一个数据记录都有对应的主键,且主键是独一无二的,不能有重复。
- 异步: indexDB操作时是异步操作,不会锁死浏览器,用户在操作indexDB数据库时,可同时进行其他操作(页面渲染等),相比于localStorage的同步操作相比,有利于在大量数据进行读写操作时,避免影响网页的性能。
- 支持事务:IndexDB支持事务,支持在数据库操作失败后,整个事务取消,数据库会回滚到之前的状态,有利于保证数据的安全与完整性。
- 同源限制:IndexDB收到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能跨域访问。
- 支持二进制储存:IndexDB不仅可以存储字符串,还可以储存二进制树(ArrayBuffer对象和Blob对象)。
- 储存空间大:indexDB由于是数据库,所以存储量比一般方式要大很多,一般来说不少于250MB。
基本概念
- 数据库(IDBDatabase对象):数据库是一系列相关数据的容器。每个域名(严格的说,是协议+域名+端口)都可以新建任意多个数据库,但他的版本的概念,同一时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或主键),只能通过升级数据库版本完成。
- 对象仓库(IDBObjectStore对象)每个数据库包含若干个对象仓库。它类似于关系型数据库的表格。
- 索引(IDBIndex):为了加速数据的检索可以在对象仓库里面,为不同的属性建立索引。
- 事务(IDTransaction对象):数据记录的读写和删除,都要通过事务完成。对象提供error、abort和complete三个监听事件监听操作结果。
- 操作请求(IDBRequest对象)。
- 指针(IDBCursor对象)。
- 主键集合(IDBKeyRange对象)。
代码示列
定义数据库初始变量
var db = null;
var db_table = null;
var databaseName = 'indexDB';
var version = 1;
var tableData = [{ //待存入数据
id:1,
name:'张一',
age: 1,
address:'西安'
}]
打开数据库
/*
*@databaseName 数据仓库的名字
*@version 数据仓库的版本
*/
window.indexDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; //获取浏览器支持的indexDB数据库
var request = window.indexedDB.open(databaseName, version);
/*
*数据仓库打开失败
*/
request.onerror = function (error){
console.log('IndexDB打开失败',error);
}
/*
*数据仓库打开成功
*/
request.onsuccess = function (res){
db = res.target.result; //打开成功后用变量db存储数据库对象
}
/*
*数据仓库升级事件(第一次新建库是也会触发,因为数据仓库从无到有算是升级了一次)
*/
request.onupgradeneeded = function (res){
db = res.target.result;
//在数据库中创建表group,设置主键为id
db_table = db.createObjectStore('tableA', { keyPath: 'id' });
//创建索引indexName指向表中的name字段且设为唯一值,不能重复
db_table.createIndex('indexName', 'name', { unique: false });
}
存储数据
- 写入数据需要新建一个事务,新建时必须指定表格名称和操作模式
/*
*新建事务
*@params 数据仓库的数组
*@params 写入模式,readwrite写入操作,为空时表示只读
*@objectStore对应数据库中的表
*/
var store = db.transaction(['tableA'], 'readwrite').objectStore('tableA');
/*
*add方法添加数据
*@params 需要添加的数据信息
*/
var transaction = store.add(tableData);//将上面创建的数据加入数据库
/*
*添加成功
*/
transaction.onsuccess = function (event) {
console.log('数据添加成功',event);
};
/*
*添加失败
*/
transaction.onerror = function (event) {
console.log('数据添加失败',event);
};
读取数据
- 读取数据也是通过事务完成的
/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['tableA']).objectStore('tableA');
/*
*get方法获取数据
*@params 数据的主键
*/
var transaction = store.get(1);
/*
*获取成功
*/
transaction.onsuccess = function (event) {
if(event.target.result){
console.log('数据获取成功',event);
}
else{
console.log('未获取到数据');
}
};
/*
*获取失败
*/
transaction.onerror = function (event) {
console.log('数据获取失败',event);
};
更新表中的数据
- 更新数据要使用 IDBObject.put()方法
/*
*新建事务
*@params 数据仓库的数组
*@params 写入模式
*/
var store = db.transaction(['tableA']).objectStore('tableA');
/*
*put方法根据主键更新数据
*@params 数据的主键
*/
var transaction = store .get(1);
transaction.onsuccess = function(event){
let oldData = event.target.result
oldData.age = 30
const update = store.put(oldData)
update.onerror=function(err){
console.log(err)
}
update.onsuccess = function(event){
console.log('完成更新')
}
}
删除数据
var transaction = db.transaction(["tableA"], "readwrite")
.objectStore("tableA")
.delete("1");//按主键删除
transaction.onsuccess = function(event) {
// It's gone!
};
transaction.onerror = function(event) {
// It's gone!
};
使用索引
/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['tableA']).objectStore('tableA');
/*
*index方法获取索引对象
*get方法获取数据
*@params 数据的索引
*/
var request = store.index('indexName').get('张四');
/*
*获取成功
*/
request.onsuccess = function (event) {
console.log('通过索引获取数据成功',event);
};
/*
*获取失败
*/
request.onerror = function (event) {
console.log('通过索引获取数据失败',event);
};
使用指针遍历表中的所有值
var objectStore = db.transaction("tableA").objectStore("tableA");
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
console.log("Name for SSN " + cursor.key + " is " + cursor.value.name);
cursor.continue();
}
else {
console.log("No more entries!");
}
};
//另一种遍历方式
objectStore.getAll().onsuccess = function(event) {
console.log("Got all objects" + event.target.result);
};
- 限定函数范围介绍(截图自MDN官方文档)
- 指定光标的范围和方向
/*
*openCursor参数
*params1 用来控制指针显示的范围,为null时不做任何限制
*@params2 表示指针遍历的的方向
"next": 光标显示所有记录,包括重复记录。它从键范围的下限开始向上移动(按键的顺序单调递增)。
"nextunique": 光标显示所有记录,不包括重复记录。如果存在多个具有相同键的记录,则仅检索第一个迭代的记录。它从键范围的下限开始向上移动。
"prev": 光标显示所有记录,包括重复记录。它从键范围的上限开始向下移动(按键的顺序单调递减)。
"prevunique": 光标显示所有记录,不包括重复记录。如果存在多个具有相同键的记录,则仅检索第一个迭代的记录。它从键范围的上限开始向下移动。
*/
var index = objectStore.index("indexName");
const range = IDBKeyRange.bound(1,10);//遍历id从1到10的数据
index.openCursor(boundKeyRange).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
cursor.continue();
}
};
实际使用(localForage)
什么是 localForage
localForage 是一个 JavaScript 库,通过简单类似 localStorage API 的异步存储来改进你的 Web 应用程序的离线体验。它能存储多种类型的数据,而不仅仅是字符串。
localForage 有一个优雅降级策略,若浏览器不支持 IndexedDB 或 WebSQL,则使用 localStorage。在所有主流浏览器中都可用:Chrome,Firefox,IE 和 Safari(包括 Safari Mobile)。
安装
npm install localforage
API 使用
localForage 提供回调 API 同时也支持 ES6 Promises API.
回调 API 形式
localforage.setItem('key', 'value', function (err) {
// if err is non-null, we got an error
localforage.getItem('key', function (err, value) {
// if err is non-null, we got an error. otherwise, value is the value
});
});
ES6 Promises 形式
localforage.setItem('key', 'value').then(function () {
return localforage.getItem('key');
}).then(function (value) {
// we got our value
}).catch(function (err) {
console.log(err);
});
或使用 async / await 形式
try {
const value = await localforage.getItem('somekey');
console.log(value);
} catch (err) {
console.log(err);
}
多个实例
createInstance 创建并返回一个 localForage 的新实例。每个实例对象都有独立的数据库,而不会影响到其他实例
var store = localforage.createInstance({
name: "nameHere"
});
var otherStore = localforage.createInstance({
name: "otherName"
});
// 设置某个数据仓库 key 的值不会影响到另一个数据仓库
store.setItem("key1", "value1");
otherStore.setItem("key2", "value2");
总结
在indexDB中,可以创建多个数据库,数据库中可以创建多张表,每张表中可以存储多条数据,当需要存储的数据复杂度高且数据量大时建议使用indexDB。
localStorage
- localStorage可以实现永久存储,即使浏览器关闭了数据依然存在,在下次打开浏览器访问网站时仍然可以使用。而且在同源下数据多窗口状态也能共享。但是大多数浏览器限制localStorage的的大小为5M左右。
- localStorage存储数据也是以键值对的形式进行保存的,且任何格式在存储时都会转为字符串格式。
用法
//保存数据
loaclStorage.setItem('name','whyweplay')
loaclStorage.setItem('name','whyweplay123') //重复对一个键进行赋值时会覆盖上一次保存的值
//读取数据
const name = localStorage.getItem('name')
//删除数据
localStorage.removeItem('name')
//清除localStorage中的全部数据
localStorage.clear()
//获取localStorage中的键
localStorage.key(index) //index表示第几个存入localStorage
//监听localStorage的的变化,当键值改变或clear()时会触发
window.addEventListener('storage', wacthHandler, false)
const watchHandler = function(e){
console.log('监听到新值为'+e.newValue)
}
sessionStorage
- sessionStorage与localStorage功能基本一致,区别是sessionStorage里面的数据会在页面会话结束时被清除。在打开多个相同页面的url的Tabs页面,会创建各自的sessionStorage。
用法
// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');
// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');
// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');
// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear();
- 前端 sessionStorage 缓存 localStorage indexDB前端sessionstorage缓存localstorage 前端sessionstorage localstorage cookies 前端sessionstorage localstorage 前端sessionstorage localstorage cookie sessionstorage localstorage cookie sessionstorage localstorage sessionstorage localstorage router cookie sessionstorage localstorage session cookie sessionstorage localstorage网站 拷贝 sessionstorage localstorage cookies