HTML5 离线存储(通常指 Application Cache)是早期用于实现 Web 应用离线访问的技术,但由于其设计缺陷已被废弃。现代 Web 开发中,取而代之的是更强大的 Service Worker + Cache API 方案(属于 Progressive Web Apps 技术栈)。下面详细解析两者:
一、已被废弃的 Application Cache (AppCache)
1. 基本原理
- 通过一个
manifest
清单文件(如app.manifest
)声明需要缓存的资源。 - 浏览器根据清单下载资源并存储在离线缓存中。
2. 使用步骤
<!-- 在 html 标签中声明 manifest 文件 -->
<html manifest="app.manifest">
app.manifest
文件示例:
CACHE MANIFEST
# v1.0.0 → 版本号(更新缓存需修改此号)
CACHE: # 需要缓存的资源
/css/style.css
/js/app.js
/images/logo.png
/index.html
NETWORK: # 必须在线访问的资源(白名单)
/api/
/login
FALLBACK: # 离线时替代方案
/offline.html # 所有失败请求的兜底页面
/images/ /images/offline.png # 特定路径的替代
3. 主要问题(被废弃原因)
- 更新机制不透明:必须修改 manifest 文件(如版本号)才能触发更新。
- 白名单机制死板:
NETWORK
和FALLBACK
规则难以维护。 - 缓存污染风险:一旦缓存失败,可能导致整个应用无法使用。
- 无细粒度控制:无法编程式管理缓存。
- 并发问题:多标签页同时更新可能冲突。
⚠️ 现代浏览器已移除支持(Chrome 70+、Firefox 46+ 等弃用)。
二、现代方案:Service Worker + Cache API
核心优势
- 完全可编程:通过 JavaScript 精细控制缓存逻辑。
- 后台运行:独立于主线程,不阻塞页面渲染。
- 拦截网络请求:可自定义缓存策略(如网络优先/缓存优先)。
1. 实现步骤
(1) 注册 Service Worker
<!-- 在页面中注册 -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW 注册成功'))
.catch(err => console.error('SW 注册失败', err));
}
</script>
(2) 编写 Service Worker 脚本 (sw.js
)
// 定义缓存名称(版本更新时修改此名)
const CACHE_NAME = 'my-app-v1';
// 需要预缓存的资源
const PRE_CACHE = [
'/',
'/index.html',
'/css/main.css',
'/js/app.js',
'/images/hero.jpg'
];
// 安装阶段:预缓存关键资源
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(PRE_CACHE))
);
});
// 激活阶段:清理旧缓存
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(name => {
if (name !== CACHE_NAME) return caches.delete(name);
})
);
})
);
});
// 拦截请求:自定义缓存策略
self.addEventListener('fetch', event => {
event.respondWith(
// 策略1:缓存优先(适用于静态资源)
caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) return cachedResponse;
// 策略2:网络请求并缓存(适用于动态内容)
return fetch(event.request)
.then(response => {
// 只缓存成功响应
if (!response || response.status !== 200) return response;
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => cache.put(event.request, responseToCache));
return response;
});
})
);
});
2. 关键特性
功能 | 实现方式 |
---|---|
预缓存 | install 事件中使用 cache.addAll() |
动态缓存 | fetch 事件中通过 cache.put() 缓存网络响应 |
缓存策略 | 可自由实现:缓存优先/网络优先/增量更新等 |
缓存清理 | activate 事件中删除旧缓存 |
离线回退 | 在 fetch 事件中返回兜底内容(如离线页面) |
3. 常用缓存策略
// 1. 缓存优先(适合静态资源)
caches.match(request).then(cached => cached || fetch(request))
// 2. 网络优先(适合频繁更新数据)
fetch(request).catch(() => caches.match(request))
// 3. 增量更新(先返回缓存,再更新缓存)
event.respondWith(caches.match(request));
event.waitUntil(
fetch(request).then(response => cache.put(request, response))
);
4. 调试与更新
- 调试:Chrome DevTools → Application → Service Workers
- 更新机制:
- 修改 Service Worker 文件(即使1字节变化)
- 新 SW 安装后处于
waiting
状态 - 通过
skipWaiting()
强制激活或关闭所有页面后生效
三、最佳实践
- 仅缓存必要资源:避免占用过多存储空间。
- 设置缓存过期:定期清理旧缓存。
- 提供离线回退:如返回离线页面或默认图片。
- 结合 IndexedDB:存储结构化数据(如用户设置)。
- 使用 Workbox:Google 官方库,简化 Service Worker 开发:
import {precacheAndRoute} from 'workbox-precaching';
precacheAndRoute([...]); // 自动生成缓存清单
四、浏览器兼容性
- Service Worker:Chrome 40+、Firefox 44+、Edge 17+、Safari 11.1+
- Cache API:同 Service Worker 支持范围
- 安全要求:必须使用 HTTPS(本地开发允许
localhost
)
总结对比
特性 | Application Cache (废弃) | Service Worker + Cache API |
---|---|---|
控制粒度 | 声明式(manifest文件) | 编程式(JavaScript) |
更新机制 | 手动修改 manifest | 文件内容变化自动更新 |
请求拦截 | ❌ 不支持 | ✅ 完全控制网络请求 |
后台同步 | ❌ 不支持 | ✅ 支持后台同步(Background Sync) |
缓存策略灵活性 | 极低 | 极高(可自定义逻辑) |
现代浏览器支持 | 已废弃 | 广泛支持 |
💡 现代 Web 离线存储应优先使用 Service Worker,它提供了更强大、灵活的离线能力,是 PWA(渐进式 Web 应用)的核心技术之一。