Service Worker 实现离线应用思路
1 背景
本地运行一个vue项目,界面如下:
使用 Service Worker 对其进行缓存,预期在停止运行项目时(模拟离线场景)也能够正常访问项目
2 缓存的资源分析
检查 network,浏览器访问:http://localhost:5173/ 时,获取的资源需要包含如下,才能正常展示界面:
- HTML 文件
- JS 文件
- CSS 文件
3 index.html 内容
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<div
style="
background-color: #fff;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 300px;
padding: 20px;
text-align: center;
transition: transform 0.3s ease;
"
>
<img
src="https://via.placeholder.com/280x150"
alt="Card Image"
style="width: 100%; border-radius: 8px; height: auto"
/>
<h2 style="margin: 20px 0; font-size: 1.5rem">Card Title</h2>
<p style="font-size: 1rem; color: #555; margin-bottom: 20px">
This is a simple card with an image, title, and description. It uses inline styles.
</p>
<button
style="
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
font-size: 1rem;
border-radius: 5px;
"
>
Click Me
</button>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
4 注册/删除 Service Worker
在 index.html 中注册Service Worker
// 注册 Service Worker 检查浏览器是否支持 Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/service-worker.js') // 注册 Service Worker
.then((registration) => {
console.log('Service Worker 注册成功:', registration)
})
.catch((error) => {
console.log('Service Worker 注册失败:', error)
})
})
}
// 删除 Service Worker
if ('serviceWorker' in navigator) {
// 获取当前注册的 Service Worker
navigator.serviceWorker.getRegistration().then((registration) => {
if (registration) {
// 注销 Service Worker
registration.unregister().then((boolean) => {
if (boolean) {
console.log('Service Worker 已成功注销')
} else {
console.log('Service Worker 注销失败')
}
})
} else {
console.log('没有找到已注册的 Service Worker')
}
})
}
5 service-worker.js 文件
const CACHE_NAME = 'my-app-cache-v1' // 缓存的名称
const URLS_TO_CACHE = [
'/', // HTML 根页面
'/src/main.ts', // JS 文件 由于是本地模拟,所以是 /src/
'/src/assets/main.css' // CSS 文件 由于是本地模拟,所以是 /src/
]
// 安装 Service Worker 时,缓存指定的资源
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('缓存文件...')
return cache.addAll(URLS_TO_CACHE)
})
)
})
// 拦截请求并返回缓存数据(如果有)
self.addEventListener('fetch', (event) => {
console.log('event.request.url ', event.request.url)
event.respondWith(
caches.match(event.request, { ignoreSearch: true }).then((cachedResponse) => { // 设置 ignoreSearch: true 来忽略查询参数,例如,/index.html 和 /index.html?version=1 将被视为相同。
// 如果缓存中有匹配的响应,则返回缓存
if (cachedResponse) {
console.log('从缓存中提供:', event.request.url)
return cachedResponse
}
console.log('从网络中提供:', event.request.url)
// 如果缓存中没有,发送网络请求
return fetch(event.request)
})
)
})
当 Service Worker 安装时,它会缓存资源(如 html、css、.js 等)
缓存的资源包含:
const URLS_TO_CACHE = [
'/', // HTML 根页面
'/src/main.ts', // JS 文件 由于是本地模拟,所以是 /src/
'/src/assets/main.css' // CSS 文件 由于是本地模拟,所以是 /src/
]
当缓存这些资源后,后续请求url如果匹配到了就可以返回,如:
- ‘http://localhost:5173/src/’ 会匹配到 ‘/’
- ‘http://localhost:5173/src/main.ts/’ 会匹配到 ‘/src/main.ts’
缓存的资源在cache中可以查看
离线
停止运行项目,模拟离线场景,输入 http://localhost:5173/ 进入项目,可以看到项目正常展示
网络请求中可以看到, html js css 都是通过 serviceworker 返回的缓存