面试题:HTML5 的离线储存怎么使用?它的工作原理是什么?

HTML5 的离线存储主要通过 Service Worker 技术实现(Application Cache 已被废弃)。它允许 Web 应用在无网络连接时依然可以访问,提供类似原生应用的体验。

下面详细介绍其使用方法工作原理


一、如何使用 HTML5 离线存储(Service Worker)

使用 Service Worker 实现离线存储是一个多步骤的过程,涉及三个主要文件:

1. 主页面 (index.html)

在主页面中注册 Service Worker。

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>离线应用示例</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <h1>欢迎使用离线应用</h1>
  <p>即使断开网络,您也可以继续使用本应用。</p>
  <script>
    // 确保在页面加载完成后注册 Service Worker
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/sw.js')
          .then((registration) => {
            console.log('Service Worker 注册成功:', registration.scope);
          })
          .catch((error) => {
            console.log('Service Worker 注册失败:', error);
          });
      });
    }
  </script>
</body>
</html>

2. Service Worker 脚本 (sw.js)

这是核心文件,必须放在应用的根目录或能被所有页面访问的路径。它控制缓存和网络请求。

// 定义缓存名称
const CACHE_NAME = 'my-app-v1';
// 需要缓存的资源列表
const urlsToCache = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js',
  '/images/logo.png',
  '/offline.html' // 离线时显示的页面
];

// 安装阶段:预缓存关键资源
self.addEventListener('install', (event) => {
  console.log('Service Worker 正在安装...');
  // waitUntil 确保安装过程在缓存完成前不会结束
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => {
        console.log('打开缓存');
        // addAll 会缓存所有资源,如果其中一个失败,整个安装失败
        return cache.addAll(urlsToCache);
      })
  );
});

// 激活阶段:清理旧缓存
self.addEventListener('activate', (event) => {
  console.log('Service Worker 激活中...');
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          // 删除旧版本的缓存
          if (cacheName !== CACHE_NAME) {
            console.log('删除旧缓存:', cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
  // 让新的 Service Worker 立即获得控制权
  return self.clients.claim();
});

// 网络请求拦截:实现离线策略
self.addEventListener('fetch', (event) => {
  console.log('拦截请求:', event.request.url);

  // 仅对同源请求应用离线策略
  if (event.request.url.startsWith(self.location.origin)) {
    event.respondWith(
      // 1. 先尝试从缓存中匹配
      caches.match(event.request)
        .then((cachedResponse) => {
          if (cachedResponse) {
            return cachedResponse; // 返回缓存
          }
          // 2. 缓存没有,发起网络请求
          return fetch(event.request).then((networkResponse) => {
            // 3. 将网络响应存入缓存(供下次使用)
            const responseToCache = networkResponse.clone();
            caches.open(CACHE_NAME).then((cache) => {
              cache.put(event.request, responseToCache);
            });
            return networkResponse;
          }).catch(() => {
            // 4. 网络请求失败(离线),返回离线页面
            return caches.match('/offline.html');
          })
        })
    );
  }
});

3. 离线页面 (offline.html) (可选但推荐)

当用户离线且请求的资源不在缓存中时,显示此页面。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>离线</title>
</head>
<body>
  <h1>您已离线</h1>
  <p>请检查您的网络连接。</p>
</body>
</html>

二、工作原理

Service Worker 的工作原理基于其生命周期网络代理能力。

1. 生命周期 (Lifecycle)

  1. 注册 (Registration)
    • 主页面 JavaScript 调用 navigator.serviceWorker.register('/sw.js')
    • 浏览器下载 sw.js 文件。
  2. 安装 (Install)
    • 下载成功后,触发 install 事件。
    • install 事件中,通过 caches.open()cache.addAll()urlsToCache 列表中的资源预先缓存到浏览器的 Cache Storage 中。
    • 如果缓存过程成功,Service Worker 进入“已安装”状态。
  3. 激活 (Activate)
    • 触发 activate 事件。
    • 在此阶段,通常清理旧版本的缓存(如示例中的 my-app-v1 之前的版本),避免缓存膨胀。
    • 激活后,Service Worker 开始拦截页面的网络请求。
  4. 控制 (Controlling)
    • 激活的 Service Worker 开始接管页面的 fetch 事件。

2. 缓存与加载机制

  • 首次在线访问
    1. 页面加载,注册 Service Worker。
    2. Service Worker 安装并缓存关键资源。
    3. 页面从网络加载资源(此时新缓存还未被使用)。
    4. Service Worker 激活。
  • 后续访问(在线或离线)
    1. 当页面发起网络请求时,Service Worker 的 fetch 事件被触发。
    2. 代码逻辑:
      • 优先缓存caches.match(event.request) 检查请求的资源是否在缓存中。
      • 命中缓存:如果找到,直接返回缓存的响应,极快
      • 缓存未命中:发起 fetch(event.request) 网络请求。
        • 网络成功:返回网络响应,并将其克隆后存入缓存(供未来使用)。
        • 网络失败(离线):捕获异常,返回预缓存的 offline.html 或其他降级资源。
  • 更新机制
    1. sw.js 文件内容发生变化(字节不同),浏览器会下载新版本。
    2. 新的 Service Worker 开始安装,但不会立即激活(旧的仍在控制页面)。
    3. 当所有使用旧 Service Worker 的页面都被关闭后,新的 Service Worker 才会激活,并在 activate 事件中清理旧缓存。

三、核心优势

  • 精细控制:开发者完全掌控缓存策略(缓存什么、何时缓存、如何更新)。
  • 增量更新:只更新变化的资源,无需全量下载。
  • 离线优先:可实现“先展示缓存,再后台更新”的最佳用户体验。
  • 强大功能:支持后台同步、消息推送等 PWA 特性。

四、注意事项

  • HTTPS 环境:Service Worker 要求网站必须通过 HTTPS 提供(本地开发 localhost 除外)。
  • 作用域register() 的路径决定了 Service Worker 能控制的页面范围。
  • 调试:可在浏览器开发者工具的 “Application” 或 “Service Workers” 面板中调试和管理。

总结

HTML5 离线存储通过 Service Worker 实现。开发者编写一个 JavaScript 脚本 (sw.js),在 install 阶段预缓存资源,在 fetch 事件中拦截网络请求,优先返回缓存内容,网络失败时返回离线页面。其工作原理基于 Service Worker 的安装、激活和网络代理生命周期,为 Web 应用提供了强大的离线能力。

THE END
喜欢就支持一下吧
点赞14 分享