Service worker可以充当Web应用程序与浏览器之间的代理服务器,常用于拦截网络请求,优化离线体验。最近开发一个功能需要用到股票信息,这些信息在每个交易日都是固定不变的,可以在前端缓存起来,减少重复请求。于是引入了Service workers。
由于Service workers只支持https,且在IE和Safari不无能为力,因此得先写一套基于传统localStorage的缓存方案,大概像这样:
let stockData = []; //缓存的股票信息
const stockCacheExpiration = 1; //缓存过期的天数
function getStockInfo(){
// 如果缓存里有信息且不过期,则直接读取缓存,够则请求新的信息
let stockCache = localStorage.getItem('stock_info');
if (stockCache) {
let stockInfo = JSON.parse(stockCache);
let today = getDate();
if (today-stockInfo.date < stockCacheExpiration) {
stockData = stockInfo.data;
} else {
refreshStockInfo();
}
} else {
refreshStockInfo();
};
}
function refreshStockInfo(){
// 请求股票信息,返回可以交易的股票信息的数组
fetch('api/stock.json', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.then(function (response) {
if (response.status == 200) {
return response.json()
}
})
.then(function (data) {
stockData = data;
let stockInfo = {
date: getDate(),
data: data
};
localStorage.setItem('stock_info', JSON.stringify(stockInfo));
})
.catch(function (error) {
console.error(error)
});
}
function getDate(){
// 获取当前日期函数,省略
return '20171010'
}
省略不重要的内容,但能说清楚需求:如果缓存在有效期内,就直接读取缓存信息;如果没有有效缓存,fetch发起get请求。
下面进入正题,用Service Worker拦截请求。这样业务代码里不需要再判断是否有缓存,直接请求就对了,缓存的事情完全交给Service Worker去实现。首先判断浏览器支持,然后引入serviceWorker:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
}
如果sw.js已经被注册过,浏览器会自动忽略上面的代码。用Chrome浏览器,可以到 chrome://inspect/#service-workers 查看启用情况。sw.js就是我们要调用的serviceWorker了,用来实现缓存,拦截fetch并返回缓存的内容。核心代码如下:
const urlsToCache = [
'api/stock.json'
];
// serviceWorker安装的时候缓存股票信息
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('stock')
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// 拦截fetch请求,并返回缓存的内容
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
这里我们用Cache进行缓存,这是Service Worker衍生出来的API,并不直接缓存字符串(想想localstorage),而是直接缓存资源请求(css、js、html、json等)。缓存的key就是request,value就是response。
在Chrome调试台的Network里查看请求,可以看到Status Code:200 OK (from ServiceWorker),表示这个请求是ServiceWorker处理过的。把缓存逻辑提取出来之后,业务代码就非常简单了,只需要在需要用到的地方直接发出请求就行了,不需要管缓存的事情。这样也可以避免代码污染,让业务代码变得更加清晰。
Service Worker使用起来还是有许多麻烦,首先是过期机制,需要关掉页面重新打开才能调用到install,调试过程比较麻烦。其次权限非常大,为了安全仅支持https,跨域也很复杂。最重要的是IE不配合,所以还得做一套兼容,代码就更加不优雅了。
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。