在 PWA 中,数据缓存是一个非常重要的问题。对于一些离线应用场景,离线数据缓存可以保证应用的正常运行。而 IndexedDB 是一种浏览器自带的数据库,可以在客户端存储大量数据,是实现 PWA 离线数据缓存的好选择。
IndexedDB 简介
IndexedDB 是浏览器自带的数据库,可以在客户端存储大量数据。IndexedDB 使用对象存储来存储数据,每个存储对象都有一个键值。IndexedDB 是异步的,使用回调函数来处理操作结果。
IndexedDB 的主要对象有:
- indexedDB:IndexedDB 的全局对象。
- IDBDatabase:IndexedDB 数据库对象。
- IDBObjectStore:存储对象的对象。
- IDBTransaction:事务对象,用于控制数据库的读写操作。
使用 IndexedDB 实现离线数据缓存
IndexedDB 可以用于实现离线数据缓存。在 PWA 中,我们可以在 service worker 中使用 IndexedDB 存储数据。
创建数据库
首先,我们需要创建一个 IndexedDB 数据库。可以使用以下代码:
// javascriptcn.com 代码示例 const dbName = 'my-db'; const dbVersion = 1; const request = indexedDB.open(dbName, dbVersion); request.onerror = (event) => { console.error('Failed to open database:', event.target.error); }; request.onsuccess = (event) => { const db = event.target.result; console.log('Database opened successfully:', db); };
以上代码创建了一个名为 my-db
的数据库,并打开了该数据库。如果数据库已经存在,则直接打开该数据库。否则,会创建一个新的数据库。
创建对象存储
在 IndexedDB 中,我们需要创建一个对象存储来存储数据。可以使用以下代码:
// javascriptcn.com 代码示例 const dbName = 'my-db'; const dbVersion = 1; const request = indexedDB.open(dbName, dbVersion); request.onerror = (event) => { console.error('Failed to open database:', event.target.error); }; request.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore('my-object-store', { keyPath: 'id' }); console.log('Object store created successfully:', objectStore); }; request.onsuccess = (event) => { const db = event.target.result; console.log('Database opened successfully:', db); };
以上代码创建了一个名为 my-object-store
的对象存储,并指定了 id
为主键。如果该对象存储已经存在,则直接打开该对象存储。否则,会创建一个新的对象存储。
存储数据
在 IndexedDB 中,可以使用以下代码存储数据:
// javascriptcn.com 代码示例 const dbName = 'my-db'; const dbVersion = 1; const request = indexedDB.open(dbName, dbVersion); request.onerror = (event) => { console.error('Failed to open database:', event.target.error); }; request.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore('my-object-store', { keyPath: 'id' }); console.log('Object store created successfully:', objectStore); }; request.onsuccess = (event) => { const db = event.target.result; const transaction = db.transaction(['my-object-store'], 'readwrite'); const objectStore = transaction.objectStore('my-object-store'); const data = { id: 1, name: 'John Doe' }; const request = objectStore.put(data); request.onerror = (event) => { console.error('Failed to store data:', event.target.error); }; request.onsuccess = (event) => { console.log('Data stored successfully:', data); }; };
以上代码使用了事务来存储数据。首先,我们需要使用 db.transaction
方法创建一个事务。然后,使用 transaction.objectStore
方法获取对象存储。最后,使用 objectStore.put
方法存储数据。
获取数据
在 IndexedDB 中,可以使用以下代码获取数据:
// javascriptcn.com 代码示例 const dbName = 'my-db'; const dbVersion = 1; const request = indexedDB.open(dbName, dbVersion); request.onerror = (event) => { console.error('Failed to open database:', event.target.error); }; request.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore('my-object-store', { keyPath: 'id' }); console.log('Object store created successfully:', objectStore); }; request.onsuccess = (event) => { const db = event.target.result; const transaction = db.transaction(['my-object-store'], 'readonly'); const objectStore = transaction.objectStore('my-object-store'); const request = objectStore.get(1); request.onerror = (event) => { console.error('Failed to get data:', event.target.error); }; request.onsuccess = (event) => { const data = event.target.result; console.log('Data retrieved successfully:', data); }; };
以上代码使用了事务来获取数据。首先,我们需要使用 db.transaction
方法创建一个事务。然后,使用 transaction.objectStore
方法获取对象存储。最后,使用 objectStore.get
方法获取数据。
删除数据
在 IndexedDB 中,可以使用以下代码删除数据:
// javascriptcn.com 代码示例 const dbName = 'my-db'; const dbVersion = 1; const request = indexedDB.open(dbName, dbVersion); request.onerror = (event) => { console.error('Failed to open database:', event.target.error); }; request.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore('my-object-store', { keyPath: 'id' }); console.log('Object store created successfully:', objectStore); }; request.onsuccess = (event) => { const db = event.target.result; const transaction = db.transaction(['my-object-store'], 'readwrite'); const objectStore = transaction.objectStore('my-object-store'); const request = objectStore.delete(1); request.onerror = (event) => { console.error('Failed to delete data:', event.target.error); }; request.onsuccess = (event) => { console.log('Data deleted successfully:', event.target.result); }; };
以上代码使用了事务来删除数据。首先,我们需要使用 db.transaction
方法创建一个事务。然后,使用 transaction.objectStore
方法获取对象存储。最后,使用 objectStore.delete
方法删除数据。
示例代码
以下是一个完整的示例代码,可以在 service worker 中使用 IndexedDB 存储数据:
// javascriptcn.com 代码示例 const dbName = 'my-db'; const dbVersion = 1; const openDatabase = () => { return new Promise((resolve, reject) => { const request = indexedDB.open(dbName, dbVersion); request.onerror = (event) => { console.error('Failed to open database:', event.target.error); reject(event.target.error); }; request.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore('my-object-store', { keyPath: 'id' }); console.log('Object store created successfully:', objectStore); }; request.onsuccess = (event) => { const db = event.target.result; console.log('Database opened successfully:', db); resolve(db); }; }); }; const storeData = (db, data) => { return new Promise((resolve, reject) => { const transaction = db.transaction(['my-object-store'], 'readwrite'); const objectStore = transaction.objectStore('my-object-store'); const request = objectStore.put(data); request.onerror = (event) => { console.error('Failed to store data:', event.target.error); reject(event.target.error); }; request.onsuccess = (event) => { console.log('Data stored successfully:', data); resolve(); }; }); }; const getData = (db, id) => { return new Promise((resolve, reject) => { const transaction = db.transaction(['my-object-store'], 'readonly'); const objectStore = transaction.objectStore('my-object-store'); const request = objectStore.get(id); request.onerror = (event) => { console.error('Failed to get data:', event.target.error); reject(event.target.error); }; request.onsuccess = (event) => { const data = event.target.result; console.log('Data retrieved successfully:', data); resolve(data); }; }); }; const deleteData = (db, id) => { return new Promise((resolve, reject) => { const transaction = db.transaction(['my-object-store'], 'readwrite'); const objectStore = transaction.objectStore('my-object-store'); const request = objectStore.delete(id); request.onerror = (event) => { console.error('Failed to delete data:', event.target.error); reject(event.target.error); }; request.onsuccess = (event) => { console.log('Data deleted successfully:', event.target.result); resolve(); }; }); }; self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); if (url.origin !== location.origin) { return; } if (event.request.method !== 'GET') { return; } event.respondWith( caches.match(event.request) .then((cachedResponse) => { if (cachedResponse) { return cachedResponse; } return openDatabase() .then((db) => { return getData(db, url.pathname); }) .then((data) => { const response = new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, }); caches.open('my-cache') .then((cache) => { cache.put(event.request, response); }); return response; }) .catch(() => { return fetch(event.request); }); }) ); }); self.addEventListener('sync', (event) => { if (event.tag === 'sync-data') { event.waitUntil( openDatabase() .then((db) => { return getData(db, 1); }) .then((data) => { return fetch('/api/data', { method: 'POST', body: JSON.stringify(data), }); }) .then(() => { return openDatabase() .then((db) => { return deleteData(db, 1); }); }) ); } }); self.addEventListener('install', (event) => { event.waitUntil( caches.open('my-cache') .then((cache) => { return cache.addAll(['/index.html', '/app.js']); }) ); });
以上代码实现了一个简单的离线数据缓存解决方案。在 fetch
事件中,先判断缓存中是否有数据,如果有则返回缓存中的数据,否则从 IndexedDB 中获取数据,并将数据存储到缓存中。在 sync
事件中,将数据同步到后端。在 install
事件中,将应用的静态资源缓存到浏览器中。
总结
使用 IndexedDB 实现 PWA 离线数据缓存解决方案,可以保证应用在离线状态下的正常运行。IndexedDB 是一种浏览器自带的数据库,可以在客户端存储大量数据。在 PWA 中,我们可以在 service worker 中使用 IndexedDB 存储数据,并通过缓存机制实现离线数据缓存。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/655d6698d2f5e1655d7a921e