<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在React應(yīng)用中使用Dexie.js進(jìn)行離線數(shù)據(jù)存儲(chǔ)

          共 9020字,需瀏覽 19分鐘

           ·

          2021-03-23 20:22

          原文:https://blog.logrocket.com/dexie-js-indexeddb-react-apps-offline-data-storage/
          作者:Ebenezer Don

          離線存儲(chǔ)應(yīng)用程序數(shù)據(jù)已成為現(xiàn)代 Web 開發(fā)中的必要條件。內(nèi)置的瀏覽器 localStorage 可以用作簡(jiǎn)單輕量數(shù)據(jù)的數(shù)據(jù)存儲(chǔ),但是在結(jié)構(gòu)化數(shù)據(jù)或存儲(chǔ)大量數(shù)據(jù)方面卻不足。

          最重要的是,我們只能將字符串?dāng)?shù)據(jù)存儲(chǔ)在受 XSS 攻擊的 localStorage 中,并且它沒有提供很多查詢數(shù)據(jù)的功能。

          這就是 IndexedDB 的亮點(diǎn)。使用 IndexedDB,我們可以在瀏覽器中創(chuàng)建結(jié)構(gòu)化的數(shù)據(jù)庫,將幾乎所有內(nèi)容存儲(chǔ)在這些數(shù)據(jù)庫中,并對(duì)數(shù)據(jù)執(zhí)行各種類型的查詢。

          在本文中,我們將了解 IndexedDB 的全部含義,以及如何使用 Dexie.js(用于 IndexedDB 的簡(jiǎn)約包裝)處理 Web 應(yīng)用程序中的離線數(shù)據(jù)存儲(chǔ)。

          IndexedDB 如何工作

          IndexedDB 是用于瀏覽器的內(nèi)置非關(guān)系數(shù)據(jù)庫。它使開發(fā)人員能夠?qū)?shù)據(jù)持久存儲(chǔ)在瀏覽器中,即使在脫機(jī)時(shí)也可以無縫使用 Web 應(yīng)用程序。使用 IndexedDB 時(shí),您會(huì)經(jīng)??吹絻蓚€(gè)術(shù)語:數(shù)據(jù)庫存儲(chǔ)和對(duì)象存儲(chǔ)。讓我們?cè)谙旅孢M(jìn)行探討。

          使用 IndexedDB 創(chuàng)建數(shù)據(jù)庫

          IndexedDB 數(shù)據(jù)庫對(duì)每個(gè) Web 應(yīng)用程序來說都是唯一的。這意味著一個(gè)應(yīng)用程序只能從與自己運(yùn)行在同一域或子域的 IndexedDB 數(shù)據(jù)庫中訪問數(shù)據(jù)。數(shù)據(jù)庫是容納對(duì)象存儲(chǔ)的地方,而對(duì)象存儲(chǔ)又包含存儲(chǔ)的數(shù)據(jù)。要使用 IndexedDB 數(shù)據(jù)庫,我們需要打開(或連接到)它們:

          const initializeDb = indexedDB.open(
          "name_of_database",
          version
          );

          indexedDb.open() 方法中的 name_of_database 參數(shù)將用作正在創(chuàng)建的數(shù)據(jù)庫的名稱,而 version 參數(shù)是一個(gè)代表數(shù)據(jù)庫版本的數(shù)字。

          在 IndexedDB 中,我們使用對(duì)象存儲(chǔ)來構(gòu)建數(shù)據(jù)庫的結(jié)構(gòu),并且每當(dāng)要更新數(shù)據(jù)庫結(jié)構(gòu)時(shí),都需要將版本升級(jí)到更高的值。這意味著,如果我們從版本 1 開始,則下次要更新數(shù)據(jù)庫的結(jié)構(gòu)時(shí),我們需要將 indexedDb.open() 方法中的版本更改為 2 或更高版本。

          使用 IndexedDB 創(chuàng)建對(duì)象存儲(chǔ)

          對(duì)象存儲(chǔ)類似于關(guān)系數(shù)據(jù)庫(如 PostgreSQL)中的表和文檔數(shù)據(jù)庫(如 MongoDB)中的集合。要在 IndexedDB 中創(chuàng)建對(duì)象存儲(chǔ),我們需要從之前聲明的 initializeDb 變量中調(diào)用 onupgradeneeded() 方法:

          initializeDb.onupgradeneeded = () => {
          const database = initializeDb.result;
          database.createObjectStore("name_of_object_store", {
          autoIncrement: true,
          });
          };

          在上面的代碼塊中,我們從 initializeDb.result 屬性獲取數(shù)據(jù)庫,然后使用其 createObjectStore() 方法創(chuàng)建對(duì)象存儲(chǔ)。第二個(gè)參數(shù) {autoIncrement:true} 告訴 IndexedDB 自動(dòng)提供/增加對(duì)象存儲(chǔ)中項(xiàng)目的 ID。

          我省略了諸如事務(wù)和游標(biāo)之類的其他術(shù)語,因?yàn)槭褂玫图?jí) IndexedDB API 需要進(jìn)行大量工作。這就是為什么我們需要 Dexie.js,它是 IndexedDB 的簡(jiǎn)約包裝。讓我們看看 Dexie 如何簡(jiǎn)化創(chuàng)建數(shù)據(jù)庫,對(duì)象存儲(chǔ),存儲(chǔ)數(shù)據(jù)以及從數(shù)據(jù)庫查詢數(shù)據(jù)的整個(gè)過程。

          使用 Dexie 離線存儲(chǔ)數(shù)據(jù)

          使用 Dexie,創(chuàng)建 IndexedDB 數(shù)據(jù)庫和對(duì)象存儲(chǔ)非常容易:

          const db = new Dexie("exampleDatabase");
          db.version(1).stores({
          name_of_object_store: "++id, name, price",
          name_of_another_object_store: "++id, title",
          });

          在上面的代碼塊中,我們創(chuàng)建了一個(gè)名為 exampleDatabase 的新數(shù)據(jù)庫,并將其作為值分配給 db 變量。我們使用 db.version(version_number).stores() 方法為數(shù)據(jù)庫創(chuàng)建對(duì)象存儲(chǔ)。每個(gè)對(duì)象存儲(chǔ)的值代表了它的結(jié)構(gòu)。例如,當(dāng)在第一個(gè)對(duì)象存儲(chǔ)中存儲(chǔ)數(shù)據(jù)時(shí),我們需要提供一個(gè)具有屬性 nameprice 的對(duì)象。++id 選項(xiàng)的作用就像我們?cè)趧?chuàng)建對(duì)象存儲(chǔ)區(qū)時(shí)使用的 {autoIncrement:true} 參數(shù)一樣。

          請(qǐng)注意,在我們的應(yīng)用程序中使用 dexie 包之前,我們需要安裝并導(dǎo)入它。當(dāng)我們開始構(gòu)建我們的演示項(xiàng)目時(shí),我們將看到如何做到這一點(diǎn)。

          我們將要建立的

          對(duì)于我們的演示項(xiàng)目,我們將使用 Dexie.js 和 React 構(gòu)建一個(gè)市場(chǎng)列表應(yīng)用程序。我們的用戶將能夠在市場(chǎng)列表中添加他們打算購買的商品,刪除這些商品或?qū)⑵錁?biāo)記為已購買。

          我們將看到如何使用 Dexie useLiveQuery hook 來監(jiān)視 IndexedDB 數(shù)據(jù)庫中的更改以及在數(shù)據(jù)庫更新時(shí)重新呈現(xiàn) React 組件。這是我們的應(yīng)用程序的外觀:

          設(shè)置我們的應(yīng)用

          首先,我們將使用為應(yīng)用程序的結(jié)構(gòu)和設(shè)計(jì)創(chuàng)建的 GitHub 模板。這里有一個(gè)模板的鏈接。點(diǎn)擊**Use this template(使用此模板)**按鈕,就會(huì)用現(xiàn)有的模板為你創(chuàng)建一個(gè)新的資源庫,然后你就可以克隆和使用這個(gè)模板。

          或者,在計(jì)算機(jī)上安裝了GitHub CLI的情況下,您可以運(yùn)行以下命令從市場(chǎng)列表 GitHub 模板創(chuàng)建名為 market-list-app 的存儲(chǔ)庫:

          gh repo create market-list-app --template ebenezerdon/market-list-template

          完成此操作后,您可以繼續(xù)在代碼編輯器中克隆并打開您的新應(yīng)用程序。使用終端在應(yīng)用程序目錄中運(yùn)行以下命令應(yīng)安裝 npm 依賴項(xiàng)并啟動(dòng)新應(yīng)用程序:

          npm install && npm start

          導(dǎo)航到成功消息中的本地 URL(通常為http://localhost:3000)時(shí),您應(yīng)該能夠看到新的React應(yīng)用程序。您的新應(yīng)用應(yīng)如下所示:

          當(dāng)您打開 ./src/App.js 文件時(shí),您會(huì)注意到我們的應(yīng)用程序組件僅包含市場(chǎng)列表應(yīng)用程序的 JSX 代碼。我們正在使用Materialize 框架中的類進(jìn)行樣式設(shè)置,并將其 CDN 鏈接包含在 ./public/index.html 文件中。接下來,我們將看到如何使用 Dexie 創(chuàng)建和管理數(shù)據(jù)。

          用 Dexie 創(chuàng)建我們的數(shù)據(jù)庫

          要在我們的 React 應(yīng)用程序中使用 Dexie.js 進(jìn)行離線存儲(chǔ),我們將從在終端中運(yùn)行以下命令開始,以安裝 dexiedexie-react-hooks 軟件包:

          npm i -s dexie dexie-react-hooks

          我們將使用 dexie-react-hooks 包中的 useLiveQuery hook 來監(jiān)視更改,并在對(duì) IndexedDB 數(shù)據(jù)庫進(jìn)行更新時(shí)重新渲染我們的 React 組件。

          讓我們將以下導(dǎo)入語句添加到我們的 ./src/App.js 文件中。這將導(dǎo)入 DexieuseLiveQuery hook:

          import Dexie from "dexie";
          import { useLiveQuery } from "dexie-react-hooks";

          接下來,我們將創(chuàng)建一個(gè)名為 MarketList 的新數(shù)據(jù)庫,然后聲明我們的對(duì)象存儲(chǔ) items

          const db = new Dexie("MarketList");
          db.version(1).stores({
          items: "++id,name,price,itemHasBeenPurchased",
          });

          我們的 items 對(duì)象存儲(chǔ)將期待一個(gè)具有屬性name、priceitemHasBeenPurchased 的對(duì)象,而 id 將由 Dexie 提供。在將新數(shù)據(jù)添加到對(duì)象存儲(chǔ)中時(shí),我們將為 itemHasBeenPurchased 屬性使用默認(rèn)布爾值 false,然后在我們從市場(chǎng)清單中購買商品時(shí)將其更新為 true

          Dexie React hook

          讓我們創(chuàng)建一個(gè)變量來存儲(chǔ)我們所有的項(xiàng)目。我們將使用 useLiveQuery 鉤子從 items 對(duì)象存儲(chǔ)中獲取數(shù)據(jù),并觀察其中的變化,這樣當(dāng) items 對(duì)象存儲(chǔ)有更新時(shí),我們的 allItems 變量將被更新,我們的組件將用新的數(shù)據(jù)重新渲染。我們將在 App 組件內(nèi)部進(jìn)行:

          const App = () => {
          const allItems = useLiveQuery(() => db.items.toArray(), []);
          if (!allItems) return null
          ...
          }

          在上面的代碼塊中,我們創(chuàng)建了一個(gè)名為 allItems 的變量,并將 useLiveQuery 鉤子作為其值。useLiveQuery 鉤子的語法類似于 React 的 useEffect 鉤子,它期望一個(gè)函數(shù)及其依賴項(xiàng)數(shù)組作為參數(shù)。我們的函數(shù)參數(shù)返回?cái)?shù)據(jù)庫查詢。

          在這里,我們以數(shù)組格式獲取 items 對(duì)象存儲(chǔ)中的所有數(shù)據(jù)。在下一行中,我們使用一個(gè)條件來告訴我們的組件,如果 allItems 變量是 undefined,則意味著查詢?nèi)栽诩虞d中。

          將 items 添加到我們的數(shù)據(jù)庫

          仍在 App 組件中,讓我們創(chuàng)建一個(gè)名為 addItemToDb 的函數(shù),我們將使用該函數(shù)向數(shù)據(jù)庫中添加項(xiàng)目。每當(dāng)我們點(diǎn)擊“ADD ITEM(添加項(xiàng)目)”按鈕時(shí),我們都會(huì)調(diào)用此函數(shù)。請(qǐng)記住,每次更新數(shù)據(jù)庫時(shí),我們的組件都會(huì)重新渲染。

          ...
          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 函數(shù)中,我們從表單輸入字段中獲取商品名稱和價(jià)格值,然后使用 db.[name_of_object_store].add 方法將新商品數(shù)據(jù)添加到商品對(duì)象存儲(chǔ)中。我們還將 itemHasBeenPurchased 屬性的默認(rèn)值設(shè)置為 false。

          從我們的數(shù)據(jù)庫中刪除 items

          現(xiàn)在我們有了 addItemToDb 函數(shù),讓我們創(chuàng)建一個(gè)名為 removeItemFromDb 的函數(shù)以從我們的商品對(duì)象存儲(chǔ)中刪除數(shù)據(jù):

          ...
          const removeItemFromDb = async id => {
          await db.items.delete(id)
          }
          ...

          更新我們數(shù)據(jù)庫中的項(xiàng)目

          接下來,我們將創(chuàng)建一個(gè)名為 markAsPurchased 的函數(shù),用于將商品標(biāo)記為已購買。我們的函數(shù)在調(diào)用時(shí),會(huì)將物品的主鍵作為第一個(gè)參數(shù)——在本例中是 id,它將使用這個(gè)主鍵來查詢我們想要標(biāo)記為購買的物品的數(shù)據(jù)庫。取得商品后,它將其 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 函數(shù)中,我們使用 event 參數(shù)來獲取用戶單擊的特定輸入元素。如果選中其值,我們將itemHasBeenPurchased 屬性更新為 true,否則更新為 false。db.[name_of_object_store] .update() 方法期望該項(xiàng)目的主鍵作為其第一個(gè)參數(shù),而新對(duì)象數(shù)據(jù)作為其第二個(gè)參數(shù)。

          下面是我們的 App 組件在這個(gè)階段應(yīng)該是什么樣子。

          ...
          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})
          }
          }
          ...
          }

          現(xiàn)在,我們創(chuàng)建一個(gè)名為 itemData 的變量,以容納我們所有商品數(shù)據(jù)的 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 數(shù)據(jù)數(shù)組中的所有項(xiàng)目,然后從每個(gè) item 對(duì)象獲取屬性 id、namepriceitemHasBeenPurchased。然后,我們繼續(xù)進(jìn)行操作,并用數(shù)據(jù)庫中的新動(dòng)態(tài)值替換了以前的靜態(tài)數(shù)據(jù)。

          注意,我們還使用了 markAsPurchasedremoveItemFromDb 方法作為相應(yīng)按鈕的單擊事件偵聽器。我們將在下一個(gè)代碼塊中將 addItemToDb 方法添加到表單的 onSubmit 事件中。

          準(zhǔn)備好 itemData 后,讓我們將 App 組件的 return 語句更新為以下 JSX 代碼:

          ...
          return (
          <div className="container">
          <h3 className="green-text center-align">Market List App</h3>
          <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 變量添加到我們的項(xiàng)目列表中(items list)。我們還使用 addItemToDb 方法作為 add-item-formonsubmit 值。

          為了測(cè)試我們的應(yīng)用程序,我們可以返回到我們先前打開的 React 網(wǎng)頁。請(qǐng)記住,您的 React 應(yīng)用必須正在運(yùn)行,如果不是,請(qǐng)?jiān)诮K端上運(yùn)行命令 npm start。您的應(yīng)用應(yīng)該能夠像下面的演示一樣運(yùn)行:

          我們還可以使用條件用 Dexie 查詢我們的 IndexedDB 數(shù)據(jù)庫。例如,如果我們要獲取價(jià)格高于 10 美元的所有商品,則可以執(zhí)行以下操作:

          const items = await db.friends
          .where("price")
          .above(10)
          .toArray();

          您可以在Dexie 文檔中查看其他查詢方法。

          結(jié)束語和源碼

          在本文中,我們學(xué)習(xí)了如何使用 IndexedDB 進(jìn)行離線存儲(chǔ)以及 Dexie.js 如何簡(jiǎn)化該過程。我們還了解了如何使用 Dexie useLiveQuery 鉤子來監(jiān)視更改并在每次更新數(shù)據(jù)庫時(shí)重新渲染 React 組件。

          由于 IndexedDB 是瀏覽器原生的,從數(shù)據(jù)庫中查詢和檢索數(shù)據(jù)比每次需要在應(yīng)用中處理數(shù)據(jù)時(shí)都要發(fā)送服務(wù)器端 API 請(qǐng)求要快得多,而且我們幾乎可以在 IndexedDB 數(shù)據(jù)庫中存儲(chǔ)任何東西。

          過去使用 IndexedDB 可能對(duì)瀏覽器的支持是一個(gè)大問題,但是現(xiàn)在所有主流瀏覽器都支持它。在 Web 應(yīng)用中使用 IndexedDB 進(jìn)行離線存儲(chǔ)的諸多優(yōu)勢(shì)大于劣勢(shì),將 Dexie.js 與 IndexedDB 一起使用,使得 Web 開發(fā)變得前所未有的有趣。

          這是我們的演示應(yīng)用程序的GitHub庫的鏈接。


          訪問本文鏈接請(qǐng)點(diǎn)擊閱讀原文

          粉絲福利

          極客時(shí)間專欄《Web協(xié)議詳解與抓包實(shí)戰(zhàn)121講》獲取資源請(qǐng)?jiān)诠娞?hào)對(duì)話框中回復(fù)關(guān)鍵字:JK02,如果沒有關(guān)注請(qǐng)掃下面的二維碼。更多福利資料請(qǐng)查看公眾號(hào)菜單

          改善程序性能和代碼質(zhì)量:通過代理模式組合HTTP請(qǐng)求

          新老手必備的34種JavaScript簡(jiǎn)寫優(yōu)化技術(shù)

          中文布局CSS庫—chinese-layout

          最近文章


          - END -

          點(diǎn)贊 + 在看 + 留言,下一個(gè)幸運(yùn)兒就是你!
          走心的分享更容易被抽中~

          開獎(jiǎng)時(shí)間 下期文末

          瀏覽 85
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产视频一区二区四区 | 黄色视频网站在线免费观看 | 天天操天| 高清无码视频免费 | 网站自拍视频网站在线看 |