7.3 离线应用与Application Cache(已废弃)

7.3 离线应用与Application Cache(已废弃)

重要提示:Application Cache API 已被现代浏览器废弃,本节内容仅作为历史参考和技术考古。实际开发中应使用Service Worker替代。

Application Cache基本原理

1. 缓存机制概述

Application Cache(AppCache)通过清单文件(manifest)定义需要缓存的资源,工作原理如下:

  1. 浏览器解析清单文件并下载指定资源
  2. 创建应用缓存副本供离线使用
  3. 后续访问优先使用缓存资源
  4. 清单文件变更时触发更新

2. 基本使用方式

HTML启用AppCache

<!DOCTYPE html>
<html manifest="example.appcache">
<!-- 页面内容 -->
</html>

清单文件示例 (example.appcache):

CACHE MANIFEST
# v1.0.0 - 2023-07-20
# 这是注释,修改注释会触发更新

CACHE:
/css/style.css
/js/app.js
/images/logo.png
/favicon.ico

NETWORK:
/api/
/login

FALLBACK:
/offline.html

Application Cache详细解析

1. 清单文件结构

段名 作用
CACHE MANIFEST 必须的第一行标识
CACHE: 列举需要缓存的资源(默认段,可省略)
NETWORK: 必须在线访问的白名单资源(使用*表示除CACHE外的所有资源都需要网络请求)
FALLBACK: 定义离线时的后备页面(格式:在线URL 后备URL

2. 缓存更新流程

  1. 首次访问

    sequenceDiagram
    浏览器->>服务器: 请求HTML文档
    服务器->>浏览器: 返回HTML+manifest链接
    浏览器->>服务器: 请求manifest文件
    服务器->>浏览器: 返回manifest内容
    浏览器->>服务器: 并行请求所有CACHE资源
    服务器->>浏览器: 返回资源内容
    浏览器->>本地: 创建应用缓存副本
    
  2. 后续更新

    sequenceDiagram
    浏览器->>服务器: 检查manifest是否更新
    alt 文件已修改
      服务器->>浏览器: 返回新manifest
      浏览器->>服务器: 重新获取所有资源
      浏览器->>本地: 创建新缓存(旧缓存仍可用)
      浏览器->>页面: 触发updateready事件
    else 文件未修改
      服务器->>浏览器: 304 Not Modified
      浏览器->>本地: 使用现有缓存
    end
    

完整示例代码

1. 离线应用实现

HTML文件

<!DOCTYPE html>
<html manifest="clock.appcache">
<head>
  <title>离线时钟</title>
  <link rel="stylesheet" href="css/clock.css">
</head>
<body>
  <h1>离线时钟</h1>
  <div id="time"></div>
  <div id="status"></div>
  
  <script src="js/clock.js"></script>
  <script>
    // 缓存状态检测
    function handleCacheEvent(e) {
      const status = document.getElementById('status');
      
      switch(e.type) {
        case 'checking':
          status.textContent = '检查更新...';
          break;
        case 'downloading':
          status.textContent = '下载新缓存...';
          break;
        case 'updateready':
          status.textContent = '更新已就绪,请刷新页面';
          break;
        case 'cached':
          status.textContent = '已缓存,可离线使用';
          break;
        case 'error':
          status.textContent = '缓存错误: ' + e.message;
          break;
      }
    }
    
    window.applicationCache.addEventListener('checking', handleCacheEvent);
    window.applicationCache.addEventListener('downloading', handleCacheEvent);
    window.applicationCache.addEventListener('updateready', handleCacheEvent);
    window.applicationCache.addEventListener('cached', handleCacheEvent);
    window.applicationCache.addEventListener('error', handleCacheEvent);
  </script>
</body>
</html>

clock.appcache

CACHE MANIFEST
# v1.1.0

CACHE:
css/clock.css
js/clock.js
images/clock-icon.png

NETWORK:
/api/

FALLBACK:
/ /offline.html

2. JavaScript控制接口

// 检查缓存状态
const appCache = window.applicationCache;

// 强制更新
function updateCache() {
  appCache.update();
}

// 切换至新缓存
if (appCache.status === appCache.UPDATEREADY) {
  appCache.swapCache();
  window.location.reload();
}

// 缓存状态常量
console.log('UNCACHED:', appCache.UNCACHED);     // 0
console.log('IDLE:', appCache.IDLE);             // 1
console.log('CHECKING:', appCache.CHECKING);     // 2
console.log('DOWNLOADING:', appCache.DOWNLOADING); // 3
console.log('UPDATEREADY:', appCache.UPDATEREADY); // 4
console.log('OBSOLETE:', appCache.OBSOLETE);     // 5

Application Cache的问题与缺陷

1. 主要设计缺陷

  1. 缓存更新不可靠

    • 清单文件任何变化(包括注释)都会触发全量下载
    • 没有部分更新机制
  2. 缓存污染风险

    • 错误配置可能导致动态内容被缓存
    • 难以清理错误缓存
  3. 白名单机制不灵活

    • NETWORK段规则过于简单
    • 无法实现复杂缓存策略
  4. 错误处理困难

    • 单个资源加载失败会导致整个缓存失败
    • 缺乏细粒度控制

2. 典型问题场景

案例1:缓存动态内容

CACHE MANIFEST
# 错误地将API响应缓存
CACHE:
/api/user

结果:用户永远看到旧的用户数据,即使服务器数据已更新

案例2:忽略必要网络资源

NETWORK:
*

结果:所有未明确缓存的资源都被允许网络访问,可能泄露敏感数据

迁移到Service Worker

1. Service Worker优势对比

特性 Application Cache Service Worker
缓存控制 清单文件定义 编程式精细控制
更新机制 全量更新 增量更新
请求拦截 不支持 完全控制网络请求
调试支持 有限 Chrome DevTools完整支持
生命周期 简单 明确的生命周期管理
兼容性 已废弃 现代浏览器全面支持

2. 迁移示例

原AppCache清单

CACHE MANIFEST
# v1.0.0
CACHE:
/css/styles.css
/js/app.js
/img/logo.png

NETWORK:
/api/

FALLBACK:
/ /offline.html

等效Service Worker实现 (sw.js):

const CACHE_NAME = 'my-app-v1';
const OFFLINE_URL = '/offline.html';
const PRECACHE_URLS = [
  '/',
  '/css/styles.css',
  '/js/app.js',
  '/img/logo.png'
];

// 安装阶段 - 预缓存关键资源
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(PRECACHE_URLS))
      .then(() => self.skipWaiting())
  );
});

