在 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)以避免缓存问题。

相关推荐
好_快3 分钟前
Lodash源码阅读-take
前端·javascript·源码阅读
好_快3 分钟前
Lodash源码阅读-takeRight
前端·javascript·源码阅读
好_快5 分钟前
Lodash源码阅读-takeRightWhile
前端·javascript·源码阅读
烂蜻蜓6 分钟前
在 HTML5 中使用 MathML 展示数学公式
前端·html·html5
好_快8 分钟前
Lodash源码阅读-takeWhile
前端·javascript·源码阅读
恋猫de小郭1 小时前
Android Studio Cloud 正式上线,不只是 Android,随时随地改 bug
android·前端·flutter
清岚_lxn6 小时前
原生SSE实现AI智能问答+Vue3前端打字机流效果
前端·javascript·人工智能·vue·ai问答
ZoeLandia6 小时前
Element UI 设置 el-table-column 宽度 width 为百分比无效
前端·ui·element-ui
橘子味的冰淇淋~7 小时前
解决 vite.config.ts 引入scss 预处理报错
前端·vue·scss
小小小小宇8 小时前
V8 引擎垃圾回收机制详解
前端