迁移指南
从手写拦截器、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: exponentialDelay | backoff: '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 包装函数,业务代码直接用原始请求方法。
📋 迁移步骤建议
- 接入 guard,保留旧逻辑:先
setupRequestGuard配好规则,但暂时不删旧的拦截器/包装函数,让两者并存观察。 - 逐步切换:把请求一个个从旧包装函数切到原生 axios 调用(guard 已接管 axios,会自动生效)。
- 验证无误后删除旧逻辑:确认 guard 的防重/重试行为符合预期后,删除
inFlight、fetchWithRetry、axios-retry 等旧代码。 - 保留逃生口:个别请求如果不想被治理,加
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 后,删除手写的同类逻辑,避免双重防重导致请求被错误拦截、双重重试导致重试次数翻倍。

