简介及应用场景
浏览器的大数据量存储方式,相当于数据库,webSql为关系型DB,indexDB为非关系型DB。
应用场景:
1、离线存储(C端应用)
2、富文本编辑,缓存记录,防止关闭标签页丢失数据。
浏览器支持:
/ | Chrome | IE | Firefox |
---|---|---|---|
webSql | 1 | 0 | 0 |
indexDB | 1 | 0 | 1 |
Web Sql DataBase
w3c:https://www.w3.org/TR/webdatabase/
使用Sqlite方言,目前规范停滞。
刷新后库删除。
主要方法
1. window.openDatabase:使用现有的数据库或者新建的数据库创建一个数据库对象。
2. Database transaction:控制一个事务,以及基于这种情况执行提交或者回滚。
3. SQLTransaction executeSql:用于执行实际的 SQL 查询。
- openDatabase()
openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024)
方法对应的五个参数说明:
1. 数据库名称
2. 版本号
3. 描述文本
4. 数据库大小
5. 创建回调
- db.transaction()
参数有三
1. 事务内容方法
2. 事务失败方法
3. 事务成功方法
db.transaction((transaction) => {
transaction.executeSql('...')
...
}, errorCb, successCb)
重点:
1. 只有executeSql抛出异常才会被transaction捕获;
2. 若executeSql已对异常进行处理(不throw),transaction则认为事务成功;
3. executeSql无失败回调默认视为抛出异常。
- transaction.executeSql()
方法参数:sql语句、values参数(数组)、成功回调(参数:SQLTransaction,SQLResultSet)、失败回调(参数:SQLTransaction,SQLError)
0. 建表
transaction.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)')
1. 增
由于新增重复ID会中断事务,所以在新增时最好加上失败回调,错误处理(不throw就可以)
transaction.executeSql('INSERT INTO LOGS (id,log) VALUES (1, "...")')
或者使用插值,以下其他操作同理
transaction.executeSql('INSERT INTO LOGS (id, field) VALUES (?, ?)', [id_value, field_value])
成功回调SQLResultSet,主要返回为rowsAffected,表示影响行数
2. 删
sql语句正常时(指语法无误)无失败情况
transaction.executeSql('DELETE FROM LOGS WHERE id=1')
成功回调SQLResultSet,主要返回为rowsAffected,表示影响行数
3. 改
sql语句正常时无失败情况
transaction.executeSql("UPDATE LOGS SET log='...' WHERE id=2")
成功回调SQLResultSet,主要返回为rowsAffected,表示影响行数
4. 查
sql语句正常时无失败情况
transaction.executeSql("SELECT * FROM LOGS")
成功回调SQLResultSet,主要返回为rows:Array<SQLResultSetRow>,表示查询结果
indexDB
IndexedDB (后简称IDB)具有以下特点。
(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
刷新后不会重置DB。
使用步骤:
1. 打开数据库
let request = indexedDB.open('test', 2)
IDBRequest
打开操作有4种结果——onsuccess, onerror, onupgradeneeded, onblocked,重点就是成功和升级
在成功时拿到数据库对象
let db
request.onsuccess = function(e) {
db = request.result
}
如果是第一次打开数据库,会经历一次升级;升级的触发条件是数据库版本发生改变时,第一次打开的时候可以看作是从0到1,所以会触发,另外数据表的新建也要在这个阶段进行,而不是在success阶段。
2. 创建数据表
IDB的数据表类型为IDBObjectStore
,为与其他sql类比,统称为数据表
let objectStore;
if (!db.objectStoreNames.contains('person')) {
objectStore = db.createObjectStore('person', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('email', 'email', { unique: true });
}
上面这段代码进行了以下操作:
1. 判定表集合有无目标表名称(person)
2. 如无目标表则创建该表,并指定主键及自增
3. 创建表索引——name和email,其中email字段要求唯一,同主键
3. 具体操作
IDB通过事务的方式操作数据表,不过IDB的transaction不是在内部通过执行sql作为回调的形式,而是链式调用objectStore方式拿到表进行操作
function getObjectStore(name) {
return db.transaction([name], 'readwrite').objectStore(name);
}
拿到表后,进行操作会返回IDBRequest,由于操作是异步的,所以只能通过监听IDBRequest的状态来判断操作是否成功
<button onclick="clearTable()">清空表</button>
<button onclick="queryAll()">查询整表</button>
<button onclick="clearLog()">清空记录</button>
<button onclick="test()">test</button>
<p>增</p>
<label for="name">name</label>
<input type="text" id="name" />
<label for="age">age</label>
<input type="text" id="age" />
<label for="email">email</label>
<input type="text" id="email" />
<button onclick="insert()">插入</button>
<p>删</p>
<label for="delId">id</label>
<input type="text" id="delId" />
<button onclick="deleteById()">删除</button>
<p>改</p>
<label for="updateId">id</label>
<input type="text" id="updateId" />
<label for="updateName">name</label>
<input type="text" id="updateName" />
<label for="updateAge">age</label>
<input type="text" id="updateAge" />
<label for="updateEmail">email</label>
<input type="text" id="updateEmail" />
<button onclick="update()">修改</button>
<p>查</p>
<label for="queryId">id</label>
<input type="text" id="queryId" />
<button onclick="queryById()">按id查</button>
<input type="radio" name="index" id="indexName" value="name" checked />name <input type="radio" name="index" id="indexEmail" value="email" />email
<input type="text" id="index" />
<button onclick="queryByIndex()">按索引查</button>
<p id="record"></p>
<div id="all"></div>
<script>
let db;
let request = indexedDB.open('test');
request.onerror = function (e) {
console.log('error...');
};
request.onsuccess = function (e) {
console.log('success...');
db = request.result;
};
request.onupgradeneeded = function (e) {
console.log('upgrade...');
db = e.target.result;
let objectStore;
if (!db.objectStoreNames.contains('person')) {
objectStore = db.createObjectStore('person', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('email', 'email', { unique: true });
}
};
request.onblocked = function (e) {
console.log('blocked...');
};
// utils
function getObjectStore(name) {
return db.transaction([name], 'readwrite').objectStore(name);
}
function transaction(request, success, error) {
request.onsuccess = function (e) {
success(e);
};
request.onerror = function (e) {
console.log(e);
log('操作失败: ' + e.target.error);
error && error();
};
}
function log(msg) {
$('#record').append(`<p>${msg}</p>`);
}
function clearLog() {
$('#record').empty();
}
// operation
function insert() {
let id = $('#id').val();
let name = $('#name').val();
let age = $('#age').val();
let email = $('#email').val();
add({ id, name, age, email });
}
function add(person) {
transaction(getObjectStore('person').add({ name: person.name, age: person.age, email: person.email }), () => {
log('插入成功');
queryAll();
});
}
function deleteById() {
let id = $('#delId').val();
// 删除错误id不会报错
transaction(getObjectStore('person').delete(+id), (e) => {
if (e.target.result) {
log('删除成功');
queryAll();
} else {
log(`id为${id}的记录不存在`);
}
});
}
function update() {
let id = +$('#updateId').val();
let name = $('#updateName').val();
let age = $('#updateAge').val();
let email = $('#updateEmail').val();
// 修改错误id不会报错,且会新增记录
transaction(getObjectStore('person').put({ id, name, age, email }), () => {
log('修改成功');
queryAll();
});
}
function queryById() {
let id = $('#queryId').val();
// 查询错误id不会报错,result为undefined
transaction(getObjectStore('person').get(id), (e) => {
let v = e.target.result || {};
log(`查询id为${id}结果: <p>${v.id || '--'} | ${v.name || '--'} | ${v.age || '--'} | ${v.email || '--'}</p>`);
});
}
function queryByIndex() {
let content = $('#index').val();
let index = $('input[name=index]:checked').val();
// 查询错误index会报错;但是对于存在的index,错误的搜索内容不会报错,result为undefined
transaction(getObjectStore('person').index(index).get(content), (e) => {
let v = e.target.result || {};
log(`查询${index}为${content}结果: <p>${v.id || '--'} | ${v.name || '--'} | ${v.age || '--'} | ${v.email || '--'}</p>`);
});
}
function clearTable() {
transaction(getObjectStore('person').clear(), (e) => {
log('清空成功');
});
}
function queryAll() {
let request = getObjectStore('person').getAll();
transaction(request, (e) => {
let res = e.target.result;
let content = `<p>查询结果:id | name | age | email</p>`;
res.forEach((v) => {
content += `<p>${v.id || '--'}(${typeof v.id}) | ${v.name || '--'} | ${v.age || '--'} | ${v.email || '--'}</p>`;
});
$('#all').html(content);
});
}
function test() {
transaction(getObjectStore('person').getAllKeys(), (e) => {
console.log(e);
});
}
</script>
</body>
网友评论