首页 / 文章

使用服务工作员为网页应用启用离线支持

服务工作者与缓存API能让网站瞬间加载,并在离线状态下继续运行。包括编写服务工作者、注册它以及选择缓存策略。

发布者:REACTAPP.TOP阅读约 5 分钟1098 词

来源: ☕ JavaScript Hack That Makes Any Website Work Offline

想象一下,有人在火车上浏览你的网站,信号每隔几秒就会时好时坏。在大多数网站上,这种不稳定的连接意味着一切都会中断——浏览器会显示离线错误提示。如果你的网站能够完全避开这个问题并继续正常运行,那会怎样?

这并非什么未来的实验性功能。利用一种名为Service Worker的浏览器技术,现在就可以实现这一目标。Service Worker就像一个可编程的中间人,位于你的网络应用与网络之间。它可以在请求到达服务器之前就截获这些请求,并利用本地存储的文件自行响应,从而让网站在完全没有网络连接的情况下依然能够正常运行。

这个技巧不仅提供了实用的备用方案,还能显著加快加载速度,让应用在网络状况不佳时更具稳定性。尽管听起来较为复杂,但实际上实现起来相当简单。

实现此技巧的关键:服务工作线程与缓存API的结合

有两个浏览器功能共同作用使得这成为可能:

  1. 服务工作线程:一种独立于页面主线程运行的后台脚本,可充当网络层的替代品。
  2. 缓存API:浏览器内置的接口,允许你以编程方式存储和获取资源,功能类似键值存储。你可以将网站所需的HTML、CSS、JavaScript、图片及其他资源存储在其中。

大致流程如下:当有人首次通过互联网访问您的网站时,服务工作员会自动安装并将重要文件下载到缓存中。此后,页面发出的所有网络请求都会先经过服务工作员处理。当访问者失去网络连接时,工作员会使用已缓存的资源来响应,而不会尝试(也无需尝试)连接服务器,因此页面仍能几乎立即显示。

第一步:编写服务工作员脚本

首先在项目的根目录下创建一个名为 sw.js 的文件。该脚本负责安装缓存并拦截后续的请求。

// sw.js

const CACHE_NAME = 'my-website-cache-v1';
const urlsToCache = [
  '/',
  '/index.html',
  '/styles.css',
  '/script.js',
  '/images/logo.png'
];

// The 'install' event is fired when the service worker is first installed.
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

// The 'fetch' event is fired for every network request.
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // If the request is in the cache, serve the cached version.
        if (response) {
          console.log('Serving from cache:', event.request.url);
          return response;
        }

        // Otherwise, fetch from the network.
        console.log('Fetching from network:', event.request.url);
        return fetch(event.request);
      })
  );
});

此文件主要监听两个生命周期事件:

  • install:用于打开指定的缓存,并在其中填入那些即便在没有网络连接时也需要可用的文件列表。
  • fetch:每当页面发起请求时都会触发此事件。工作线程会检查缓存中是否存在对应的响应;如果存在,则返回缓存的副本,否则则像平常一样将请求发送到网络。

第2步:从您的应用中注册工作线程

一旦工作线程脚本准备就绪,仍需告知浏览器使用它。请将以下代码片段添加到您的主脚本文件中,或直接嵌入HTML中的<script>标签内。

// Register the service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('Service Worker registered successfully:', registration);
      })
      .catch(error => {
        console.error('Service Worker registration failed:', error);
      });
  });
}

该代码块首先检查当前浏览器是否支持 Service Worker API,如果支持,则会注册你的 sw.js 文件。注意注册时使用的路径:/sw.js 必须位于网站的根目录下,因为 Service Worker 只能控制其注册所在目录下的页面。

验证其功能

要查看实际效果,可在支持 Service Workers 的浏览器中打开你的网站——Chrome、Firefox 和 Edge 均符合要求。

  • 打开浏览器的开发者工具(F12 或 Ctrl+Shift+I)。
  • 切换到 Application 面板。
  • 在侧边栏中找到 Service Workers,你的 sw.js 文件应会显示为处于活动状态。
  • 点击旁边的Cache Storage,确认您命名的缓存确实存在且包含您列出的文件。
  • 在 Service Worker 部分顶部的“Offline”复选框前打勾。
  • 重新加载页面。
  • 如果一切设置正确,即使没有网络连接,页面也能立即加载。开发工具中的控制台输出会显示请求已被拦截,并直接从缓存而非网络获取响应。

    进一步拓展:其他缓存策略

    上述方法被称为先缓存、再联网的策略——非常适合那些很少变化的资源,如静态图片或打包后的脚本。但 Service Workers 并不局限于这种单一模式,还有其他几种常见策略:

    • 网络优先,缓存兜底:适用于内容频繁变化且需要保持最新状态的场景。工作进程会首先尝试从网络获取数据,只有当请求因连接不良等原因失败时才会转而使用缓存中的内容。
    • 在验证期间仍使用旧数据:工作进程会立即利用已缓存的资料快速响应,同时在后台发起网络请求,以便在下一次页面加载时更新缓存。这样既能实现即时响应,又能很快获得最新内容。

    掌握这种基本的离线处理模式,还能为你构建完整的渐进式 Web 应用奠定基础,这类应用将离线功能与推送通知以及像原生应用一样安装到设备主屏幕的功能相结合。

    将此模式引入项目仅需少量投入,却能带来显著收益:这样一来,即使连接中断,也不会彻底破坏用户体验,用户几乎察觉不到这种干扰。