背景
接口连路很长导致返回数据很慢,页面长时间loading空白,甚至超时报错,导致整个站的用户体验很差。场景特点:
- 数据量比较大,
- 仅用于进入页面的时候展示,替代空白的loading页面或者后端接口报错的展示数据
- 不要求兼容性
为什么要用缓存
- 减少网络宽带消耗,减轻拥塞
- 降低服务器的压力
- 加快页面的打开速度
- 如果远程服务器故障造成远程服务器无法响应,打开历史版本可以提供用户体验
缓存分类
- 数据库缓存
- 服务器缓存
- 代理服务器缓存
- CDN缓存
- 浏览器缓存
- HTTP header缓存机制
- 强缓存(cache-control expires)
- 协商缓存(Etag last-modified)
- 数据缓存
- cookie
- storage (Local Storage和Session Storage)
- indexDB
- web SQL
- 应用缓存
- mainifest
- HTTP header缓存机制
因为数据库缓存和服务器缓存不是前端的控制所以提一下,下面考虑浏览器缓存,针对场景更多的考虑数据缓存
数据缓存方式对比
方式 | 存储的大小 | 兼容性 | 难易程度 | 影响 |
---|---|---|---|---|
cookie | 4k | 兼容 | 手动封装,有难度 | 和http请求一起发送 |
storage | 5M | ie8 | 简单 | 不和http一起发送 |
IndexedDB | 无限容量 | ie11,safari9.3,opera不支持 | 复杂 | 不和http一起发送;异步操作大量数据操作不会拖慢网页 |
IndexedDB
名词解释
- 关系型数据库:后端存储数据用到的一般是关系型数据库,例如存储人员信息,根据人的id去关联他的考勤,发布的文章等等。它对于一致性的要求非常严格
- 非关系型数据库:数据结构不固定,非常容易拓展,常见的有3个,1、数据结构类似json的key:value,典型的是radius;2、以列来定义每一个表,每一行的内容可以不同,茹茹第一行存人员信息,第二行存商品信息;3、mongodb,类似关系型数据库的非关系型数据库
- 游标:可以理解为卡尺,一行行的数据
- 锁:数据库中的锁是用来保证数据库高并发的时候数据一致性的一种机制,例如车票售出
- 事务:对数据库的操作,而且专指一个序列上的操作,例如银行转帐(转出和转入要么都执行要么都不执行)
基本概念
- 数据库:IDBDatabase 对象
- 可以有任意多个数据库
- 含有版本的概念,同时只能有一个版本的数据库
- 对象仓库:IDBObjectStore 对象
- 数据库里面含有很多对象仓库,类似表
- 索引:IDBIndex 对象
- 加速数据的检索,可以在对象仓库里面,为不同的属性建立索引
- 指针:IDBCursor 对象
- 事务:IDBTransaction 对象
- 数据记录的读写和删改,都要通过事务完成
- 事务对象提供error、abort和complete三个事件,用来监听操作结果
- 操作请求:IDBRequest 对象
- 主键集合:IDBKeyRange 对象
基础操作
- 打开数据库
- 建表
- 写数据
- 查找数据
- 删除数据
- 更改数据
- 删除数据库
下面通过一个例子来完整的演示上面的操作
1、首先定义一些变量,当调用的时候传入给函数,就可以新建到自己想要的数据库和表
let defaultParams = {
indexdbName: '', // 数据库的名字
indexdbVersion: 1, // 数据库的版本
tableName: '', // 数据表的名字
data: '', // 影响的数据
unique: 'index' // keyPath(默认是主键为index)
}
2、打开数据库
// 如果发现没有数据库存在则会创建,执行
openIndexDB (params) {
// 如果浏览器本身不支持,则直接返回onupgradeneeded
// 如果数据库已经创建则会直接执行onsuccess
if (!window.indexedDB) { return }
let _this = this
// 把用户传入的参数和默认参数做合并,作为后面建数据库和建表的基础
let obj = Object.assign({}, defaultParams, params)
let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
openDBRequest.onsuccess = function () {
// 获取数据
_this.getData()
}
openDBRequest.onerror = function () {
console.log('openDBRequest onerror')
}
openDBRequest.onupgradeneeded = function (event) {
// request(即event.target)的result属性是IDBDatabase类型
let db = event.target.result
// 创建一个对象存储空间来存储信息
// createObjectStore()方法返回的是IDBObjectStore类型的对象
let objectStore = db.createObjectStore(obj.tableName, { keyPath: obj.unique, autoIncrement: obj.unique === 'index' })
// 创建一个索引来快速查找
// 可能会有重复的,因此我们不能使用 unique 索引。
objectStore.createIndex('name', 'name', { unique: false })
// 存储数据
// 在新创建的对象存储空间中保存值
for (let i in _this[obj.data]) {
objectStore.add(_this[obj.data][i])
}
}
}
3、在表里面添加数据
addData (params) {
if (!window.indexedDB) { return }
let _this = this
let obj = Object.assign({}, defaultParams, params)
let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
openDBRequest.onsuccess = function (event) {
const db = event.target.result
// 找到表,然后设置readwrite
const transaction = db.transaction([obj.tableName], 'readwrite')
let objectStore = transaction.objectStore(obj.tableName)
for (let i in _this[obj.data]) {
// 添加数据
objectStore.add(_this[obj.data][i])
}
}
}
4、查找数据
getData (params) {
let _this = this
let obj = Object.assign({}, defaultParams, params)
let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
openDBRequest.onsuccess = function (event) {
const db = event.target.result
const transaction = db.transaction([obj.tableName], 'readwrite')
let objectStore = transaction.objectStore(obj.tableName)
// openCursor()方法返回一个IDBRequest类型的请求对象cursorRequest,
// 如果该请求对象cursorRequest成功,则cursorRequest的result属性是IDBCursorWithValue类型的对象
let cursorRequest = objectStore.openCursor()
let list = []
cursorRequest.onsuccess = function (event) {
// cursorRequest的result属性是IDBCursorWithValue类型的对象
// 利用游标找到所有的数据
let cursor = event.target.result
if (cursor) {
list.push(cursor.value)
cursor.continue()
} else {
// 把找到的数据塞到data里面
_this[obj.data] = list
}
}
}
}
5、删除数据
// 删除某一条数据
deleteData (params, id) {
if (!window.indexedDB) { return }
let obj = Object.assign({}, defaultParams, params)
let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
openDBRequest.onsuccess = function (event) {
const db = event.target.result
const transaction = db.transaction([obj.tableName], 'readwrite')
let objectStore = transaction.objectStore(obj.tableName)
// 根据id删除数据
let request = objectStore.delete(id)
request.onsuccess = function (event) {
// console.log('删除数据成功', event.target.result)
}
}
}
// 删除该表的所有数据
deleteAll (params) {
if (!window.indexedDB) { return }
let obj = Object.assign({}, defaultParams, params)
let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
openDBRequest.onsuccess = function (event) {
const db = event.target.result
const transaction = db.transaction([obj.tableName], 'readwrite')
let objectStore = transaction.objectStore(obj.tableName)
// openCursor()方法返回一个IDBRequest类型的请求对象cursorRequest,
// 如果该请求对象cursorRequest成功,则cursorRequest的result属性是IDBCursorWithValue类型的对象
let cursorRequest = objectStore.openCursor()
cursorRequest.onsuccess = function (event) {
let cursor = event.target.result
if (cursor) {
let request = objectStore.delete(cursor.key)
request.onsuccess = function (event) {}
cursor.continue()
} else {
console.log('删除完成')
}
}
}
}
6、更改数据
updateData (params, newData) {
if (!window.indexedDB) { return }
let obj = Object.assign({}, defaultParams, params)
let openDBRequest = window.indexedDB.open(obj.indexdbName, obj.indexdbVersion)
openDBRequest.onsuccess = function (event) {
const db = event.target.result
const transaction = db.transaction([obj.tableName], 'readwrite')
let objectStore = transaction.objectStore(obj.tableName)
let request = objectStore.put(newData)
request.onsuccess = function (event) {
// console.log('更新数据成功')
}
request.onerror = function (event) {
// console.log('更新数据失败')
}
}
}
7、删除数据库
deleteIndexDB (params) {
// 如果浏览器本身不支持,则直接返回onupgradeneeded
// 如果数据库已经创建则会直接执行onsuccess
if (!window.indexedDB) { return }
let obj = Object.assign({}, defaultParams, params)
window.indexedDB.deleteDatabase(obj.indexdbName)
}
简单的使用场景(vue实现)
data () {
return {
list: [], // 用来渲染的列表数据
indexdbParams: { // 创建数据库用的参数
indexdbName: 'indexedDBTest', // 数据库的名字
indexdbVersion: 2, // 数据库的版本
unique: 'index', // keyPath(默认是主键为index)
tableName: 'test', // 数据表的名字
data: 'list' // 影响的数据
}
}
},
created () {
// 先打开数据库,并get数据
// success函数执行了getData,所以会去获取本地存储的数据放到this.list,作为默认的展示
this.openIndexDB(this.indexdbParams)
// 同时发出请求获取新的数据
this.getList()
}
methods: {
getList () {
fetch('/users.html')
.then(function(response) {
// 删除上次存储的数据
this.deleteAll(this.indexdbParams)
this.list = response.data
// 把新的数据写进去
this.addData(this.indexdbParams)
})
}
}
现在在控制台的Application下面的indexedDB里面就可以看到你存储数据了,更新之后需要在表的那个地方点击refresh database更新查看