第七章 离线浏览

7.1 解锁缓存

在没有网络时,用户尝试访问 Web 应用,Service Worker 能拦截 HTTP 请求,返回用户要查看的页面的缓存版本。

7.2 提供离线文件

↓ 将离线页面添加到 Service Worker 缓存中

1
2
3
4
5
6
7
8
9
10
const cacheName = 'offline-cache'
const offlineUrl = 'offline-page.html'

self.addEventListener('install', event => {
event.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll([offlineUrl])
})
)
})

↓ 当用户没有连接时提供离线页面

1
2
3
4
5
6
7
8
9
self.addEventListener('fetch', event => {
if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) {
event.respondWith(fetch(event.request.url).catch(error => {
return caches.match(offlineUrl)
}))
} else {
event.respondWith(fetch(event.request))
}
})

↓ 如果资源没有存储在缓存中,则降级成默认的离线页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const cacheName = 'latestNews-v1'
const offlineUrl = 'offline-page.html'

self.addEventListener('install', event => {
event.waitUntil(
caches.open(cacheName)
.then(cache => cache.addAll([
'./js/main.js',
'./js/article.js',
'./images/newspaper.svg',
'./css/site.css',
'./data/latest.json',
'./data/data-1.json',
'./article.html',
'./index.html',
offlineUrl
]))
)
})

self.addEventListener('fetch', event => {
event.respondWith(caches.match(event.request).then(function (response) {
if (response) return response

var fetchRequest = event.request.clone()

return fetch(fetchRequest).then(function (response) {
if (!response || response.status !== 200) return response

var responseToCache = response.clone()
caches.open(cacheName).then(function (cache) {
cache.put(event.request, responseToCache)
})

return response
}).catch(error => { // 如果由于任何原因 fetch 事件失败,就检查用户是否跳转至其他页面并且请求的是 HTML 网页
if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) {
return caches.match(offlineUrl) // 返回在 Service Worker 安装阶段存储在缓存中的离线页面
}
})
}))
})

上一段代码永远会先从缓存中寻找资源,如果缓存中找不到,才会转而去发起请求。如果它无法通过网络来获取下一个网页,又会降级成缓存的离线页面。

7.3 几个需要注意的问题

是否需要在 Service Worker 安装期间将整个网站缓存?

当提供离线功能时,请考虑用户需求及用户量。用户是如何访问你的网站的?他们是否需要一次下载完整个网站,或者当它们访问某个新页面时再去获取?

7.4 缓存是非永久性的

每个站点都具有一定量的可用空间,这些空间可以与其他所有基于 Web 的存储器(LocalStorage、IndexedDB 和设备上的文件系统)共享。

7.5 离线用户体验

1
2
3
4
5
6
7
8
9
10
11
12
13
var offlineNotification = document.getElementById('offline')

function showIndicator() { // 当用户离线时,此函数用来显示离线通知
offlineNotification.innerHTML = 'You are currently offline.'
offlineNotification.className = 'showOfflineNotification'
}

function hideIndicator() { // 当用户再次上线时,此函数用来隐藏离线通知
offlineNotification.className = 'hideOfflineNotification'
}

window.addEventListener('online', hideIndicator)
window.addEventListener('offline', showIndicator)

7.6 跟踪离线使用情况

如果用户处于离线状态,则无法使用传统的 Web 分析方法来跟踪它们。在没有网络连接的情况下,分析请求将无法发出,用户的操作行为将会丢失。

Google 的开源工具库 Workbox 提供了一个叫 googleAnalytics 的离线分析工具。当用户离线时,这个库会将所有分析请求放入队列中等候,一旦用户重新连接网络,会将队列中的请求发送到分析服务器。