离线缓存的核心是通过service worker结合cache api实现,1. 首先在主线程注册service worker;2. 在sw.js中监听install事件预缓存关键资源;3. 在activate事件中清理旧缓存版本;4. 在fetch事件中采用“缓存优先,网络回退”等策略响应请求;5. 可借助workbox库简化开发,提升缓存管理的可靠性与效率,最终实现极速加载、网络韧性、流量节省和类原生app体验,显著提升用户在弱网或离线环境下的使用满意度。

离线缓存,简单来说,就是把网站的资源(比如HTML、CSS、JavaScript文件,图片,甚至是API数据)储存在用户的浏览器本地,这样即使没有网络连接,网站也能正常访问或至少提供基础功能。而Cache API,就是我们前端开发者用来程序化地管理这些本地存储资源的工具,它让这种离线能力成为可能。
要实现离线缓存,核心在于Service Worker和Cache API的配合。Service Worker是一个独立于主线程的JavaScript文件,它能拦截网络请求并决定如何响应。Cache API则提供了存储和检索这些网络请求响应的能力。
首先,你需要在主线程中注册Service Worker:
// 在你的主页面(例如index.html)中
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
});
}然后,在
sw.js
// sw.js
const CACHE_NAME = 'my-site-cache-v1'; // 缓存名称,用于版本控制
const urlsToCache = [
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
// 安装事件:Service Worker首次安装时触发,通常用于预缓存核心资源
self.addEventListener('install', (event) => {
console.log('Service Worker installing...');
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(urlsToCache); // 将指定资源添加到缓存
})
.catch(error => {
console.error('Failed to cache during install:', error);
})
);
});
// 激活事件:Service Worker安装成功并激活时触发,通常用于清理旧缓存
self.addEventListener('activate', (event) => {
console.log('Service Worker activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName); // 删除旧版本的缓存
}
return null;
})
);
})
);
});
// 抓取事件:Service Worker拦截网络请求时触发
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request) // 尝试从缓存中匹配请求
.then((response) => {
// 如果缓存中有匹配的响应,直接返回
if (response) {
console.log('Serving from cache:', event.request.url);
return response;
}
// 否则,发起网络请求
console.log('Fetching from network:', event.request.url);
return fetch(event.request).then((networkResponse) => {
// 检查响应是否有效,例如HTTP状态码200
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
// 克隆响应,因为响应流只能被消费一次
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache); // 将新的网络响应存入缓存
});
return networkResponse;
});
})
.catch(error => {
console.error('Fetch failed:', error);
// 可以在这里返回一个离线页面
// return caches.match('/offline.html');
})
);
});这个例子展示了一个“缓存优先,然后回退到网络”的策略。当用户请求一个资源时,Service Worker会先尝试从
CACHE_NAME
我觉得离线缓存不仅仅是一个技术上的“炫技”,它直接关乎到用户对一个网站或应用的信任和依赖。试想一下,你在通勤路上,网络信号时断时续,或者干脆没有,如果你的网页应用还能正常加载,甚至执行一些操作,那简直是体验上的巨大飞跃。这就像你手机里的原生App,无论有没有网,它总能给你一些反馈,而不是一个白屏或者错误提示。
具体来说,它能带来几个显而易见的优势:
我个人觉得,一个连最基本的离线能力都没有的Web应用,在当今移动优先的时代,多少是有点“不负责任”的。用户对数字产品的期望值越来越高,我们作为开发者,有责任提供更稳定、更可靠的服务。
说实话,虽然Cache API和Service Worker的概念听起来很美好,但实际落地的时候,坑还是不少的。最让我头疼的,永远是缓存失效(Cache Invalidation)的问题。你缓存了资源,但当这些资源在服务器端更新了,如何确保用户能及时获取到最新版本,而不是一直用旧的缓存?
my-site-cache-v1
v2
v3
activate
app.1a2b3c.js
Application
Service Workers
Cache Storage
此外,缓存容量限制也是一个需要考虑的因素。虽然浏览器通常会提供相当大的缓存空间(几百MB甚至更多),但它并非无限,而且不同浏览器有不同的策略。你需要合理规划缓存内容,避免缓存大量不必要或过大的资源。对于一些可能很快失效的动态数据,要谨慎缓存,或者设置合适的过期策略。
Cache API提供了灵活的控制能力,这也就意味着你需要根据不同类型的内容和应用场景,选择最合适的缓存策略。没有万能的“最佳实践”,只有最适合你需求的方案。
缓存优先,网络回退 (Cache First, then Network): 这是最常见的策略,也是上面示例中使用的。Service Worker会首先尝试从缓存中获取资源。如果成功,立即返回;如果失败(缓存中没有),则发起网络请求,并将网络响应存入缓存以备下次使用。
网络优先,缓存回退 (Network First, then Cache): Service Worker会首先尝试从网络获取资源。如果网络请求成功,返回网络响应,并更新缓存;如果网络请求失败(例如离线),则回退到缓存中获取资源。
仅缓存 (Cache Only): Service Worker只从缓存中获取资源,不进行任何网络请求。
仅网络 (Network Only): Service Worker不使用缓存,直接发起网络请求。
陈旧时重新验证 (Stale-While-Revalidate): Service Worker立即从缓存中返回资源(“陈旧”),同时在后台发起网络请求获取最新版本。网络请求成功后,更新缓存以供下次使用。
在实际项目中,你可能会发现你需要混合使用这些策略。例如,你的HTML、CSS、JS文件可以使用“缓存优先”,API数据可以使用“网络优先”或“陈旧时重新验证”,而一些不常变动的图片则可以使用“仅缓存”。
如果你觉得直接操作Cache API和Service Worker的事件监听器过于繁琐,或者担心自己写出bug,那么我强烈推荐使用像Workbox这样的库。Workbox是Google Chrome团队开发的一套Service Worker工具集,它封装了Cache API的复杂性,提供了高级的缓存策略、路由匹配、预缓存等功能,能极大地简化Service Worker的开发和维护。它就像一个贴心的管家,帮你把这些复杂逻辑都打理得井井有条,让你可以更专注于业务逻辑。
// 使用Workbox的例子 (在sw.js中)
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst, NetworkFirst } from 'workbox-strategies';
import { precacheAndRoute } from 'workbox-precaching';
// 预缓存通过构建工具注入的资源列表
precacheAndRoute(self.__WB_MANIFEST || []);
// 缓存图片:缓存优先,但设置过期时间
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
// workbox-expiration插件,设置缓存图片的最大数量和过期时间
new workbox.expiration.ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30天
}),
],
})
);
// 缓存CSS和JS文件:缓存优先
registerRoute(
({ request }) => request.destination === 'script' || request.destination === 'style',
new CacheFirst({
cacheName: 'static-assets',
})
);
// 缓存API请求:陈旧时重新验证
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({
cacheName: 'api-data',
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 20,
maxAgeSeconds: 60 * 60, // 1小时
}),
],
})
);
// 默认情况下,所有未匹配的请求都走网络优先
registerRoute(
({ request }) => request.mode === 'navigate',
new NetworkFirst({
cacheName: 'pages',
})
);Workbox让Service Worker的开发变得直观而高效,它抽象了底层的复杂性,让你能够用更声明式的方式定义缓存行为。我个人觉得,对于大多数现代Web应用而言,直接上手Workbox比从零开始写Cache API的逻辑要划算得多,它能帮你规避很多潜在的错误,并提供更健壮的离线能力。
以上就是什么是离线缓存?Cache API的使用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号