您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何在React應用中使用Dexie.js進行離線數據存儲”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何在React應用中使用Dexie.js進行離線數據存儲”吧!
離線存儲應用程序數據已成為現代 Web 開發中的必要條件。內置的瀏覽器 localStorage 可以用作簡單輕量數據的數據存儲,但是在結構化數據或存儲大量數據方面卻不足。
最重要的是,我們只能將字符串數據存儲在受 XSS 攻擊的 localStorage 中,并且它沒有提供很多查詢數據的功能。
這就是 IndexedDB 的亮點。使用 IndexedDB,我們可以在瀏覽器中創建結構化的數據庫,將幾乎所有內容存儲在這些數據庫中,并對數據執行各種類型的查詢。
在本文中,我們將了解 IndexedDB 的全部含義,以及如何使用 Dexie.js(用于 IndexedDB 的簡約包裝)處理 Web 應用程序中的離線數據存儲。
IndexedDB 是用于瀏覽器的內置非關系數據庫。它使開發人員能夠將數據持久存儲在瀏覽器中,即使在脫機時也可以無縫使用 Web 應用程序。使用 IndexedDB 時,您會經常看到兩個術語:數據庫存儲和對象存儲。讓我們在下面進行探討。
IndexedDB 數據庫對每個 Web 應用程序來說都是唯一的。這意味著一個應用程序只能從與自己運行在同一域或子域的 IndexedDB 數據庫中訪問數據。數據庫是容納對象存儲的地方,而對象存儲又包含存儲的數據。要使用 IndexedDB 數據庫,我們需要打開(或連接到)它們:
const initializeDb = indexedDB.open( "name_of_database", version );
indexedDb.open() 方法中的 name_of_database 參數將用作正在創建的數據庫的名稱,而 version 參數是一個代表數據庫版本的數字。
在 IndexedDB 中,我們使用對象存儲來構建數據庫的結構,并且每當要更新數據庫結構時,都需要將版本升級到更高的值。這意味著,如果我們從版本 1 開始,則下次要更新數據庫的結構時,我們需要將 indexedDb.open() 方法中的版本更改為 2 或更高版本。
對象存儲類似于關系數據庫(如 PostgreSQL)中的表和文檔數據庫(如 MongoDB)中的集合。要在 IndexedDB 中創建對象存儲,我們需要從之前聲明的 initializeDb 變量中調用 onupgradeneeded() 方法:
initializeDb.onupgradeneeded = () => { const database = initializeDb.result; database.createObjectStore("name_of_object_store", { autoIncrement: true, }); };
在上面的代碼塊中,我們從 initializeDb.result 屬性獲取數據庫,然后使用其 createObjectStore() 方法創建對象存儲。第二個參數 {autoIncrement:true} 告訴 IndexedDB 自動提供/增加對象存儲中項目的 ID。
我省略了諸如事務和游標之類的其他術語,因為使用低級 IndexedDB API 需要進行大量工作。這就是為什么我們需要 Dexie.js,它是 IndexedDB 的簡約包裝。讓我們看看 Dexie 如何簡化創建數據庫,對象存儲,存儲數據以及從數據庫查詢數據的整個過程。
使用 Dexie,創建 IndexedDB 數據庫和對象存儲非常容易:
const db = new Dexie("exampleDatabase"); db.version(1).stores({ name_of_object_store: "++id, name, price", name_of_another_object_store: "++id, title", });
在上面的代碼塊中,我們創建了一個名為 exampleDatabase 的新數據庫,并將其作為值分配給 db 變量。我們使用 db.version(version_number).stores() 方法為數據庫創建對象存儲。每個對象存儲的值代表了它的結構。例如,當在第一個對象存儲中存儲數據時,我們需要提供一個具有屬性 name 和 price 的對象。++id 選項的作用就像我們在創建對象存儲區時使用的 {autoIncrement:true} 參數一樣。
請注意,在我們的應用程序中使用 dexie 包之前,我們需要安裝并導入它。當我們開始構建我們的演示項目時,我們將看到如何做到這一點。
對于我們的演示項目,我們將使用 Dexie.js 和 React 構建一個市場列表應用程序。我們的用戶將能夠在市場列表中添加他們打算購買的商品,刪除這些商品或將其標記為已購買。
我們將看到如何使用 Dexie useLiveQuery hook 來監視 IndexedDB 數據庫中的更改以及在數據庫更新時重新呈現 React 組件。這是我們的應用程序的外觀:
首先,我們將使用為應用程序的結構和設計創建的 GitHub 模板。這里有一個模板的鏈接。點擊**Use this template(使用此模板)**按鈕,就會用現有的模板為你創建一個新的資源庫,然后你就可以克隆和使用這個模板。
或者,在計算機上安裝了GitHub CLI的情況下,您可以運行以下命令從市場列表 GitHub 模板創建名為 market-list-app 的存儲庫:
gh repo create market-list-app --template ebenezerdon/market-list-template
完成此操作后,您可以繼續在代碼編輯器中克隆并打開您的新應用程序。使用終端在應用程序目錄中運行以下命令應安裝 npm 依賴項并啟動新應用程序:
npm install && npm start
導航到成功消息中的本地 URL(通常為http://localhost:3000)時,您應該能夠看到新的React應用程序。您的新應用應如下所示:
當您打開 ./src/App.js 文件時,您會注意到我們的應用程序組件僅包含市場列表應用程序的 JSX 代碼。我們正在使用Materialize 框架中的類進行樣式設置,并將其 CDN 鏈接包含在 ./public/index.html 文件中。接下來,我們將看到如何使用 Dexie 創建和管理數據。
要在我們的 React 應用程序中使用 Dexie.js 進行離線存儲,我們將從在終端中運行以下命令開始,以安裝 dexie 和 dexie-react-hooks 軟件包:
npm i -s dexie dexie-react-hooks
我們將使用 dexie-react-hooks 包中的 useLiveQuery hook 來監視更改,并在對 IndexedDB 數據庫進行更新時重新渲染我們的 React 組件。
讓我們將以下導入語句添加到我們的 ./src/App.js 文件中。這將導入 Dexie 和 useLiveQuery hook:
import Dexie from "dexie"; import { useLiveQuery } from "dexie-react-hooks";
接下來,我們將創建一個名為 MarketList 的新數據庫,然后聲明我們的對象存儲 items:
const db = new Dexie("MarketList"); db.version(1).stores({ items: "++id,name,price,itemHasBeenPurchased", });
我們的 items 對象存儲將期待一個具有屬性name、price和 itemHasBeenPurchased 的對象,而 id 將由 Dexie 提供。在將新數據添加到對象存儲中時,我們將為 itemHasBeenPurchased 屬性使用默認布爾值 false,然后在我們從市場清單中購買商品時將其更新為 true。
讓我們創建一個變量來存儲我們所有的項目。我們將使用 useLiveQuery 鉤子從 items 對象存儲中獲取數據,并觀察其中的變化,這樣當 items 對象存儲有更新時,我們的 allItems 變量將被更新,我們的組件將用新的數據重新渲染。我們將在 App 組件內部進行:
const App = () => { const allItems = useLiveQuery(() => db.items.toArray(), []); if (!allItems) return null ... }
在上面的代碼塊中,我們創建了一個名為 allItems 的變量,并將 useLiveQuery 鉤子作為其值。useLiveQuery 鉤子的語法類似于 React 的 useEffect 鉤子,它期望一個函數及其依賴項數組作為參數。我們的函數參數返回數據庫查詢。
在這里,我們以數組格式獲取 items 對象存儲中的所有數據。在下一行中,我們使用一個條件來告訴我們的組件,如果 allItems 變量是 undefined,則意味著查詢仍在加載中。
仍在 App 組件中,讓我們創建一個名為 addItemToDb 的函數,我們將使用該函數向數據庫中添加項目。每當我們點擊“ADD ITEM(添加項目)”按鈕時,我們都會調用此函數。請記住,每次更新數據庫時,我們的組件都會重新渲染。
... const addItemToDb = async event => { event.preventDefault() const name = document.querySelector('.item-name').value const price = document.querySelector('.item-price').value await db.items.add({ name, price: Number(price), itemHasBeenPurchased: false }) } ...
在 addItemToDb 函數中,我們從表單輸入字段中獲取商品名稱和價格值,然后使用 db.[name_of_object_store].add 方法將新商品數據添加到商品對象存儲中。我們還將 itemHasBeenPurchased 屬性的默認值設置為 false。
現在我們有了 addItemToDb 函數,讓我們創建一個名為 removeItemFromDb 的函數以從我們的商品對象存儲中刪除數據:
... const removeItemFromDb = async id => { await db.items.delete(id) } ...
接下來,我們將創建一個名為 markAsPurchased 的函數,用于將商品標記為已購買。我們的函數在調用時,會將物品的主鍵作為第一個參數——在本例中是 id,它將使用這個主鍵來查詢我們想要標記為購買的物品的數據庫。取得商品后,它將其 markAsPurchased 屬性更新為 true:
... const markAsPurchased = async (id, event) => { if (event.target.checked) { await db.items.update(id, {itemHasBeenPurchased: true}) } else { await db.items.update(id, {itemHasBeenPurchased: false}) } } ...
在 markAsPurchased 函數中,我們使用 event 參數來獲取用戶單擊的特定輸入元素。如果選中其值,我們將itemHasBeenPurchased 屬性更新為 true,否則更新為 false。db.[name_of_object_store] .update() 方法期望該項目的主鍵作為其第一個參數,而新對象數據作為其第二個參數。
下面是我們的 App 組件在這個階段應該是什么樣子。
... const App = () => { const allItems = useLiveQuery(() => db.items.toArray(), []); if (!allItems) return null const addItemToDb = async event => { event.preventDefault() const name = document.querySelector('.item-name').value const price = document.querySelector('.item-price').value await db.items.add({ name, price, itemHasBeenPurchased: false }) } const removeItemFromDb = async id => { await db.items.delete(id) } const markAsPurchased = async (id, event) => { if (event.target.checked) { await db.items.update(id, {itemHasBeenPurchased: true}) } else { await db.items.update(id, {itemHasBeenPurchased: false}) } } ... }
現在,我們創建一個名為 itemData 的變量,以容納我們所有商品數據的 JSX 代碼:
... const itemData = allItems.map(({ id, name, price, itemHasBeenPurchased }) => ( <div className="row" key={id}> <p className="col s5"> <label> <input type="checkbox" checked={itemHasBeenPurchased} onChange={event => markAsPurchased(id, event)} /> <span className="black-text">{name}</span> </label> </p> <p className="col s5">${price}</p> <i onClick={() => removeItemFromDb(id)} className="col s2 material-icons delete-button"> delete </i> </div> )) ...
在 itemData 變量中,我們映射了 allItems 數據數組中的所有項目,然后從每個 item 對象獲取屬性 id、name、price 和 itemHasBeenPurchased。然后,我們繼續進行操作,并用數據庫中的新動態值替換了以前的靜態數據。
注意,我們還使用了 markAsPurchased 和 removeItemFromDb 方法作為相應按鈕的單擊事件偵聽器。我們將在下一個代碼塊中將 addItemToDb 方法添加到表單的 onSubmit 事件中。
準備好 itemData 后,讓我們將 App 組件的 return 語句更新為以下 JSX 代碼:
... return ( <div className="container"> <h4 className="green-text center-align">Market List App</h4> <form className="add-item-form" onSubmit={event => addItemToDb(event)} > <input type="text" className="item-name" placeholder="Name of item" required/> <input type="number" step=".01" className="item-price" placeholder="Price in USD" required/> <button type="submit" className="waves-effect waves-light btn right">Add item</button> </form> {allItems.length > 0 && <div className="card white darken-1"> <div className="card-content"> <form action="#"> { itemData } </form> </div> </div> } </div> ) ...
在 return 語句中,我們已將 itemData 變量添加到我們的項目列表中(items list)。我們還使用 addItemToDb 方法作為 add-item-form 的 onsubmit 值。
為了測試我們的應用程序,我們可以返回到我們先前打開的 React 網頁。請記住,您的 React 應用必須正在運行,如果不是,請在終端上運行命令 npm start。您的應用應該能夠像下面的演示一樣運行:
我們還可以使用條件用 Dexie 查詢我們的 IndexedDB 數據庫。例如,如果我們要獲取價格高于 10 美元的所有商品,則可以執行以下操作:
const items = await db.friends .where("price") .above(10) .toArray();
您可以在Dexie 文檔中查看其他查詢方法。
在本文中,我們學習了如何使用 IndexedDB 進行離線存儲以及 Dexie.js 如何簡化該過程。我們還了解了如何使用 Dexie useLiveQuery 鉤子來監視更改并在每次更新數據庫時重新渲染 React 組件。
由于 IndexedDB 是瀏覽器原生的,從數據庫中查詢和檢索數據比每次需要在應用中處理數據時都要發送服務器端 API 請求要快得多,而且我們幾乎可以在 IndexedDB 數據庫中存儲任何東西。
過去使用 IndexedDB 可能對瀏覽器的支持是一個大問題,但是現在所有主流瀏覽器都支持它。在 Web 應用中使用 IndexedDB 進行離線存儲的諸多優勢大于劣勢,將 Dexie.js 與 IndexedDB 一起使用,使得 Web 開發變得前所未有的有趣。
感謝各位的閱讀,以上就是“如何在React應用中使用Dexie.js進行離線數據存儲”的內容了,經過本文的學習后,相信大家對如何在React應用中使用Dexie.js進行離線數據存儲這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。