IndexDB實現(xiàn)一個本地數(shù)據(jù)庫的增刪查改
在客戶端,我們所接觸到的絕大部分本地緩存方案主要有l(wèi)ocalStorage以及sessionStorage,其實Storage除了這兩大高頻api,另外還有IndexDB、cookies、WebSQL,Trust Token(信任令牌),cookies相對來說在前端接觸比另外幾個多點,IndexDB在平常業(yè)務(wù)中肯定有所耳聞,至于其他的貌似還真沒用過??
本文是筆者關(guān)于IndexDB的一個簡單的實踐示例,一起來學(xué)習(xí)下IndexDB,因為有時候,可能真的很有用。
正文開始...
在閱讀本文之前,本文主要從以下幾點去探討IndexDB
為什么會有IndexDB,本地localStorage與sessionStorage不夠用嗎
IndexDB有何特征
以一個示例加深對于IndexDB的理解
IndexDB在什么情況下能為我們的業(yè)務(wù)解決什么樣的問題
了解IndexDB
根據(jù)官方MDNIndex DB[1]文檔查詢解釋
IndexDB是瀏覽器提供的一種可持久化數(shù)據(jù)存儲方案
支持多種類型的鍵,可以支持存儲任何類型的數(shù)據(jù)
支持鍵檢索,查詢,新增,刪除操作
在客戶端瀏覽器可以存儲大數(shù)據(jù)量,適用于離線應(yīng)用
所有接口都是基于事件 在與lcoalStorage或者seesionStorage來說,IndexDB存儲數(shù)據(jù)量更大,更強(qiáng)大
IndexDB特征
你可以把IndexDB當(dāng)成一個本地的數(shù)據(jù)庫,如果你要使用它。那么會有以下幾個步驟
打開數(shù)據(jù)庫,創(chuàng)建本地數(shù)據(jù)庫并連接IndexDB.open('lcoal-test')
創(chuàng)建對象庫db.createObjectStore('user')
基于事務(wù)操作本地數(shù)據(jù)庫,實現(xiàn)增刪查改
舉個例子
本示例主要考慮最簡單方式實現(xiàn),也不依賴任何工程化工具,首先新建一個index.html,在index.html中引入vue2.7,vue2.7出來了,嘗下鮮,主要支持組合式api方式了,基本api使用上與組合式API沒有啥區(qū)別。
并且,這里我沒有直接用原生IndexDB,而是使用了官方文檔推薦的一個庫dexie.js[2],因為官方原生API太難用了,而這個庫是對原生IndexDB的二次封裝,使用起來更高效
index.html
<!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>初識index-db</title>
<link rel="stylesheet" href="./css/index.css" />
</head>
<body>
<div id="app">
<h3>{{lesson}}</h3>
<a href="javascript:void(0)" @click="handleAdd('add')">新增</a>
<div class="content-box">
<div class="search-bar">
<input type="text" placeholder="請輸入名稱" v-model="searchName" />
<span @click="handleSearch">點擊搜索</span>
</div>
<template v-for="(item) in initData">
<p>
<span>{{item.name}}</span>
<span>{{item.age}}</span>
<span @click="handleAdd('edit',item)">編輯</span>
<span @click="handleDel(item)">刪除</span>
</p>
</template>
</div>
<div class="wrap-modal" v-if="showDiag">
<input placeholder="請輸入name" v-model="formParams.name" />
<input placeholder="請輸入age" v-model="formParams.age" />
<div>
<span @click="handleSure">確認(rèn)</span>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.0/vue.min.js"></script>
<script src="./js/dexie.min.js"></script>
</body>
</html>
然后我們引入業(yè)務(wù)js
...
<script type="module">
// 引入hooks
import { useApp, useIndexDB } from './hooks/index.js';
const { reactive, toRefs, createApp, onMounted } = Vue;
const App = {
setup() {
const { searchName, lesson, initData, showDiag, view, formParams } = useApp();
const { add_indexDB, update_indexDB, search_indexDB, del_indexDB } = useIndexDB();
// todo 查詢數(shù)據(jù)
const featchList = async (searchName = '') => {
const colletion = await search_indexDB(searchName);
initData.value = colletion;
};
onMounted(() => {
featchList();
});
// todo 編輯or添加
const handleAdd = (viewType, row) => {
searchName.value = '';
view.value = viewType;
showDiag.value = true;
// 編輯
if (view.value === 'edit') {
console.log(row);
formParams.value = {
...row
};
} else {
// 添加
formParams.value.name = '';
formParams.value.age = '';
}
};
const handleSure = () => {
showDiag.value = false;
view.value === 'edit' ? update_indexDB(formParams.value, featchList) : add_indexDB(formParams.value, featchList);
};
const handleDel = (row) => {
del_indexDB(row.id, featchList);
};
// 搜索
const handleSearch = () => {
featchList(searchName.value);
};
return {
searchName,
lesson,
showDiag,
initData,
formParams,
handleAdd,
handleSure,
handleDel,
handleSearch
};
}
};
// 綁定app
const app = new Vue(App).$mount('#app');
</script>
我們看下這里面引入的useApp, useIndexDB
// hooks/index.js
const { reactive, toRefs, ref } = Vue;
export const useApp = () => {
const useInfo = reactive({
lesson: '初識IndexDB,實現(xiàn)本地crud一把梭',
initData: [],
showDiag: false,
view: 'add',
searchName: '',
formParams: {
name: '',
age: ''
}
});
return {
...toRefs(useInfo)
}
}
// IndexDB hooks
export const useIndexDB = () => {
const db = new Dexie('local-test');
db.version(1).stores({
user: '++id, name, age'
});
// 添加數(shù)據(jù)
const add_indexDB = (params, callback) => {
db.user.add(params);
callback();
}
// 更新數(shù)據(jù)
const update_indexDB = (params, callback) => {
db.user.put(params);
callback()
}
// 查詢
const search_indexDB = async (params) => {
const colletion = params ? await db.user.where('name').equals(params).toArray() : await db.user.toArray();
return colletion;
}
// 刪除
const del_indexDB = (id, callback) => {
db.user.where('id').equals(id).delete();
callback();
}
return {
db,
add_indexDB,
update_indexDB,
search_indexDB,
del_indexDB
}
}
頁面已經(jīng)搭完,我們打開頁面看下
新增
現(xiàn)在我們新增一條數(shù)據(jù),在頁面點擊新增按鈕,在applcation/Storage/IndexDB中就會保存一條數(shù)據(jù)
當(dāng)我們刷新時,數(shù)據(jù)頁面仍然會保留上一次的數(shù)據(jù)
在我們新增操作,然后刷新的過程中主要發(fā)生了什么呢
其實IndexDB主要做了以下幾件事情
// hooks/index.js
// 1 建立連接,創(chuàng)建db
const db = new Dexie('local-test');
//2 創(chuàng)建了一個user的表名
db.version(1).stores({
user: '++id, name, age'
});
// 3 向user中添加數(shù)據(jù)
// 添加數(shù)據(jù)
const add_indexDB = (params, callback) => {
db.user.add(params);
callback();
}
//4 查詢user表中的數(shù)據(jù),并返回
const search_indexDB = async (params) => {
const colletion = params ? await db.user.where('name').equals(params).toArray() : await db.user.toArray();
return colletion;
}
在點擊創(chuàng)建時,然后點擊確認(rèn)操作,就是在創(chuàng)建數(shù)據(jù)操作
...
// 點擊確認(rèn)會調(diào)用這個方法
const handleSure = () => {
// showDiag.value = false;
view.value === 'edit' ? update_indexDB(formParams.value, featchList) : add_indexDB(formParams.value, featchList);
};
并且注意,我們還傳入了一個featchList方法,這是在添加數(shù)據(jù)成功了,我們重新更新頁面數(shù)據(jù)的一個回調(diào)
...
// todo 查詢數(shù)據(jù)
const featchList = async (searchName = '') => {
const colletion = await search_indexDB(searchName);
// 頁面數(shù)據(jù)賦值
initData.value = colletion;
};
...
至此一個增加操作流程就已經(jīng)結(jié)束
更新
當(dāng)我們點擊編輯時,我們嘗試修改名稱,然后點擊確認(rèn),那么此時就調(diào)用更新數(shù)據(jù)操作
// hooks/index.js
// 更新數(shù)據(jù)
const update_indexDB = (params, callback) => {
db.user.put(params);
callback()
}
我們使用的是put方法直接就可以更新數(shù)據(jù)了
更新前
當(dāng)我點擊編輯
更新后
我們可以刷新右側(cè)的刷新按鈕現(xiàn)實對應(yīng)的數(shù)據(jù)
刪除
...
// 刪除
const del_indexDB = (id, callback) => {
db.user.where('id').equals(id).delete();
callback();
}
...
刪除前
刪除后
當(dāng)我們刪除后,又可以重新添加
但是我們發(fā)現(xiàn),每次只能添加一次,如果重復(fù)添加,那么此時會添加不了
主要原因是store中的key重復(fù)了,無法重復(fù)添加,但是你把上一條刪除了,你就可以重復(fù)添加了
而且你刪除后,當(dāng)你刷新頁面,那條數(shù)據(jù)就真的沒有,當(dāng)你新增一條數(shù)據(jù),只要你不刪除,那么打開頁面數(shù)據(jù)就會一直在頁面中。
所以IndexDB這個相當(dāng)于在前端設(shè)計了一個小型數(shù)據(jù)庫能力了,真的是666。
什么樣業(yè)務(wù)適合用IndexDB
在上一個例子中,我們嘗試用簡單的一個例子去了解了IndexDB,但是在具體實際業(yè)務(wù)中,我們也很少會使用IndexDB去做這種殺雞用牛刀的事,因為localStorage與sessionStorage也可以滿足了,但如果是那種大數(shù)據(jù)量計算,如果涉及步驟操作那種,比如在這樣的一個業(yè)務(wù)場景中,現(xiàn)在比較流行的低代碼平臺,拖拉拽的幾個步驟就能生成一個頁面,如果中途我只完成了一部分操作,頁面不小心關(guān)掉了,此時如果你又讓用戶重新配置操作,那么體驗就不會那么好,因此你可以嘗試用IndexDB去做你操作流程的本地數(shù)據(jù)持久化操作,因為IndexDB可以存儲足夠大的數(shù)據(jù)量,你只需要保證你存的Schema數(shù)據(jù)能正常渲染你的頁面就行,或者你的暫存操作也可以不用服務(wù)端處理,暫存功能完全可以依賴客戶端做,這樣也會減少服務(wù)端的壓力。
總結(jié)
基礎(chǔ)的了解IndexDB,它是瀏覽器提供的一種可持久化緩存數(shù)據(jù)方案,相當(dāng)于一個本地的數(shù)據(jù)庫
寫了一個簡單的例子,支持IndexDB的增刪查改功能
探討了業(yè)務(wù)實際使用場景,一般用于存儲大數(shù)據(jù)量,暫存操作等
本文示例code example[3]
最后,看完覺得有收獲的,點個贊,在看,轉(zhuǎn)發(fā),收藏等于學(xué)會,歡迎關(guān)注Web技術(shù)學(xué)苑,好好學(xué)習(xí),天天向上!
參考資料
[1]
Index DB: https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB
[2]
dexie.js: https://dexie.org/docs/API-Reference
[3]
code example: https://github.com/maicFir/lessonNote/tree/master/javascript/16-indexDB
作者:Maic
歡迎關(guān)注微信公眾號 :web技術(shù)學(xué)苑