Progressive Web Apps (PWA) 使用离线存储策略来提供离线体验,即使在网络不可用时也能访问部分内容。两种主要的离线存储技术是IndexedDB和Cache API(也称为Service Worker Cache)。它们各有特点,适用于不同的场景。
IndexedDB
IndexedDB 是一种键值对存储,适合存储大量结构化数据,如数据库记录、用户设置或较大的文件。它可以进行复杂的查询,并支持事务操作。以下是一个简单的IndexedDB创建、存储和读取数据的例子:
// 打开或创建数据库
let dbPromise = indexedDB.open("myDatabase", 1);
// 当数据库打开成功时
dbPromise.then(function(db) {
// 创建一个对象存储
let objectStore = db.createObjectStore("myStore", { keyPath: "id" });
// 插入数据
objectStore.add({ id: 1, name: "Alice" });
// 查询数据
let transaction = db.transaction(["myStore"], "readonly");
let store = transaction.objectStore("myStore");
let request = store.get(1);
request.onsuccess = function(event) {
console.log("Retrieved data:", event.target.result);
};
});
Cache API
Cache API 是Service Worker的一部分,用于缓存网络请求和响应,通常用于静态资源的离线存储。它提供了一种简单的方式来存储和检索URL对应的资源。以下是如何使用Cache API缓存网页资源的示例:
// 在service worker中安装阶段缓存资源
self.addEventListener("install", async event => {
event.waitUntil(
caches.open("myCache").then(cache => {
return cache.addAll([
"/index.html",
"/styles.css",
"/scripts.js"
]);
})
);
});
// 在fetch事件中,尝试从缓存中获取资源
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request).then(response => {
// 如果缓存中有资源,返回缓存的响应
if (response) {
return response;
} else {
// 否则,尝试从网络获取资源,并将新资源添加到缓存
return fetch(event.request).then(networkResponse => {
caches.open("myCache").then(cache => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
}).catch(() => {
// 网络失败时返回缓存的默认响应
return caches.match("/offline.html");
});
}
})
);
});
在上面的代码中:
install
事件用于初始化缓存,将指定的URL列表添加到缓存中。
fetch
事件监听网络请求,优先尝试从缓存中获取资源。如果缓存中没有,就从网络请求,成功后将新资源存入缓存。如果网络请求失败,返回一个备用的离线页面。
结合使用IndexedDB和Cache API,PWA可以提供离线存储用户数据和静态资源,确保在离线状态下仍然有良好的用户体验。
在实际的PWA应用中,通常会结合使用IndexedDB和Cache API,以实现最佳的离线体验。例如,IndexedDB用于存储用户数据,而Cache API用于存储静态资源。
self.addEventListener("install", async event => {
event.waitUntil(
Promise.all([
caches.open("staticCache").then(cache => {
return cache.addAll([
"/index.html",
"/styles.css",
"/scripts.js"
]);
}),
// 假设有一个API获取用户数据
fetch("/api/user").then(response => {
return response.json().then(data => {
return indexedDB.open("userData", 1).then(db => {
let transaction = db.transaction(["userData"], "readwrite");
let store = transaction.objectStore("userData");
store.put(data);
});
});
})
])
);
});
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request).then(response => {
if (response) {
return response;
}
// 尝试从网络获取资源
return fetch(event.request).then(networkResponse => {
caches.open("staticCache").then(cache => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
}).catch(() => {
// 网络失败时,尝试从IndexedDB获取用户数据
if (event.request.url.endsWith("/api/user")) {
return indexedDB.open("userData", 1).then(db => {
let transaction = db.transaction(["userData"], "readonly");
let store = transaction.objectStore("userData");
let request = store.get(1);
return request.onsuccess ? request.onsuccess.event.target.result : null;
});
} else {
return caches.match("/offline.html");
}
});
})
);
});
-
在
install
事件中,同时缓存静态资源并从API获取用户数据,然后将数据存储在IndexedDB中。 -
在
fetch
事件中,优先从缓存中获取资源。如果缓存中没有,尝试从网络获取。对于API请求,如果网络请求失败,尝试从IndexedDB中获取用户数据。对于其他资源,返回离线页面。