You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在AMP文件中使用Service Worker缓存CDN文件及解决跨域问题

解决Service Worker缓存CDN跨域资源+AMP适配的完整方案

这个问题我之前帮不少开发者踩过坑,核心就是跨域资源的CORS配置+Service Worker请求模式的正确设置,再结合AMP的特殊要求,咱们一步步来解决:

一、先搞定CDN的CORS配置(最关键的前提)

Service Worker是在你的根域名下运行的脚本,请求CDN资源属于跨域操作,浏览器会严格校验CORS响应头。你必须在CDN的后台配置以下响应头:

  • Access-Control-Allow-Origin: https://your-root-domain.com(替换成你的实际根域名,别用*,虽然省事但不安全)
  • Access-Control-Allow-Methods: GET, HEAD(静态资源只需要这两个请求方法)
  • Access-Control-Allow-Headers: Cache-Control(确保缓存相关的请求头能正常传递)

不同CDN的配置位置不一样:比如Cloudflare在「规则→转换规则→修改响应头」里加;阿里云CDN在「域名管理→HTTP头设置」里添加。配置完后,可以用浏览器DevTools的Network面板查看CDN资源的响应头,确认这些头存在。

二、Service Worker(swAmp.js)的缓存逻辑

核心是在fetch事件中处理CDN资源时,必须用mode: 'cors'发起请求,同时实现缓存策略。下面是完整的示例代码,包含缓存预加载、请求拦截、缓存更新:

// 定义缓存名称和需要缓存的CDN资源列表
const CACHE_NAME = 'amp-cdn-cache-v1';
const CDN_ASSETS = [
  'https://your-cdn-domain.com/css/amp-style.css',
  'https://your-cdn-domain.com/js/amp-script.js',
  'https://your-cdn-domain.com/images/hero.jpg'
];

// 安装阶段:预缓存CDN资源
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => {
        // 注意:请求CDN资源时必须加mode: 'cors'
        return Promise.all(
          CDN_ASSETS.map((url) => {
            return fetch(url, { mode: 'cors' })
              .then((response) => {
                if (response.ok) {
                  return cache.put(url, response);
                }
                throw new Error(`Failed to cache ${url}`);
              });
          })
        );
      })
      .then(() => self.skipWaiting()) // 强制激活新的Service Worker
  );
});

// 激活阶段:清理旧缓存
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    }).then(() => self.clients.claim()) // 控制所有打开的页面
  );
});

// fetch事件:拦截请求,优先返回缓存,缓存不存在则请求网络并更新缓存
self.addEventListener('fetch', (event) => {
  // 只处理CDN资源的请求(可以根据URL前缀判断)
  const isCDNAsset = CDN_ASSETS.some((url) => event.request.url.startsWith(new URL(url).origin));
  
  if (isCDNAsset) {
    event.respondWith(
      caches.match(event.request)
        .then((cachedResponse) => {
          // 如果缓存有,先返回缓存,同时后台发起网络请求更新缓存
          if (cachedResponse) {
            // 后台更新缓存,不阻塞当前响应
            fetch(event.request, { mode: 'cors' })
              .then((networkResponse) => {
                if (networkResponse.ok) {
                  caches.open(CACHE_NAME).then((cache) => {
                    cache.put(event.request, networkResponse);
                  });
                }
              });
            return cachedResponse;
          }

          // 缓存不存在,发起网络请求,成功后存入缓存
          return fetch(event.request, { mode: 'cors' })
            .then((networkResponse) => {
              if (!networkResponse.ok) {
                return new Response('Resource not found', { status: 404 });
              }
              // 克隆响应,因为响应只能被读取一次
              const responseToCache = networkResponse.clone();
              caches.open(CACHE_NAME).then((cache) => {
                cache.put(event.request, responseToCache);
              });
              return networkResponse;
            });
        })
    );
  }
});

三、AMP页面中注册Service Worker

AMP不允许直接用原生JS注册Service Worker,必须使用官方的<amp-install-serviceworker>组件,具体代码如下:

<!doctype html>
<html ⚡>
<head>
  <meta charset="utf-8">
  <title>AMP + Service Worker CDN Cache</title>
  <link rel="canonical" href="https://your-root-domain.com/amp-page.html">
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
  <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
  <script async src="https://cdn.ampproject.org/v0.js"></script>
  <!-- 引入AMP Service Worker组件 -->
  <script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>
</head>
<body>
  <!-- 页面内容 -->
  <h1>AMP Page with CDN Caching via Service Worker</h1>
  <amp-img src="https://your-cdn-domain.com/images/hero.jpg" width="800" height="450" layout="responsive" alt="Hero Image"></amp-img>
  
  <!-- 注册Service Worker -->
  <amp-install-serviceworker
    src="/swAmp.js"
    data-iframe-src="/install-sw.html"
    layout="nodisplay">
  </amp-install-serviceworker>
</body>
</html>

补充说明:install-sw.html的作用

AMP的amp-install-serviceworker需要一个iframe来完成注册(因为AMP页面的沙箱限制),这个文件内容很简单,只需要注册Service Worker即可:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Install Service Worker</title>
</head>
<body>
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/swAmp.js')
        .then((registration) => {
          console.log('Service Worker registered:', registration.scope);
        })
        .catch((error) => {
          console.error('Service Worker registration failed:', error);
        });
    }
  </script>
</body>
</html>

四、缓存后如何提供文件?

在上面的Service Worker代码中,fetch事件的逻辑已经处理了:

  1. 当用户请求CDN资源时,先检查缓存中是否存在匹配的响应
  2. 如果有,直接返回缓存的响应,同时后台发起网络请求更新缓存(保证用户拿到最新资源)
  3. 如果缓存中没有,就发起跨域请求获取资源,成功后存入缓存,再返回给用户

常见坑点提醒

  • 一定要确保CDN的CORS头配置正确,否则即使Service Worker加了mode: 'cors'也会报错
  • AMP页面必须使用<amp-install-serviceworker>组件,不能用原生JS注册,否则会被AMP的校验拦截
  • 缓存名称要加版本号(比如amp-cdn-cache-v1),这样更新缓存时只需要修改版本号,Service Worker会自动清理旧缓存

内容的提问来源于stack exchange,提问作者Crio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:11:50