HTML5 的离线存储主要通过 Service Worker 技术实现(Application Cache
已被废弃)。它允许 Web 应用在无网络连接时依然可以访问,提供类似原生应用的体验。
下面详细介绍其使用方法和工作原理。
一、如何使用 HTML5 离线存储(Service Worker)
使用 Service Worker 实现离线存储是一个多步骤的过程,涉及三个主要文件:
1. 主页面 (index.html)
在主页面中注册 Service Worker。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>离线应用示例</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>欢迎使用离线应用</h1>
<p>即使断开网络,您也可以继续使用本应用。</p>
<script>
// 确保在页面加载完成后注册 Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('Service Worker 注册成功:', registration.scope);
})
.catch((error) => {
console.log('Service Worker 注册失败:', error);
});
});
}
</script>
</body>
</html>
2. Service Worker 脚本 (sw.js)
这是核心文件,必须放在应用的根目录或能被所有页面访问的路径。它控制缓存和网络请求。
// 定义缓存名称
const CACHE_NAME = 'my-app-v1';
// 需要缓存的资源列表
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/images/logo.png',
'/offline.html' // 离线时显示的页面
];
// 安装阶段:预缓存关键资源
self.addEventListener('install', (event) => {
console.log('Service Worker 正在安装...');
// waitUntil 确保安装过程在缓存完成前不会结束
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('打开缓存');
// addAll 会缓存所有资源,如果其中一个失败,整个安装失败
return cache.addAll(urlsToCache);
})
);
});
// 激活阶段:清理旧缓存
self.addEventListener('activate', (event) => {
console.log('Service Worker 激活中...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
// 删除旧版本的缓存
if (cacheName !== CACHE_NAME) {
console.log('删除旧缓存:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
// 让新的 Service Worker 立即获得控制权
return self.clients.claim();
});
// 网络请求拦截:实现离线策略
self.addEventListener('fetch', (event) => {
console.log('拦截请求:', event.request.url);
// 仅对同源请求应用离线策略
if (event.request.url.startsWith(self.location.origin)) {
event.respondWith(
// 1. 先尝试从缓存中匹配
caches.match(event.request)
.then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse; // 返回缓存
}
// 2. 缓存没有,发起网络请求
return fetch(event.request).then((networkResponse) => {
// 3. 将网络响应存入缓存(供下次使用)
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache);
});
return networkResponse;
}).catch(() => {
// 4. 网络请求失败(离线),返回离线页面
return caches.match('/offline.html');
})
})
);
}
});
3. 离线页面 (offline.html) (可选但推荐)
当用户离线且请求的资源不在缓存中时,显示此页面。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>离线</title>
</head>
<body>
<h1>您已离线</h1>
<p>请检查您的网络连接。</p>
</body>
</html>
二、工作原理
Service Worker 的工作原理基于其生命周期和网络代理能力。
1. 生命周期 (Lifecycle)
- 注册 (Registration):
- 主页面 JavaScript 调用
navigator.serviceWorker.register('/sw.js')
。 - 浏览器下载
sw.js
文件。
- 主页面 JavaScript 调用
- 安装 (Install):
- 下载成功后,触发
install
事件。 - 在
install
事件中,通过caches.open()
和cache.addAll()
将urlsToCache
列表中的资源预先缓存到浏览器的Cache Storage
中。 - 如果缓存过程成功,Service Worker 进入“已安装”状态。
- 下载成功后,触发
- 激活 (Activate):
- 触发
activate
事件。 - 在此阶段,通常清理旧版本的缓存(如示例中的
my-app-v1
之前的版本),避免缓存膨胀。 - 激活后,Service Worker 开始拦截页面的网络请求。
- 触发
- 控制 (Controlling):
- 激活的 Service Worker 开始接管页面的
fetch
事件。
- 激活的 Service Worker 开始接管页面的
2. 缓存与加载机制
- 首次在线访问:
- 页面加载,注册 Service Worker。
- Service Worker 安装并缓存关键资源。
- 页面从网络加载资源(此时新缓存还未被使用)。
- Service Worker 激活。
- 后续访问(在线或离线):
- 当页面发起网络请求时,Service Worker 的
fetch
事件被触发。 - 代码逻辑:
- 优先缓存:
caches.match(event.request)
检查请求的资源是否在缓存中。 - 命中缓存:如果找到,直接返回缓存的响应,极快。
- 缓存未命中:发起
fetch(event.request)
网络请求。- 网络成功:返回网络响应,并将其克隆后存入缓存(供未来使用)。
- 网络失败(离线):捕获异常,返回预缓存的
offline.html
或其他降级资源。
- 优先缓存:
- 当页面发起网络请求时,Service Worker 的
- 更新机制:
- 当
sw.js
文件内容发生变化(字节不同),浏览器会下载新版本。 - 新的 Service Worker 开始安装,但不会立即激活(旧的仍在控制页面)。
- 当所有使用旧 Service Worker 的页面都被关闭后,新的 Service Worker 才会激活,并在
activate
事件中清理旧缓存。
- 当
三、核心优势
- 精细控制:开发者完全掌控缓存策略(缓存什么、何时缓存、如何更新)。
- 增量更新:只更新变化的资源,无需全量下载。
- 离线优先:可实现“先展示缓存,再后台更新”的最佳用户体验。
- 强大功能:支持后台同步、消息推送等 PWA 特性。
四、注意事项
- HTTPS 环境:Service Worker 要求网站必须通过 HTTPS 提供(本地开发
localhost
除外)。 - 作用域:
register()
的路径决定了 Service Worker 能控制的页面范围。 - 调试:可在浏览器开发者工具的 “Application” 或 “Service Workers” 面板中调试和管理。
总结
HTML5 离线存储通过 Service Worker 实现。开发者编写一个 JavaScript 脚本 (sw.js
),在 install
阶段预缓存资源,在 fetch
事件中拦截网络请求,优先返回缓存内容,网络失败时返回离线页面。其工作原理基于 Service Worker 的安装、激活和网络代理生命周期,为 Web 应用提供了强大的离线能力。
THE END