// 激活阶段 - 清理旧缓存
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cache => {
          if (cache !== CACHE_NAME) {
            return caches.delete(cache);
          }
        })
      );
    }).then(() => self.clients.claim())
  );
});

// 请求拦截
self.addEventListener('fetch', event => {
  // API请求直接通过网络获取
  if (event.request.url.includes('/api/')) {
    event.respondWith(fetch(event.request));
    return;
  }
  
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
      .catch(() => caches.match(OFFLINE_URL))
  );
});

历史版本检测与降级

// 检测浏览器支持情况
function checkOfflineSupport() {
  if ('serviceWorker' in navigator) {
    return 'serviceWorker';
  } else if ('applicationCache' in window) {
    console.warn('Application Cache is deprecated. Migrate to Service Worker.');
    return 'appCache';
  } else {
    return 'none';
  }
}

// 根据支持情况初始化
const support = checkOfflineSupport();

if (support === 'serviceWorker') {
  navigator.serviceWorker.register('/sw.js')
    .then(registration => {
      console.log('ServiceWorker 注册成功');
    });
} else if (support === 'appCache') {
  // 仅作演示,实际项目不应使用
  window.addEventListener('load', function() {
    const appCache = window.applicationCache;
    appCache.addEventListener('error', function(e) {
      console.error('AppCache 错误:', e.message);
    });
  });
} else {
  console.warn('当前浏览器不支持离线功能');
}

总结:为什么被废弃

  1. 僵化的缓存策略:无法适应现代Web应用的复杂需求
  2. 更新机制缺陷:导致不必要的带宽消耗
  3. 错误处理不足:失败时缺乏恢复手段
  4. 与现代API冲突:难以与Service Worker等新技术协同工作
  5. 开发者体验差:调试困难,问题难以排查

迁移建议

  1. 现有使用AppCache的项目应尽快迁移到Service Worker
  2. 新项目不应再使用Application Cache
  3. 学习现代的Workbox等工具库简化Service Worker开发

虽然Application Cache已经退出历史舞台,但理解其设计思想和缺陷,有助于我们更好地使用现代离线技术构建可靠的Web应用。

#前端开发 分享于 2025-05-20

【 内容由 AI 共享,不代表本站观点,请谨慎参考 】