在 React 项目中使用 TypeScript 编写 Service Worker 的完整方案

在 React 项目中使用 TypeScript 编写 Service Worker 的完整方案如下:


📝 实现步骤

1. 项目初始化

确保你的 React 项目已支持 TypeScript(通过 create-react-app 或自定义配置)

bash 复制代码
npx create-react-app my-app --template typescript
cd my-app
npm install @types/service_worker_api --save-dev

2. 创建 Service Worker 文件

新建 src/sw.ts

typescript 复制代码
// src/sw.ts
interface DomainUpdateEvent extends ExtendableEvent {
  detail: {
    domains: string[];
  };
}

const ALLOWED_DOMAINS: string[] = ['api.example.com'];

self.addEventListener('fetch', (event) => {
  const requestURL = new URL(event.request.url);
  const requestedDomain = requestURL.hostname;

  if (ALLOWED_DOMAINS.includes(requestedDomain)) {
    console.log(`[SW] 允许请求: ${event.request.url}`);
    
    // 示例:添加请求头
    const modifiedHeaders = new Headers(event.request.headers);
    modifiedHeaders.append('X-SW-Optimized', 'true');
    
    event.respondWith(
      fetch(event.request, {
        headers: modifiedHeaders
      })
    );
  }
});

// 动态更新白名单
self.addEventListener('message', (e: DomainUpdateEvent) => {
  if (e.detail?.domains) {
    ALLOWED_DOMAINS.push(...e.detail.domains);
    console.log('[SW] 更新白名单:', ALLOWED_DOMAINS);
  }
});

3. 配置编译输出

修改 tsconfig.json

json 复制代码
{
  "compilerOptions": {
    "outDir": "build/sw",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/sw.ts"]
}

4. 注册 Service Worker

src/App.tsx 中注册:

typescript 复制代码
// src/App.tsx
import { useEffect } from 'react';

function App() {
  useEffect(() => {
    if ('serviceWorker' in navigator) {
      const registerSW = async () => {
        try {
          const registration = await navigator.serviceWorker.register(
            process.env.PUBLIC_URL + '/sw.js'
          );
          console.log('SW 注册成功:', registration);
        } catch (err) {
          console.error('SW 注册失败:', err);
        }
      };

      // 开发环境热更新
      if (process.env.NODE_ENV === 'development') {
        const updateSW = () => {
          navigator.serviceWorker.getRegistrations().then(registrations => {
            registrations.forEach(registration => registration.update());
          });
        };
        setInterval(updateSW, 60000); // 每分钟检查更新
      }

      registerSW();
    }
  }, []);

  return <div>App Content</div>;
}

export default App;

5. 配置构建脚本

package.json 中添加构建命令:

json 复制代码
{
  "scripts": {
    "build:sw": "tsc --project tsconfig.json",
    "build": "npm run build:sw && react-scripts build",
    "start": "react-scripts start && npm run build:sw -- --watch"
  }
}

🛠️ 高级技巧

  1. 缓存策略集成

    添加 Workbox 支持:

    bash 复制代码
    npm install workbox-build

    创建 workbox-config.js

    javascript 复制代码
    module.exports = {
      globDirectory: 'build/',
      globPatterns: ['**/*.{js,css,html,png}'],
      swDest: 'build/sw.js',
      clientsClaim: true,
      skipWaiting: true
    };

    修改构建脚本:

    json 复制代码
    {
      "scripts": {
        "build": "react-scripts build && workbox injectManifest workbox-config.js"
      }
    }
  2. 动态配置更新

    通过网页发送消息更新配置:

    typescript 复制代码
    // 在组件中
    const updateSWConfig = () => {
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.controller?.postMessage({
          type: 'UPDATE_DOMAINS',
          domains: ['new-domain.com']
        });
      }
    };
  3. 调试支持

    src/sw.ts 中添加调试开关:

    typescript 复制代码
    const DEBUG_MODE = process.env.NODE_ENV === 'development';
    if (DEBUG_MODE) {
      self.addEventListener('activate', (event) => {
        console.log('[SW] 激活事件:', event);
      });
    }

⚠️ 注意事项

  1. 作用域限制

    确保 Service Worker 文件位于网站根目录或子目录,且作用域能覆盖目标请求路径。

  2. HTTPS 要求

    生产环境必须使用 HTTPS(本地开发可通过 localhost 使用)。

  3. 更新机制

    修改 Service Worker 后需重新编译并刷新页面,或调用 registration.update()

  4. 文件哈希

    建议为编译后的 Service Worker 文件添加哈希值(如 sw.[hash].js)以避免缓存问题。

相关推荐
前端之虎陈随易1 小时前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·vue.js·人工智能·typescript·node.js
一路向北he1 小时前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
kyriewen2 小时前
豆包和千问同时关了智能体,我用它们搭的 3 个自动化全废了——迁移方案整理
前端·javascript·ai编程
前端一小卒2 小时前
我用 TypeScript 从零手写了一个 Claude Code,然后发现它的核心只有 30 行
前端·agent
大圣编程3 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang3 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆4 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜5 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞6 小时前
异步HttpModule的实现方式
java·服务器·前端
YFF菲菲兔7 小时前
其他 Hooks 解析
react.js