SW初探

SW - Service Worker

下面来简单解析下hello world

register

// TODO add service worker code here
if ('serviceWorker' in navigator) {
navigator
.serviceWorker
.register('./service-worker.js')
.then(function () {
console.log('Service Worker Registered');
});
}

在程序入口注册serverWorker,当然要判断浏览器是否支持

注意Service worker程序所在目录就是他的作用域,一般放根目录下

install

var cacheName = 'weatherPWA-step-6-1';
var dataCacheName = 'weatherData-v1';
var filesToCache = [
'/',
'/index.html',
'/scripts/app.js',
'/styles/inline.css',
'/images/clear.png',
'/images/cloudy-scattered-showers.png',
'/images/cloudy.png',
'/images/fog.png',
'/images/ic_add_white_24px.svg',
'/images/ic_refresh_white_24px.svg',
'/images/partly-cloudy.png',
'/images/rain.png',
'/images/scattered-showers.png',
'/images/sleet.png',
'/images/snow.png',
'/images/thunderstorm.png',
'/images/wind.png'
];

console.log(self);
self.addEventListener('install', function (e) {
console.log('[ServiceWorker] Install', e, caches);
e.waitUntil(caches.open(cacheName).then(function (cache) {
console.log('[ServiceWorker] Caching app shell', cache);
return cache.addAll(filesToCache);
}));
});

安装的时候开启caches,把自己的cache列表加入caches

activate

self.addEventListener('activate', function (e) {
console.log('[ServiceWorker] Activate');
// cacheName = `${cacheName}${Math.random()}`
e.waitUntil(caches.keys().then(function (keyList) {
console.log(keyList, 'keylist');
return Promise.all(keyList.map(function (key) {
if (key !== cacheName && key !== dataCacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
}));
return self
.clients
.claim();
});

激活的时候根据cacheName dataCacheName 来把不在列表中的东西从 cache干掉

dataCacheName存放请求的数据的

When the app is complete, self.clients.claim() fixes a corner case in which the app wasn’t returning the latest data. You can reproduce the corner case by commenting out the line below and then doing the following steps: 1) load app for first time so that the initial New York City data is shown 2) press the refresh button on the app 3) go offline 4) reload the app. You expect to see the newer NYC data, but you actually see the initial data. This happens because the service worker is not yet activated. self.clients.claim() essentially lets you activate the service worker faster.

fetch拦截

self.addEventListener('fetch', function(e) {
console.log('[Service Worker] Fetch', e.request.url);
var dataUrl = 'https://query.yahooapis.com/v1/public/yql';
if (e.request.url.indexOf(dataUrl) > -1) {
/*
* When the request URL contains dataUrl, the app is asking for fresh
* weather data. In this case, the service worker always goes to the
* network and then caches the response. This is called the "Cache then
* network" strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
*/
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response){
console.log(response,"fetch with network");
cache.put(e.request.url, response.clone());
return response;
});
})
);
} else {
/*
* The app is asking for app shell files. In this scenario the app uses the
* "Cache, falling back to the network" offline strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
*/
e.respondWith(
caches.match(e.request).then(function(response) {
console.log(response,"fetch in app shell");
return response || fetch(e.request);
})
);
}
});

不仅仅cache一些静态的资源,还可以拦截请求,并操作是否预先冲cache中取缓存数据
一般有两种策略

  • 缓存有限
  • 网络请求优先

app中应用

var url = 'https://query.yahooapis.com/v1/public/yql?format=json&q=' + statement;
// TODO add cache logic here Fetch the latest data.
if ('caches' in window) {
/*
* Check if the service worker has already cached this city's weather
* data. If the service worker has the data, then display the cached
* data while the app fetches the latest data.
*/
caches.match(url).then(function(response) {
if (response) {
response.json().then(function updateFromCache(json) {
console.log(json);
var results = json.query.results;
results.key = key;
results.label = label;
results.created = json.query.created;
app.updateForecastCard(results);
});
}
});
}

var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status === 200) {
var response = JSON.parse(request.response);
var results = response.query.results;
results.key = key;
results.label = label;
results.created = response.query.created;
app.updateForecastCard(results);
}
} else {
// Return the initial weather forecast since no data is available.
app.updateForecastCard(initialWeatherForecast);
}
};
request.open('GET', url);
request.send();
};

程序内应用 要先判断是否浏览器支持caches
如果请求的url可以匹配到,就先用cache更新UI
后面会请求更新最新数据,更新的时候需要判断网络请求回来的数据和cache里的那个是最新的(SW拿东西不一定就比网络快)

如果要在生产环境应用这个,还需调研 https://github.com/GoogleChrome/sw-precache