一、WebSQL
WebSQL是前端的一個(gè)獨(dú)立模塊,是web存儲(chǔ)方式的一種,我們調(diào)試的時(shí)候會(huì)經(jīng)常看到,只是一般很少使用。并且,當(dāng)前只有谷歌支持,ie和火狐均不支持。

我們對(duì)數(shù)據(jù)庫的一般概念是后端才會(huì)跟數(shù)據(jù)庫打交道,進(jìn)行一些業(yè)務(wù)性的增刪改查。而這里的數(shù)據(jù)庫也不同于真正意義上的數(shù)據(jù)庫。
廢話少說,先出招吧:
主要方法:
- openDatabase:這個(gè)方法使用現(xiàn)有的數(shù)據(jù)庫或者新建的數(shù)據(jù)庫創(chuàng)建一個(gè)數(shù)據(jù)庫對(duì)象。
- transaction:這個(gè)方法讓我們能夠控制一個(gè)事務(wù),以及基于這種情況執(zhí)行提交或者回滾。
- executeSql:這個(gè)方法用于執(zhí)行實(shí)際的 SQL 查詢。
openDatabase() 方法對(duì)應(yīng)的五個(gè)參數(shù)說明:
- 數(shù)據(jù)庫名稱
- 版本號(hào)
- 描述文本
- 數(shù)據(jù)庫大小
- 創(chuàng)建回調(diào)
transaction執(zhí)行數(shù)據(jù)庫操作,操作內(nèi)容就是正常的數(shù)據(jù)庫的增刪改查。
executeSql是執(zhí)行具體的sql,參數(shù)是1.sql語句(大寫?),2.語句中的變量,3.執(zhí)行后的回調(diào),4.
示例:
[
](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;"> 1 var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
2 db.transaction(function (tx) { 3 tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
4 tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "菜鳥教程")');
5 tx.executeSql('INSERT INTO LOGS (id,log) VALUES (?, ?)', [e_id, e_log]); //使用外部變量,執(zhí)行時(shí)會(huì)將變量數(shù)組中的值依次替換前邊的問號(hào)位置
6 tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) { 7 var len = results.rows.length, i; msg = "<p>查詢記錄條數(shù): " + len + "</p>"; document.querySelector('#status').innerHTML += msg; for (i = 0; i < len; i++){ msg = "<p><b>" + results.rows.item(i).log + "</b></p>"; document.querySelector('#status').innerHTML += msg; } 8 }, null); //查詢和回調(diào)
9 tx.executeSql('DELETE FROM LOGS WHERE id=1'); //刪除
10 tx.executeSql('DELETE FROM LOGS WHERE id=?', [id]); 11 tx.executeSql('UPDATE LOGS SET log='www.w3cschool.cc' WHERE id=2'); //更新
12 tx.executeSql('UPDATE LOGS SET log='www.w3cschool.cc' WHERE id=?', [id]); 13 });</pre>

](javascript:void(0); "復(fù)制代碼")
基本操作與實(shí)際數(shù)據(jù)庫操作基本一致。
最終的數(shù)據(jù)去向,我理解為只是做臨時(shí)存儲(chǔ)和大型網(wǎng)站的業(yè)務(wù)運(yùn)行存儲(chǔ)緩存的作用,頁面刷新后該庫就不存在了。而其本身與關(guān)系數(shù)據(jù)庫的概念比較相似。
二、IndexedDB
誕生背景:
隨著瀏覽器的功能不斷增強(qiáng),越來越多的網(wǎng)站開始考慮,將大量數(shù)據(jù)儲(chǔ)存在客戶端,這樣可以減少從服務(wù)器獲取數(shù)據(jù),直接從本地獲取數(shù)據(jù)?,F(xiàn)有的瀏覽器數(shù)據(jù)儲(chǔ)存方案,都不適合儲(chǔ)存大量數(shù)據(jù):Cookie 的大小不超過4KB,且每次請(qǐng)求都會(huì)發(fā)送回服務(wù)器;LocalStorage 在 2.5MB 到 10MB 之間(各家瀏覽器不同),而且不提供搜索功能,不能建立自定義的索引。所以,需要一種新的解決方案,這就是 IndexedDB 誕生的背景。
IndexedDB是瀏覽器提供的本地?cái)?shù)據(jù)庫, 允許儲(chǔ)存大量數(shù)據(jù),提供查找接口,還能建立索引。這些都是 LocalStorage 所不具備的。就數(shù)據(jù)庫類型而言,IndexedDB 不屬于關(guān)系型數(shù)據(jù)庫(不支持 SQL 查詢語句),更接近 NoSQL 數(shù)據(jù)庫。
IndexedDB 具有以下特點(diǎn):
(1)鍵值對(duì)儲(chǔ)存。 IndexedDB 內(nèi)部采用對(duì)象倉庫(object store)存放數(shù)據(jù)。所有類型的數(shù)據(jù)都可以直接存入,包括 JavaScript 對(duì)象。對(duì)象倉庫中,數(shù)據(jù)以"鍵值對(duì)"的形式保存,每一個(gè)數(shù)據(jù)記錄都有對(duì)應(yīng)的主鍵,主鍵是獨(dú)一無二的,不能有重復(fù),否則會(huì)拋出一個(gè)錯(cuò)誤。
(2)異步。 IndexedDB 操作時(shí)不會(huì)鎖死瀏覽器,用戶依然可以進(jìn)行其他操作,這與 LocalStorage 形成對(duì)比,后者的操作是同步的。異步設(shè)計(jì)是為了防止大量數(shù)據(jù)的讀寫,拖慢網(wǎng)頁的表現(xiàn)。
(3)支持事務(wù)。 IndexedDB 支持事務(wù)(transaction),這意味著一系列操作步驟之中,只要有一步失敗,整個(gè)事務(wù)就都取消,數(shù)據(jù)庫回滾到事務(wù)發(fā)生之前的狀態(tài),不存在只改寫一部分?jǐn)?shù)據(jù)的情況。
(4)同源限制 IndexedDB 受到同源限制,每一個(gè)數(shù)據(jù)庫對(duì)應(yīng)創(chuàng)建它的域名。網(wǎng)頁只能訪問自身域名下的數(shù)據(jù)庫,而不能訪問跨域的數(shù)據(jù)庫。
(5)儲(chǔ)存空間大 IndexedDB 的儲(chǔ)存空間比 LocalStorage 大得多,一般來說不少于 250MB,甚至沒有上限。
(6)支持二進(jìn)制儲(chǔ)存。 IndexedDB 不僅可以儲(chǔ)存字符串,還可以儲(chǔ)存二進(jìn)制數(shù)據(jù)(ArrayBuffer 對(duì)象和 Blob 對(duì)象)。
IndexedDB的一些基本概念:
- 數(shù)據(jù)庫:IDBDatabase 對(duì)象
- 對(duì)象倉庫:IDBObjectStore 對(duì)象
- 索引: IDBIndex 對(duì)象
- 事務(wù): IDBTransaction 對(duì)象
- 操作請(qǐng)求:IDBRequest 對(duì)象
- 指針: IDBCursor 對(duì)象
- 主鍵集合:IDBKeyRange 對(duì)象
(1)數(shù)據(jù)庫。
數(shù)據(jù)庫是一系列相關(guān)數(shù)據(jù)的容器。每個(gè)域名(嚴(yán)格的說,是協(xié)議 + 域名 + 端口)都可以新建任意多個(gè)數(shù)據(jù)庫。
IndexedDB 數(shù)據(jù)庫有版本的概念。同一個(gè)時(shí)刻,只能有一個(gè)版本的數(shù)據(jù)庫存在。如果要修改數(shù)據(jù)庫結(jié)構(gòu)(新增或刪除表、索引或者主鍵),只能通過升級(jí)數(shù)據(jù)庫版本完成。
(2)對(duì)象倉庫
每個(gè)數(shù)據(jù)庫包含若干個(gè)對(duì)象倉庫(object store)。它類似于關(guān)系型數(shù)據(jù)庫的表格。
(3)數(shù)據(jù)記錄
對(duì)象倉庫保存的是數(shù)據(jù)記錄。每條記錄類似于關(guān)系型數(shù)據(jù)庫的行,但是只有主鍵和數(shù)據(jù)體兩部分。主鍵可以是數(shù)據(jù)記錄里面的一個(gè)屬性,也可以指定為一個(gè)遞增的整數(shù)編號(hào)。
(4)索引
(5)事務(wù)
數(shù)據(jù)記錄的讀寫和刪改,都要通過事務(wù)完成。事務(wù)對(duì)象提供error、abort和complete三個(gè)事件,用來監(jiān)聽操作結(jié)果。
基本操作:
(1)打開數(shù)據(jù)庫
使用 IndexedDB 的第一步是打開數(shù)據(jù)庫,使用indexedDB.open()方法。
這個(gè)方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)是字符串,表示數(shù)據(jù)庫的名字。如果指定的數(shù)據(jù)庫不存在,就會(huì)新建數(shù)據(jù)庫。第二個(gè)參數(shù)是整數(shù),表示數(shù)據(jù)庫的版本。如果省略,打開已有數(shù)據(jù)庫時(shí),默認(rèn)為當(dāng)前版本;新建數(shù)據(jù)庫時(shí),默認(rèn)為1。
indexedDB.open()方法返回一個(gè) IDBRequest 對(duì)象。這個(gè)對(duì)象通過三種事件error、success、upgradeneeded,處理打開數(shù)據(jù)庫的操作結(jié)果。
(2)新建數(shù)據(jù)庫
eg:
[
](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">var db; var objectStore; var request = window.indexedDB.open(databaseName, version);
request.onerror = function (event) {}
request.onsuccess = function (event) {
db = request.result//可以拿到數(shù)據(jù)庫對(duì)象
} //如果指定的版本號(hào),大于數(shù)據(jù)庫的實(shí)際版本號(hào),就會(huì)發(fā)生數(shù)據(jù)庫升級(jí)事件upgradeneeded
request.onupgradeneeded = function (event) {
db = event.target.result; if (!db.objectStoreNames.contains('person')) {//判斷是否存在
objectStore = db.createObjectStore('person', { keyPath: 'id' }); //自動(dòng)生成主鍵db.createObjectStore( // 'person', // { autoIncrement: true } //);
} //新建索引,參數(shù)索引名稱、索引所在的屬性、配置對(duì)象
objectStore.createIndex('email', 'email', { unique: true });
}</pre>

](javascript:void(0); "復(fù)制代碼")
(3)新增數(shù)據(jù)
在以上操作的基礎(chǔ)上,需要新建一個(gè)事務(wù)。新建時(shí)必須指定表格名稱和操作模式("只讀"或"讀寫")。新建事務(wù)以后,通過IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 對(duì)象,再通過表格對(duì)象的add()方法,向表格寫入一條記錄。
寫入操作是一個(gè)異步操作,通過監(jiān)聽連接對(duì)象的success事件和error事件,了解是否寫入成功。
eg:
[
](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">function add() { var request = db.transaction(['person'], 'readwrite')
.objectStore('person')
.add({ id: 1, name: '張三', age: 24, email: 'zhangsan@example.com' });
request.onsuccess = function (event) {
console.log('數(shù)據(jù)寫入成功');
};
request.onerror = function (event) {
console.log('數(shù)據(jù)寫入失敗');
}
}
add();</pre>
[
](javascript:void(0); "復(fù)制代碼")
(4)讀取數(shù)據(jù)
objectStore.get()方法用于讀取數(shù)據(jù),參數(shù)是主鍵的值。
eg:
[
](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">function read() { var transaction = db.transaction(['person']); var objectStore = transaction.objectStore('person'); var request = objectStore.get(1);
request.onerror = function(event) {
console.log('事務(wù)失敗');
};
request.onsuccess = function( event) { if (request.result) {
console.log('Name: ' + request.result.name);
console.log('Age: ' + request.result.age);
console.log('Email: ' + request.result.email);
} else {
console.log('未獲得數(shù)據(jù)記錄');
}
};
}
read();</pre>
[
](javascript:void(0); "復(fù)制代碼")
(5)遍歷數(shù)據(jù)
遍歷數(shù)據(jù)表格的所有記錄,要使用指針對(duì)象 IDBCursor。openCursor()方法是一個(gè)異步操作,所以要監(jiān)聽success事件。

](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">function readAll() { var objectStore = db.transaction('person').objectStore('person');
objectStore.openCursor().onsuccess = function (event) { var cursor = event.target.result; if (cursor) {
console.log('Id: ' + cursor.key);
console.log('Name: ' + cursor.value.name);
console.log('Age: ' + cursor.value.age);
console.log('Email: ' + cursor.value.email);
cursor.continue();
} else {
console.log('沒有更多數(shù)據(jù)了!');
}
};
}
readAll();</pre>
[
](javascript:void(0); "復(fù)制代碼")
(6)數(shù)據(jù)更新
IDBObject.put()方法。

](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">function update() { var request = db.transaction(['person'], 'readwrite')
.objectStore('person')
.put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });
request.onsuccess = function (event) {
console.log('數(shù)據(jù)更新成功');
};
request.onerror = function (event) {
console.log('數(shù)據(jù)更新失敗');
}
}
update();</pre>
[
](javascript:void(0); "復(fù)制代碼")
(7)數(shù)據(jù)刪除
IDBObjectStore.delete()方法用于刪除記錄。

](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">function remove() { var request = db.transaction(['person'], 'readwrite')
.objectStore('person')
.delete(1);
request.onsuccess = function (event) {
console.log('數(shù)據(jù)刪除成功');
};
}
remove();</pre>
[
](javascript:void(0); "復(fù)制代碼")
(8)索引的使用
添加索引后可以使用索引查詢數(shù)據(jù)
[
](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 15px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; line-height: 1.72222; color: inherit; border-radius: 6px;">var transaction = db.transaction(['person'], 'readonly'); var store = transaction.objectStore('person'); var index = store.index('name'); var request = index.get('李四');
request.onsuccess = function (e) { var result = e.target.result; if (result) { // ...
} else { // ...
}
}</pre>

](javascript:void(0); "復(fù)制代碼")