Skip to content

迁移指南

从手写拦截器、axios-retry 等方案迁移到 request-guard。

🛡️ 从手写防重拦截器迁移

📦 旧方案:手动 inFlight 变量

javascript
const inFlight = new Set();

axios.interceptors.request.use((config) => {
  const key = config.url + JSON.stringify(config.data);
  if (inFlight.has(key)) {
    return Promise.reject(new Error('请勿重复提交'));
  }
  inFlight.add(key);
  return config;
});

axios.interceptors.response.use(
  (res) => { inFlight.delete(res.config.url + JSON.stringify(res.config.data)); return res; },
  (err) => { inFlight.delete(err.config.url + JSON.stringify(err.config.data)); return Promise.reject(err); }
);

✅ 迁移后

javascript
import { setupRequestGuard } from '@hydd/request-guard';

setupRequestGuard(axios, {
  notify: (payload) => Toast.show(payload.message),
  rules: [
    { method: 'post', duplicate: { strategy: 'ignore', message: '请勿重复提交' } }
  ]
});

// 删除原来的 inFlight 逻辑和两个拦截器

迁移要点

旧方案痛点request-guard 解决方式
key 生成手动拼字符串,容易漏字段compareFields 声明字段,稳定序列化 + 自动哈希
失败后忘记清 inFlight,导致后续请求永久被拦请求生命周期自动释放状态,无需手动清理
提示 toast 散落在拦截器里统一 notify 出口
无法区分"静默忽略"、"阻断"和"复用"ignore / block / reuse 三种策略可选,默认 ignore 避免触发业务 then/catch 副作用

⚠️ 从旧版 request-guard 默认 block 迁移

本次迭代后,duplicate 默认策略从 block 调整为 ignore。这会影响没有显式配置 strategy 的项目:

旧默认 block新默认 ignore
重复请求 reject RequestGuardBlockedError重复请求返回永远 pending 的 Promise
调用方 .catch() / .finally() 会执行调用方 .then() / .catch() / .finally() 都不会执行
适合需要显式错误分支的业务适合不希望重复调用触发成功/失败副作用的表单、下单、支付

如果你的业务依赖 .catch(RequestGuardBlockedError) 关闭 loading、重置按钮或展示提示,请显式保留旧行为:

javascript
setupRequestGuard(axios, {
  defaults: {
    duplicate: { strategy: 'block' }
  }
});

也可以只在特定请求上使用旧行为:

javascript
axios.post('/api/order/submit', data, {
  requestGuard: {
    duplicate: { strategy: 'block' }
  }
});

🔄 从 axios-retry 迁移

📦 旧方案

javascript
import axiosRetry from 'axios-retry';

axiosRetry(axios, {
  retries: 3,
  retryDelay: axiosRetry.exponentialDelay,
  retryCondition: (error) => error.response?.status >= 500
});

✅ 迁移后

javascript
import { setupRequestGuard } from '@hydd/request-guard';

setupRequestGuard(axios, {
  defaults: {
    retry: {
      attempts: 3,            // 首次 + 2 次重试
      delay: 300,
      backoff: 'exponential'
    }
  },
  rules: [
    // 默认排除 POST/PUT/PATCH/DELETE,与 axios-retry 行为接近
    { method: 'get', retry: true }
  ]
});

迁移要点

旧方案request-guard
retries: 3(重试 3 次)attempts: 3(含首次共 3 次,即重试 2 次)。注意语义不同,需换算
retryDelay: exponentialDelaybackoff: 'exponential'
retryCondition 自定义函数canRetry(context),context 含 status/code/attempt 等完整字段
只能配 axios全平台可用,fetch / 小程序 / SDK 都能接
重试时不防重、不熔断可与 duplicate / circuitBreaker 自由组合

attempts 语义

attempts总尝试次数(含首次),不是重试次数。attempts: 3 = 首次 + 2 次重试。axios-retry 的 retries: 3 = 首次 + 3 次重试。迁移时 attempts = retries + 1

🔁 从手写重试循环迁移

📦 旧方案

javascript
async function fetchWithRetry(url, maxRetry = 3) {
  for (let i = 0; i <= maxRetry; i++) {
    try {
      return await axios.get(url);
    } catch (err) {
      if (i === maxRetry) throw err;
      await new Promise(r => setTimeout(r, 1000 * (i + 1)));
    }
  }
}

✅ 迁移后

javascript
setupRequestGuard(axios, {
  defaults: {
    retry: { attempts: 4, delay: 1000, backoff: 'linear' }
  }
});

// 直接用 axios,重试自动生效
const res = await axios.get('/api/data', {
  requestGuard: { retry: true }
});

删除所有手写的 fetchWithRetry 包装函数,业务代码直接用原始请求方法。

📋 迁移步骤建议

  1. 接入 guard,保留旧逻辑:先 setupRequestGuard 配好规则,但暂时不删旧的拦截器/包装函数,让两者并存观察。
  2. 逐步切换:把请求一个个从旧包装函数切到原生 axios 调用(guard 已接管 axios,会自动生效)。
  3. 验证无误后删除旧逻辑:确认 guard 的防重/重试行为符合预期后,删除 inFlightfetchWithRetry、axios-retry 等旧代码。
  4. 保留逃生口:个别请求如果不想被治理,加 requestGuard: false 绕过,无需删 guard。

⚠️ 注意事项

  • 响应拦截器收不到短路错误:block 命中、熔断拦截这类"请求未发出"的错误不经过 axios.interceptors.response.use。如果旧拦截器里有针对这类错误的处理,迁移到 notify 出口。详见 错误处理模式
  • ignore 命中的重复请求不会 settle:默认 duplicate ignore 不会触发重复调用方的 .then() / .catch() / .finally()。如果业务需要显式错误,请配置 strategy: 'block'
  • 自定义 transport 可按需配置 inFlightTtl:默认在途 key 只在原请求 settle 或 clearState() 时释放。如果 transport 可能永不 settle,可显式设置 defaults.duplicate.inFlightTtl 做兜底;开启后,原请求未结束但 key 已超时时,后续同 key 请求可能重新发出。
  • clearState 替代手动清理:原来在路由切换/登出时手动清 inFlight 的逻辑,改为 requestManager.clearState()。详见 守护卸载与生命周期
  • 不要在 guard 外再套防重/重试:接入 guard 后,删除手写的同类逻辑,避免双重防重导致请求被错误拦截、双重重试导致重试次数翻倍。

基于 MIT 许可发布