常见的Web存储方式
Cookie
Local Storage
Session Storage
Web SQL
IndexedDB
WebSQL
Web SQL 是一种使用SQL语句来实现数据库中存储数据的网页API,引入了一套使用 SQL 操作客户端数据库方法。各家均采用SQLite进行实现。
核心方法
openDatabase:使用现有数据库或新建数据库来创建数据库对象
transaction:这个方法允许我们根据情况控制事务提交或回滚
executeSql:执行SQL 查询
WebSQL使用过程
openDatabase语法
//创建或打开数据库连接
/**
* @description 建立数据库连接
* @param databasename 数据库名称
* @param version 版本
* @param description 描述信息
* @param 数据库大小
* @param 回调函数(可选)
*/
openDatabase(databasename,version,description,size,[callback]);
实例:
const db = openDatabase('test_db', '1.0', 'hello websql', 5 * 1024 * 1024);
console.log(db);
transaction语法
/**
* @description 开启事务
* @param fx 事务回调
* @param errCallback 错误处理函数
* @param successCallback 事务成功回调
*/
db.transaction((fx)=>{},errCallback,successCallback)
excuteSql语法
/**
* @description 执行sql语句
* @param sql 要运行的sql语句
* @param params sql语句的参数,如果没有参数,传入空数组[]
* @param function 回调
*/
fx.executeSql(sql,params,function(tx,result){})
TodoList
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Web SQL 实现 TodoList</title>
<style>
* {
margin: 0;
padding: 0;
}
.layout-container {
margin: 100px auto;
max-width: 1200px;
}
.ready-list {
margin-top: 60px;
}
.item {
width: 100%;
display: flex;
line-height: 42px;
padding-left: 20px;
border-bottom: 1px dashed #ced8dc;
background-color: #f9f6f3;
color: #5c5c5c;
}
.item .order {
width: 40px;
}
.item .content {
flex: 1;
}
.item .act-btn {
padding: 0 30px;
background-color: #62a4f5;
border-radius: 4px;
border: 1px solid #64bcde;
color: #fff;
outline: none;
cursor: pointer;
}
</style>
</head>
<body>
<div class="layout-container">
<div id="app">
<div class="layout-type-box">
<input type="text" v-model.trim="content" />
<button @click="addItem">添加事项</button>
</div>
<ul class="ready-list">
<ul v-for="(item,index) in todoList" :key="item.id">
<li class="item">
<span class="order">{{item.id}}</span>
<p class="content">{{item.content}}</p>
<button class="act-btn" @click="delItem(item,index)">删除</button>
</li>
</ul>
</ul>
</div>
</div>
<script src="/static/js/vue.min.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
database: null,
content: null,
todoList: [],
newRow: null,
},
methods: {
//初始化数据库
initDatabase() {
const _that = this;
this.database = openDatabase(
"todo_db",
"1.0",
"todolist ",
10 * 1024 * 1024
);
this.database.transaction(
(tx) => {
//创建数据表
tx.executeSql(
"CREATE TABLE IF NOT EXISTS todolist_tb (id integer primary key autoincrement,content,create_at datetime,last_modified datetime)"
);
tx.executeSql(
`SELECT * FROM todolist_tb ORDER BY id DESC`,
[],
(tx, result) => {
_that.todoList = Object.values(result.rows);
}
);
},
(e) => {
//console.log("初始化表失败");
},
() => {
//console.log("创建数据表成功");
}
);
},
//存储
storageItem(content, errorCallback, completeCallback) {
const _that = this;
this.database.transaction(
(tx) => {
//创建数据表
tx.executeSql(
`INSERT INTO todolist_tb (content,create_at) VALUES (?,?)`,
[content, Date.now()],
(tx, result) => {
_that.todoList.push({
id: result.insertId,
content,
});
}
);
},
(e) => {
errorCallback && errorCallback(e);
},
() => {
completeCallback && completeCallback();
}
);
},
//新增事项
addItem() {
if (!this.content) {
alert("待办事项不能为空");
return false;
}
this.storageItem(
this.content,
() => {},
() => {
this.content = null;
}
);
},
//从db中删除记录
removeRecord(id, errorCallback, completeCallback) {
this.database.transaction(
(tx) => {
//创建数据表
tx.executeSql(
`DELETE FROM todolist_tb WHERE id=?`,
[id],
(tx, result) => {}
);
},
(e) => {
errorCallback && errorCallback(e);
},
() => {
completeCallback && completeCallback();
}
);
},
delItem(item, index) {
this.removeRecord(
item.id,
(e) => {
console.log(e);
},
() => {
this.todoList.splice(index, 1);
}
);
},
},
beforeMount: function () {
this.initDatabase();
},
mounted: function () {},
});
</script>
</body>
</html>
兼容性
IndexedDB
IndexedDB是HTML5规范里新出现的浏览器里内置的数据库,是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象),IndexedDB不是传统的RDBMS,类似于NoSQL。
IndexedDB里数据以对象的形式存储,每个对象都有一个key值索引。IndexedDB里的操作都是事务性的。一种对象存储在一个objectStore里,objectStore就相当于关系数据库里的表。IndexedDB可以有很多objectStore,objectStore里可以有很多对象。每个对象可以用key值获取。
使用方式
兼容性检查
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
if(!indexedDB){
console.log("你的浏览器不支持IndexedDB");
return false;
}
打开或创建数据库
indexedDB.open()语法
//创建或打开数据库连接
/**
* @description 建立数据库连接请求
* @param databasename 数据库名称
* @param version 版本号,可选,
*/
indexedDB.open(databaseName, version);
如果指定名称的数据不存在,则创建该名称的数据库。版本号可以在升级数据库时用来调整数据库结构和数据,打开已有数据库时,默认为当前版本;新建数据库时,默认为1。
indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件error
、success
、upgradeneeded
,处理打开数据库的操作结果。
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
if(!indexedDB){
console.log("你的浏览器不支持IndexedDB");
}else{
const request=indexedDB.open('testDB',1);
request.onerror = function(event){
alert('打开indexedDB失败')
}
request.onupgradeneeded = function(event){
alert('indexedDB版本更新')
};
request.onsuccess=function(){
alert('indexedDB建立成功')
}
};
当onerror事件触发时,可以通过事件对象的target.errorCode属性,获取错误相关信息
当增加数据库版本号时,会触发onupgradeneeded事件,可以通过事件对象的target.result属性,拿到数据库实例。如果 onupgradeneeded 事件成功执行完成,打开数据库请求的 onsuccess 处理函数会被触发
当触发onsuccess事件时通过request对象的result属性拿到数据库对象。
实例代码:
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
let db=null;
if(!indexedDB){
console.log("你的浏览器不支持IndexedDB");
}else{
const request=indexedDB.open('testDB',1);
request.onerror = function(event){
alert('打开indexedDB失败')
}
request.onupgradeneeded = function(event){
db = event.target.result;
alert('indexedDB版本更新')
};
request.onsuccess=function(){
db = request.result;
alert('indexedDB建立成功')
}
}
创建对象仓库
在新建或打开已有的indexedDB数据库后,需要使用createObjectStore方法创建对象仓库,类似于关系型数据中的一个table
createObjectStore语法
db.createObjectStore(storename, { keyPath:keyname,autoIncrement: true });
db.createObjectStore 方法接收2个参数,第一个为对象仓库名称,第二个参数为可选参数,是一个对象,其中的 keyPath属性为主键,autoIncrement 属性为 true,表示主键值自增。
db.objectStoreNames.contains方法
db.objectStoreNames.contains(storename);
检查是否包含该对象仓库onupgradeneeded
是唯一可以修改数据库结构的地方,所以所有创建和删除对象存储空间以及构建和删除索引的操作都需要放在其中。
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
let db=null;
if(!indexedDB){
console.log("你的浏览器不支持IndexedDB");
}else{
const request=indexedDB.open('testDB',2);
request.onerror = function(event){
alert('打开indexedDB失败')
}
request.onupgradeneeded = function(event){
db = event.target.result;
if (!db.objectStoreNames.contains('todoList')) {
//创建对象仓库
store = db.createObjectStore('todoList', {keyPath: 'id', autoIncrement: true });
//创建索引
store.createIndex("content", "content", { unique: false });
}
};
request.onsuccess=function(){
db = request.result;
console.log('indexedDB建立成功',db)
}
}
增删改查数据
在indexedDB中,使用事务来进行数据库的操作,任何存取对象操作都需要放在事务里执行,事务支持三种模式。
- readonly: 只读,不指定模式时默认为只读模式
- readwrite: 可读可写
- versionchange: 数据库版本变化
//新建事务
const transaction = db.transaction('todoList','readwrite')
//事务成功回调
transaction.oncomplete = function(event) {
console.log("Success");
};
//事务失败回调
transaction.onerror = function(event) {
console.log("Error");
};
增加数据
新建事务以后,通过IDBTransaction.objectStore(name)方法,获取 IDBObjectStore 对象,再通过对象的add()方法添加记录。
//创建事务
const transaction = db.transaction('todoList','readwrite')
transaction.oncomplete = function(event) {
console.log("Success");
};
transaction.onerror = function(event) {
console.log("Error");
};
const store=transaction.objectStore('todoList')
//添加记录
store.add({content: '这是测试内容', create_at:2021});
写入操作是一个异步操作,有success事件和error事件,可以绑定回调函数执行相应处理
//创建事务
const transaction = db.transaction('todoList','readwrite')
//事务完成回调
transaction.oncomplete = function(event) {
console.log("Success");
};
//事务失败回调
transaction.onerror = function(event) {
console.log("Error");
};
const store=transaction.objectStore('todoList')
const action=store.add({content: '这是测试内容', create_at:2021});
//写入操作成功回调
action.onsuccess = function (event) {
console.log('数据写入成功');
};
//写入操作失败回调
action.onerror = function (event) {
console.log('数据写入失败');
}
删除数据
使用 delete() 方法删除数据,参数是主键的值
//删除操作
const delAction = store.get(8);
delAction.onsuccess = function(e) {
console.log('删除成功')
}
修改数据
使用IDBObject.put()
方法修改数据
//修改操作
const updateAction = store.put({content: '这是新的测试内容', create_at:2021});
updateAction.onsuccess = function(e) {
console.log('修改数据成功');
}
updateAction.onerror = function (event) {
console.log('修改数据失败');
}
读取数据
objectStore.get()
方法用于读取数据,参数是主键的值
//读取操作
const readAction = store.get(8);
readAction.onsuccess = function(e) {
if (readAction.result) {
console.log('查询到数据结果:',readAction.result);
} else {
console.log('未查询到数据');
}
}
遍历数据
全局遍历
遍历数据仓库的所有记录,需要使用游标对象 IDBCursor
const store=transaction.objectStore('todoList')
let datas=[];
//使用游标进行遍历
store.openCursor().onsuccess=function(event){
const cursor = event.target.result;
if (cursor) {
console.log(cursor);
datas.push(cursor.value);
//游标指向下一个
cursor.continue();
} else {
console.log('没有数据了');
console.log(datas);
}
}
也可以使用getAll()
方法获取所有数据
store.getAll().onsuccess=function(event){
datas=event.target.result
};
限制遍历范围
openCursor()
接收两个参数,第一个参数是IDBKeyRange对象,用来限制游标范围,第二个参数表示游标的读取方向
/**
* @description 遍历数据
* @param IDBKeyRange 游标范围
* @param next 游标方向
*/
store.openCursor(IDBKeyRange,next)
IDBKeyRange 游标范围对象
IDBKeyRange.bound()方法
//创建游标范围
var myIDBKeyRange = IDBKeyRange.bound(lower, upper);
var myIDBKeyRange = IDBKeyRange.bound(lower, upper, lowerOpen);
var myIDBKeyRange = IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen);
lower 指定低边界
upper 指定高边界
lowerOpen 可选 指定是否包含低边界的值,默认为false
upperOpen 可选 指定是否包含高边界的值,默认为false
实例:
//指定边界从1到10 不包含1和10
var boundRange = IDBKeyRange.bound(1, 10, false, false);
细分方法
IDBKeyRange.only("Bill")
IDBKeyRange.lowerBound("Bill",true);
IDBKeyRange.upperBound("Bill",false);
var singleKeyRange = IDBKeyRange.only("Donna");
// 匹配所有超过“Bill”的,包括“Bill”
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
// 匹配所有超过“Bill”的,但不包括“Bill”
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
// 匹配所有不超过“Donna”的,但不包括“Donna”
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
// 匹配所有在“Bill”和“Donna”之间的,但不包括“Donna”
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
游标方向
next: 数据按主键值升序排序,主键值相等的数据都被读取到。
nextunique: 数据按主键值升序排序,主键值相等只读取第一条数据。
prev: 数据按主键值降序排序,主键值相等的数据都被读取。
prevunique: 数据按主键值降序排序,主键值相等只读取第一条数据。
索引
创建索引
createIndex方法创建索引
/**
* @description 创建索引
* @param indexName 索引名称
* @param fieldName 属性名称
* @param 索引配置对象
*/
objectStore.createIndex(indexName,fieldName, { unique: false });
通过索引查找
const store = transaction.objectStore('todoList');
const index = store.index('title');
const request = index.get('hello world');
request.onsuccess = function (e) {
const result = e.target.result;
if (result) {
// ...
} else {
// ...
}
}
TODO
todoList