菜谱闹钟
"Offline" is a big topic these days, especially as many web apps look to also function as mobile apps. The original offline helper API, the Application Cache API (also known as "appcache"), has a host of problems, many of which can be found in Jake Archibald's Application Cache is a Douchebag post. Problems with appcache include:
如今,“离线”是一个大话题,尤其是许多Web应用程序看起来也可以像移动应用程序一样工作。 最初的脱机帮助程序API,即应用程序缓存API(也称为“ appcache”),存在很多问题,其中许多问题都可以在Jake Archibald的应用程序缓存中找到(Dougebag帖子)。 appcache的问题包括:
- Files are served from cache even when the user is online. 即使用户在线,也可以从缓存中提供文件。
- There's no dynamism: the appcache file is simply a list of files to cache. 没有动力:appcache文件只是要缓存的文件列表。
One is able to cache the .appcache file itself and that leads to update problems.
一种能够缓存.appcache文件本身 ,从而导致更新问题。
其他陷阱 。
Today there's a new API available to developers to ensure their web apps work properly: the Service Worker API. The Service Worker API allows developers to manage what does and doesn't go into cache for offline use with JavaScript.
如今,开发人员可以使用一种新的API来确保其Web应用程序正常运行: Service Worker API 。 Service Worker API使开发人员可以管理缓存中要做什么和不做什么,以供JavaScript脱机使用。
介绍服务人员食谱 (Introducing the Service Worker Cookbook)
To introduce you to the Service Worker API we'll be using examples from Mozilla's new Service Worker Cookbook! The Cookbook is a collection of working, practical examples of service workers in use in modern web apps. We'll be introducing service workers within this three-part series:
为了向您介绍Service Worker API,我们将使用Mozilla新的Service Worker Cookbook中的示例! 该菜谱是现代Web应用程序中使用的服务工作者的实际工作示例的集合。 我们将在这个分为三部分的系列中介绍服务人员:
- Offline Recipes for Service Workers (today's post) 服务人员的离线食谱(今天的帖子)
- At Your Service for More Than Just appcache 为您服务的不仅仅是appcache
- Web Push Updates to the Masses 网络推送更新给大众
Of course this API has advantages other than enabling offline capabilities, such as performance for one, but I'd like to start by introducing basic service worker strategies for offline.
当然,此API除了启用脱机功能(例如一种性能)外,还具有其他优势,但我想首先介绍脱机的基本服务工作者策略。
离线意味着什么? (What do we mean by offline?)
Offline doesn't just mean the user doesn't have an internet connection — it can also mean that the user is on a flaky network connection. Essentially "offline" means that the user doesn't have a reliable connection, and we've all been there before!
脱机不仅意味着用户没有Internet连接,还意味着用户处于不稳定的网络连接中。 从本质上讲,“离线”意味着用户没有可靠的连接,而我们以前都去过那里!
食谱: 离线状态 (Recipe: Offline Status)
The Offline Status recipe illustrates how to use a service worker to cache a known asset list and then notify the user that they may now go offline and use the app. The app itself is quite simple: show a random image when a button is clicked. Let's have a look at the components involved in making this happen.
脱机状态配方说明了如何使用服务人员来缓存已知资产列表,然后通知用户他们现在可能脱机并使用该应用程序。 该应用程序本身非常简单:单击按钮时显示随机图像。 让我们看看实现这一目标所涉及的组件。
服务人员 (The Service Worker)
We'll start by looking at the service-worker.js
file to see what we're caching. We'll be caching the random images to display, as well as the display page and critical JavaScript resources, in a cache named dependencies-cache
:
我们将从查看service-worker.js
文件开始,以查看我们要缓存的内容。 我们将在名为dependencies-cache
要显示的随机图像以及显示页面和关键JavaScript资源:
var CACHE_NAME = 'dependencies-cache';
// Files required to make this app work offline
var REQUIRED_FILES = [
'random-1.png',
'random-2.png',
'random-3.png',
'random-4.png',
'random-5.png',
'random-6.png',
'style.css',
'index.html',
'/', // Separate URL than index.html!
'index.js',
'app.js'
];
The service worker's install
event will open the cache and use addAll
to direct the service worker to cache our specified files:
服务人员的install
事件将打开缓存,并使用addAll
指示服务人员缓存我们的指定文件:
self.addEventListener('install', function(event) {
// Perform install step: loading each required file into cache
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
// Add all offline dependencies to the cache
return cache.addAll(REQUIRED_FILES);
})
.then(function() {
// At this point everything has been cached
return self.skipWaiting();
})
);
});
The fetch
event of a service worker is fired for every single request the page makes. The fetch
event also allows you to serve alternate content than was actually requested. For the purposes of offline content, however, our fetch
listener will be very simple: if the file is cached, return it from cache; if not, retrieve the file from server:
页面发出的每个请求都会触发服务工作者的fetch
事件。 fetch
事件还允许您提供实际请求之外的替代内容。 但是,出于脱机内容的目的,我们的fetch
侦听器将非常简单:如果文件已缓存,请从缓存返回; 如果不是,请从服务器检索文件:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return the response from the cached version
if (response) {
return response;
}
// Not in cache - return the result from the live server
// `fetch` is essentially a "fallback"
return fetch(event.request);
}
)
);
});
The last part of this service-worker.js
file is the activate
event listener where we immediately claim the service worker so that the user doesn't need to refresh the page to activate the service worker. The activate event
fires when a previous version of a service worker (if any) has been replaced and the updated service worker takes control of the scope.
这最后一部分service-worker.js
文件是activate
事件监听器,我们马上要求服务工作者,让用户无需刷新页面激活服务工作者。 当替换了以前版本的服务程序(如果有)并且更新的服务程序可以控制作用域时,会activate event
。
self.addEventListener('activate', function(event) {
// Calling claim() to force a "controllerchange" event on navigator.serviceWorker
event.waitUntil(self.clients.claim());
});
Essentially we don't want to require the user to refresh the page for the service worker to begin — we want the service worker to activate upon initial page load.
本质上,我们不希望用户刷新页面以便服务人员开始-我们希望服务人员在初始页面加载时激活。
服务人员注册 (Service worker registration)
With the simple service worker created, it's time to register the service worker:
创建简单的服务程序后,就该注册服务程序了:
// Register the ServiceWorker
navigator.serviceWorker.register('service-worker.js', {
scope: '.'
}).then(function(registration) {
// The service worker has been registered!
});
Remember that the goal of the recipe is to notify the user when required files have been cached. To do that we'll need to listen to the service worker's state
. When the state
has become activated
, we know that essential files have been cached, our app is ready to go offline, and we can notify our user:
请记住,配方的目标是在缓存了所需文件时通知用户。 为此,我们需要听服务人员的state
。 state
变为activated
state
,我们知道基本文件已经缓存,我们的应用程序已准备就绪可以离线,我们可以通知用户:
// Listen for claiming of our ServiceWorker
navigator.serviceWorker.addEventListener('controllerchange', function(event) {
// Listen for changes in the state of our ServiceWorker
navigator.serviceWorker.controller.addEventListener('statechange', function() {
// If the ServiceWorker becomes "activated", let the user know they can go offline!
if (this.state === 'activated') {
// Show the "You may now use offline" notification
document.getElementById('offlineNotification').classList.remove('hidden');
}
});
});
Testing the registration and verifying that the app works offline simply requires using the recipe! This recipe provides a button to load a random image by changing the image's src
attribute:
测试注册并验证应用程序可以脱机运行,只需使用配方即可! 此配方提供了一个按钮,可通过更改图像的src
属性来加载随机图像:
// This file is required to make the "app" work offline
document.querySelector('#randomButton').addEventListener('click', function() {
var image = document.querySelector('#logoImage');
var currentIndex = Number(image.src.match('random-([0-9])')[1]);
var newIndex = getRandomNumber();
// Ensure that we receive a different image than the current
while (newIndex === currentIndex) {
newIndex = getRandomNumber();
}
image.src = 'random-' + newIndex + '.png';
function getRandomNumber() {
return Math.floor(Math.random() * 6) + 1;
}
});
Changing the image's src
would trigger a network request for that image, but since we have the image cached by the service worker, there's no need to make the network request.
更改图像的src
会触发对该图像的网络请求,但是由于我们已将图像由服务工作者缓存,因此无需发出网络请求。
This recipe covers probably the most simple of offline cases: caching required static files for offline use.
本食谱可能涵盖最简单的脱机情况:缓存所需的静态文件以供脱机使用。
食谱: 离线后备 (Recipe: Offline Fallback)
This recipe follows another simple use case: fetch a page via AJAX but respond with another cached HTML resource (offline.html
) if the request fails.
此食谱遵循另一个简单的用例:通过AJAX提取页面,但是如果请求失败,则使用另一个缓存HTML资源( offline.html
)进行响应。
服务人员 (The service worker)
The install
step of the service worker fetches the offline.html
file and places it into a cache called offline
:
服务工作者的install
步骤将获取offline.html
文件并将其放入名为offline
的缓存中:
self.addEventListener('install', function(event) {
// Put `offline.html` page into cache
var offlineRequest = new Request('offline.html');
event.waitUntil(
fetch(offlineRequest).then(function(response) {
return caches.open('offline').then(function(cache) {
return cache.put(offlineRequest, response);
});
})
);
});
If that request fails the service worker won't register because the promise is rejected.
如果该请求失败,那么服务工作人员将不会注册,因为承诺被拒绝了。
The fetch
listener listens for a request for the page and, upon failure, responds with the offline.html
file we cached during the event registration:
fetch
侦听器侦听对该页面的请求,并在失败时以事件注册期间缓存的offline.html
文件作为响应:
self.addEventListener('fetch', function(event) {
// Only fall back for HTML documents.
var request = event.request;
// && request.headers.get('accept').includes('text/html')
if (request.method === 'GET') {
// `fetch()` will use the cache when possible, to this examples
// depends on cache-busting URL parameter to avoid the cache.
event.respondWith(
fetch(request).catch(function(error) {
// `fetch()` throws an exception when the server is unreachable but not
// for valid HTTP responses, even `4xx` or `5xx` range.
return caches.open('offline').then(function(cache) {
return cache.match('offline.html');
});
})
);
}
// Any other handlers come here. Without calls to `event.respondWith()` the
// request will be handled without the ServiceWorker.
});
Notice we use catch
to detect if the request has failed and that therefore we should respond with offline.html
content.
注意,我们使用catch
来检测请求是否失败,因此我们应该使用offline.html
内容进行响应。
服务人员注册 (Service Worker Registration)
A service worker needs to be registered only once. This example shows how to bypass registration if it's already been done by checking the presence of the navigator.serviceWorker.controller
property; if the controller
property doesn't exist, we move on to registering the service worker.
服务人员只需注册一次。 这个例子展示了如何通过检查navigator.serviceWorker.controller
属性的存在来绕过注册(如果已经完成的话)。 如果controller
属性不存在,我们将继续注册服务工作者。
if (navigator.serviceWorker.controller) {
// A ServiceWorker controls the site on load and therefor can handle offline
// fallbacks.
console.log('DEBUG: serviceWorker.controller is truthy');
debug(navigator.serviceWorker.controller.scriptURL + ' (onload)', 'controller');
}
else {
// Register the ServiceWorker
console.log('DEBUG: serviceWorker.controller is falsy');
navigator.serviceWorker.register('service-worker.js', {
scope: './'
}).then(function(reg) {
debug(reg.scope, 'register');
});
}
With the service worker confirmed as registered, you can test the recipe (and trigger the new page request) by clicking the "refresh" link: (which then triggers a page refresh with a cache-busting parameter):
在确认服务工作者已注册后,您可以通过单击“刷新”链接来测试配方(并触发新页面请求):(然后使用缓存清除参数触发页面刷新):
// The refresh link needs a cache-busting URL parameter
document.querySelector('#refresh').search = Date.now();
Providing the user an offline message instead of allowing the browser to show its own (sometimes ugly) message is an excellent way of keeping a dialog with the user about why the app isn't available while they're offline!
向用户提供脱机消息而不是允许浏览器显示其自身的消息(有时是丑陋的)是与用户保持对话的原因,这是为什么他们脱机时无法使用该应用程序的好方法!
下线! (Go offline!)
Service workers have moved offline experience and control into a powerful new space. Today you can use the Service Worker API in Chrome and Firefox Developer Edition. Many websites are using service workers today as you can see for yourself by going to about:serviceworkers
in Firefox Developer Edition; you'll see a listing of installed service workers from websites you've visited!
服务人员将离线体验和控制转移到一个强大的新空间中。 今天,您可以在Chrome和Firefox Developer Edition中使用Service Worker API。 如今,许多网站都在使用服务工作者,您可以通过以下网站亲自about:serviceworkers
Firefox开发人员版中的服务工作者; 您会从访问过的网站上看到已安装的服务人员的列表!
The Service Worker Cookbook is full of excellent, practical recipes and we continue to add more. Keep an eye out for the next post in this series, At Your Service for More than Just appcache, where you'll learn about using the Service Worker API for more than just offline purposes.
《 服务人员食谱》中包含许多实用的优秀食谱,我们将继续增加更多内容。 请密切注意本系列的下一篇文章,“ 为您提供的不仅仅是服务缓存” appcache ,在这里您将学到如何使用Service Worker API来实现不仅仅是离线目的。
翻译自: https://davidwalsh.name/offline-recipes-service-workers
菜谱闹钟