Service Worker到底是干什么的?
拦截和处理浏览器和网络发出的请求,且开发者可以缓存网页的资源文件(如HTML、CSS、JavaScript、图像等),使用户在离线情况下仍能够访问应用程序,或者在网络条件较差时提供更快的加载速度。
使用场景
- 离线缓存(Offline caching):通过缓存资源文件,使应用程序在断网情况下仍然可用。
- 推送通知(Push notifications):使网站能够向用户发送推送通知,即使用户当前没有打开网站。
- 后台同步(Background sync):在网络连接恢复后,可以在后台同步数据,而不必依赖用户打开应用程序。
- 性能优化(Performance optimization):通过缓存策略和智能加载,提高网页的加载速度和性能。
如何使用
首先我们来直接模拟建立一个正常地请求
建立页面index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>News App</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<h1>News App</h1>
<div id="news-list">
</div>
<script src="./script.js"></script>
</body>
</html>
模拟数据 news.json
[
{
"title": "Breaking News",
"content": "This is a breaking news article."
},
{
"title": "Latest Update",
"content": "Here is the latest update on the ongoing event."
}
]
建立请求并显示数据 script.js
document.addEventListener('DOMContentLoaded', async () => {
try {
const response = await fetch('/news.json');
const newsData = await response.json();
const newsList = document.getElementById('news-list');
newsData.forEach(article => {
const articleElement = document.createElement('div');
articleElement.innerHTML = `
<h2>${article.title}</h2>
<p>${article.content}</p>
`;
newsList.appendChild(articleElement);
});
} catch (error) {
console.error('Error fetching news:', error);
}
});
运行看结果
以上都是非常常规的请求数据的方法,那我们再来看看加入service worker之后的
加入service worker
添加service-worker.js
const CACHE_NAME = 'news-app-cache-v1';
const urlsToCache = [
'/index.html',
'/script.js',
'/news.json'
];
// 创建一个缓存
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
return cache.addAll(urlsToCache);
})
);
});
// 拦截请求
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// 如果缓存中存在请求的响应,则直接返回缓存的响应
if (cachedResponse) {
console.log('Response from cache:', event.request.url);
return cachedResponse;
}
// 如果缓存中不存在请求的响应,则继续向网络发出请求
return fetch(event.request)
.then(networkResponse => {
// 将从网络获取到的响应添加到缓存中
return caches.open('dynamic-cache')
.then(cache => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
})
.catch(error => {
console.error('Fetch error:', error);
// 如果网络请求失败,可以返回一个自定义的响应
return new Response('Network request failed!', {
status: 500,
statusText: 'Internal Server Error'
});
});
})
);
});
修改script.js
document.addEventListener('DOMContentLoaded', async () => {
try {
const response = await fetch('/news.json');
const newsData = await response.json();
const newsList = document.getElementById('news-list');
newsData.forEach(article => {
const articleElement = document.createElement('div');
articleElement.innerHTML = `
<h2>${article.title}</h2>
<p>${article.content}</p>
`;
newsList.appendChild(articleElement);
});
} catch (error) {
console.error('Error fetching news:', error);
}
// 添加部分 判断是否可用,可用就进行注册
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./service-worker.js')
.then(registration => {
console.log('Service Worker registered:', registration);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
}
});
来看看成果
首先第一次请求时,我们看到还是实打实地去进行请求
我们来刷新一下页面,就会瞬间发现,请求都改成了从缓存中获取,大大节省了我们的请求时间
添加清理缓存
假如我们要清理掉缓存,也是非常简单,调用caches.delete即可
修改index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>News App</title>
</head>
<body>
<h1>News App</h1>
<div id="news-list">
</div>
<script src="./script.js"></script>
<script>
function handleClick() {
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
return caches.delete(cacheName);
})
);
}).then(() => {
console.log('All caches have been deleted');
// 在页面中显示消息,通知用户缓存已被清除
alert('缓存已清除');
}).catch(error => {
console.error('Cache deletion failed:', error);
// 如果删除缓存失败,也可以在页面中显示错误消息
alert('清除缓存失败');
});
}
</script>
</body>
</html>
总结
以上就是基于Service Worker做的缓存文件功能,当然因为其本身拦截和处理浏览器和网络发出请求的特性,可以做到的东西其实还有挺多的,比如在基于self.addEventListener('fetch', event => {
更改请求头,更改返回主体,等等功能
比如
self.addEventListener('fetch', event => {
event.respondWith(
// 返回自定义的响应
new Response('Hello from Service Worker!', {
headers: { 'Content-Type': 'text/plain' }
})
);
});