JavaScript 中的 Service Worker 简介

2025-05-28

JavaScript 中的 Service Worker 简介

这篇文章最初发表在attacomsian.com/blog上。


Service Worker 是渐进式 Web 应用的核心部分,它支持缓存资源、Web 推送通知等功能,从而打造高效的离线体验。Service Worker 充当 Web 应用、浏览器和网络之间的代理,允许开发者拦截和缓存网络请求,并根据网络可用性采取适当的措施。

Service Worker 在单独的线程上运行,因此是非阻塞的。这也意味着它无法访问DOM以及主 JavaScript 线程中可用的其他 API,例如 Cookie、XHR、Web 存储 API(本地存储和会话存储)等。由于 Service Worker 被设计为完全异步的,因此它们大量使用Promise来等待网络请求的响应。

出于安全考虑,Service Worker 仅通过 HTTPS 运行,并且无法在隐私浏览模式下使用。不过,在发出位置请求时无需安全连接(测试时足够)。

浏览器支持

Service Worker 是一个相对较新的 API,目前只有现代 Web 浏览器支持。因此,我们首先需要检查浏览器是否支持该 API:

if('serviceWorker' in navigator) {
    // Supported 😍
} else {
    // Not supported 😥
}

服务工作者注册

在开始缓存资源或拦截网络请求之前,我们必须在浏览器中安装一个 Service Worker。由于 Service Worker 本质上是一个 JavaScript 文件,因此可以通过指定文件路径来注册。该文件必须可通过网络访问,并且应该只包含 Service Worker 代码。

您应该等到页面加载完毕,然后将服务工作者文件路径传递给navigator.serviceWorker.register()方法:

window.addEventListener('load', () => {
    if ('serviceWorker' in navigator) {
        // register service worker
        navigator.serviceWorker.register('/sw-worker.js').then(
            () => {
                console.log('SW registration succesful 😍');
            },
            err => {
                console.error('SW registration failed 😠', err)
            });
    } else {
        // Not supported 😥
    }
});

您可以在每次页面加载时运行上述代码,不会出现任何问题;浏览器将判断服务工作线程是否已安装并进行相应处理。

Service Worker 生命周期

注册生命周期包括三个步骤:

  1. 下载
  2. 安装
  3. 激活

当用户首次访问您的网站时,系统会立即下载 Service Worker 文件并尝试安装。如果安装成功,则 Service Worker 会被激活。Service Worker 文件中的任何功能只有在用户访问其他页面或刷新当前页面后才会可用。

浏览器事件

一旦 Service Worker 安装并激活,它就可以开始拦截网络请求并缓存资源。这可以通过在 Service Worker 文件中监听浏览器发出的事件来实现。浏览器会发出以下事件:

  • install在安装服务工作者时发出。
  • activate在 Service Worker 成功注册并安装后发送。此事件可用于在安装新版本之前删除过期的缓存资源。
  • fetch每当网页请求网络资源时,都会触发此事件。请求的资源可以是任何内容:新的 HTML 文档、图片、JSON API、样式表或 JavaScript 文件,只要是远程位置上可用的资源即可。
  • push当收到新的推送通知时,Push API 会发送此事件。您可以使用此事件向用户显示通知。
  • sync当浏览器在连接丢失后检测到网络可用性时调用。

提供缓存资源

我们可以在服务工作者安装时监听install事件,以缓存当我们脱离网络时需要为页面提供服务的特定资源:

const CACHE_NAME = 'site-name-cache';

self.addEventListener('install', event => {
    event.waitUntil(
        caches
            .open(CACHE_NAME)
            .then(cache =>
                cache.addAll([
                    'favicon.ico',
                    'projects.json',
                    'style.css',
                    'index.js',
                    'https://fonts.googleapis.com/css?family=Open+Sans:400,700'
                ])
            )
    );
});

上述示例代码使用 Cache API 将资源存储在名为 的缓存中site-name-cache

self是一个只读的全局属性,服务人员可以使用它来访问自己。

现在让我们监听一个fetch事件来检查所请求的资源是否已经存储在缓存中,如果找到则返回它:

// ...
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            if (response) {
                //found cached resource
                return response;
            }
            return fetch(event.request);
        })
    );
});

我们查找由request属性标识的资源的缓存条目,如果未找到,则发出获取请求来获取它。如果您也想缓存新的请求,可以通过处理获取请求的响应并将其添加到缓存中来实现,如下所示:

//...
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            if (response) {
                //found cached resource
                return response;
            }

            // get resource and add it to cache
            return fetch(event.request)
                .then(response => {
                    // check if the response is valid
                    if (!response.ok) {
                        return response;
                    }

                    // clone the response
                    const newResponse = response.clone();

                    // add it to cache
                    caches.open(CACHE_NAME)
                        .then(cache =>
                            cache.put(event.request, newResponse)
                        );

                    // return response
                    return response;
                });
        })
    );
});

Service Worker 更新

Service Worker 安装后会持续运行,直到用户移除或更新。要更新 Service Worker,您只需在服务器上上传新版本的 Service Worker 文件即可。当用户访问您的网站时,浏览器会自动检测文件更改(即使只有一个字节也足够),并安装新版本。

与首次安装一样,新的服务工作者功能只有当用户导航到另一个页面或刷新当前页面时才可用。

我们可以做的一件事是监听activate事件并删除旧的缓存资源。以下代码通过循环遍历所有缓存并删除与缓存名称匹配的缓存来实现此目的:

// ...
self.addEventListener('activate', event => {
    event.waitUntil(
        caches.keys().then(keys => {
            return Promise.all(
                keys.map(cache => {
                    if (cache === CACHE_NAME) {
                        return caches.delete(cache);
                    }
                })
            );
        })
    );
});

以上就是 Service Worker 的介绍。如果您想了解更多信息,请查看ServiceWorker Cookbook — 一系列在现代网站中使用 Service Worker 的实用示例。


✌️ 我撰写有关现代 JavaScript、Node.js、Spring Boot以及所有Web 开发方面的文章。订阅我的新闻通讯,每周获取 Web 开发教程和专业技巧。


喜欢这篇文章吗? 请在 Twitter 上关注 @attacomsian。你也可以在LinkedInDEV上关注我。

文章来源:https://dev.to/attacomsian/introduction-to-server-workers-in-javascript-1epa
PREV
使用 Vanilla JavaScript 构建一个简单的街机游戏 - DOM 操作🚀 我们将涵盖解决它的过程 1. 布局样式 2. JavaScript 完成的程序
NEXT
10 个你可能想要使用的鲜为人知的 Web API 一些众所周知的 Web API tl;dr 关于 Web API 的重点 鲜为人知但有用的 Web API ...还有更多 进一步阅读