目录
写在前面
资源离线缓存
Service Worker相关的事件
1. fetch事件
2. message事件
今天我们来谈一谈ServiceWorker ,利用ServiceWorker 来进行资源的离线缓存,进而构建离线应用。谈到ServiceWorker,不得不谈一谈近些年大火的PWA。
PWA(Progressive Web App)翻译过来就是渐进式增强WEB应用, 是Google 在2016年提出的概念,2017年落地的web技术。目的就是在移动端利用提供的标准化框架,在网页应用中实现和原生应用相近的用户体验的渐进式网页应用。
再来看看MDN官方的解释:PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富,使其具有与原生应用相同的用户体验优势。
此节我们将通过一个简单的demo来向大家描述如何利用ServiceWorker来进行资源的离线缓存。先看看整个demo的目录结构:
先来看看app.js的内容:
const sw = navigator.serviceWorker
if (sw) {
sw.register('sw.js', {scope: './'})
.then(res => {
console.log('sw open...', res);
})
.catch(err => {
console.log('sw error', err);
})
} else {
console.log('sw is not supported!');
}
可以看到,我们先注册serviceWorker文件,调用navigator.serviceWorker.register方法即可在应用中注册需要用到的serviceWorker文件。
再来看sw.js的文件内容:
const cacheList = [
'./index.html',
'./main.css',
'app.js',
'img/wk.jpg'
]
const SW_CACHE_NAME = 'sw-offline'
self.addEventListener('install', e => {
e.waitUntil(
caches.open(SW_CACHE_NAME)
.then(cache => {
console.log('cahche open');
return cache.addAll(cacheList)
})
)
})
self.addEventListener('fetch', event => {
const req = event.request
console.log('请求为', req);
event.respondWith(
caches.match(req).then(cache => {
if (cache) {
return cache
} else {
// 如果我们想要增量的缓存新的请求,我们可以通过处理fetch请求的response并且添加它们到缓存中来实现(如下代码所示)
console.log('此处的资源需要向网络进行请求', req);
// 克隆请求。因为请求是一个“stream”,只能用一次。但我们需要用两次,一次用来缓存,一次给浏览器抓取内容,所以需要克隆
var fetchRequest = req.clone();
return fetch(fetchRequest).then(response=> {
// 检查是否为有效的响应。basic表示同源响应,也就是说,这意味着,对第三方资产的请求不会添加到缓存。
if (!response || response.status !== 200 || !response.headers.get('Content-type').match(/img/)) {
return response;
}
// 同request,response是一个“stream”,只能用一次,但我们需要用两次,一次用来缓存一个返回给浏览器,所以需要克隆。
var responseToCache = response.clone();
// 缓存新请求
caches.open(SW_CACHE_NAME)
.then(function (cache) {
cache.put(req, responseToCache);
})
})
}
})
)
})
在sw.js文件中监听install事件,即监听serviceWorker的安装,这里的self代表ServiceWorkerGlobalScope 全局作用域,e.waitUntil方法接收一个promise作为参数,这里的waitUtil会在serviceWorker脚本安装成功之前执行一些预装的操作,但是只建议做一些轻量级和非常重要资源的缓存,减少安装失败的概率。安装成功后ServiceWorker状态会从installing变为installed。SW_CACHE_NAME代表这个缓存的名字,cacheList代表初次缓存的文件。
上面的代码中,可以看到通过caches.open方法打开我们指定的cache文件名,然后我们调用cache.addAll并传入我们的缓存文件数组。这是通过一连串的promise(caches.open和caches.addAll)完成的event.waitUntil拿到一个promise的状态来获取安装是否成功,如果所有的文件都被缓存成功了,那么Service Worker就安装成功了。如果任何一个文件下载安装失败,那么整个Service Worker的安装就失败。这意味着你需要非常谨慎地决定那些文件需要在安装步骤中被缓存,指定太多文件的话就会增加整个Service Worker应用安装失败的概率。
监听fetch事件,即Service Worker可以监听网络请求,当Service Worker被安装成功并且用户浏览了另一个页面,Service Worker将开始接受fetch事件。event.respondWith里我们传入了一个由caches.match产生的promise.caches.match查找request中被Service Worker缓存命中的response。如果我们有一个命中的response,我们返回被缓存的值,否则我们返回一个实时从网络请求的结果。
在页面发起http/https请求时,Service Worker可以通过fetch事件拦截请求,并且给出自己的相应。w3c提供了一个新的fetch API可用于取代XMLHttpRequest,与XMLHttpRequest最大的不同就是:fetch方法返回的是promise对象,可通过then方法进行连续调用,减少嵌套。
页面和ServiceWorker质检可以通过postMessage方法发送消息,发送的消息可以通过message事件接收到。这是一个双向的过程,页面可以发消息给Service Worker,Service Worker也可以发送消息给页面,由于这个特性,可以将Service Worker作为中间纽带,使得一个域名或者子域名下的多个页面自由通信页可以实现服务器消息推送的功能。
项目demo地址:github