longmo-plugin-axios
v2.0.4
Published
基于 `axios-cache-interceptor` 增强的 axios 插件(见 `./llms-full.txt` ),提供开箱即用的缓存、错误处理、loading 等功能。
Maintainers
Readme
longmo-plugin-axios
基于 axios-cache-interceptor 增强的 axios 插件(见 ./llms-full.txt ),提供开箱即用的缓存、错误处理、loading 等功能。
支持 axios-cache-interceptor 1.x 版本
注意:如果使用 Web Storage(localStorage/sessionStorage),无需额外依赖。如果使用其他存储类型,请按需安装:
# 使用 IndexedDB 存储(可选) pnpm add localforage@^1.10.0 # 使用内存存储的 LRU 策略(可选,已内置) pnpm add lru-cache@^11.2.4
快速使用
使用 setup 返回一个平台的 axios 最佳实践配置,并提供钩子函数进行自定义。
import { setup } from 'longmo-plugin-axios'
const _axios = setup({}) // 返回 axios 实例,用法见 axios 的文档
export default _axiossetup
配置了接口的通用错误处理能力
返回axios实例,使用默认配置可快速使用,简化配置,不和任何框架绑定,适用所有平台的UI框架
在业务平台可快速使用, 注意重复setup都会返回一个新的axios实例,可全局注册复用实例
如果setup不能满足项目需求,可自行使用axios配置,配置说明见上。
基础使用
import { setup } from 'longmo-plugin-axios'
const _axios = setup()
export default _axios高级使用
loading.js全局loading配置
import {Loading} from 'element-ui'
export default {
instance: null,
show() {
this.instance = Loading.service({fullscreen: true, lock: true, background: 'rgba(255, 255, 255, 0.2)'})
},
hide() {
if (this.instance) {
this.instance.close()
}
}
}plugins/axios.js配置
import loading from './loading.js'
import { Notification } from 'element-ui'
import { setup } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
ttl: 3 * 60 * 1000, // 缓存有效期 3 分钟
},
loading: loading,
errorAction: (error) => {
Notification.error({
title: error.name, // 如果不想使用默认标题,可通过 error.type 判断设置
message: error.message
})
},
authFailAction: () => {
// logout 返回到登录页面逻辑
}
})
export default _axios- 请求使用参数
import axios from './plugins/axios.js'
axios.get('/user')
axios.get('/list', {
manual: true, // 手动处理报文,不用默认方式处理
loading: true, // 请求时显示网络请求 loading,前提是在 setup 设置了 loading
})setupOptions
// setup 配置参数
export type SetupConfig = {
cache?: CacheOptions, // 缓存配置,详见 axios-cache-interceptor 文档
loading?: IAxiosLoading, // 全局 loading 配置,默认不显示,需要在每个请求中设置
authFailAction?(error: any): void, // 认证失败调用的函数,可在这里处理跳转逻辑
errorAction?(error: IError): void, // 网络请求失败错误处理逻辑
requestInterceptor?(config: TCInternalCacheRequestConfig): TCInternalCacheRequestConfig, // 自定义请求拦截器
responseInterceptor?(response: any): any, // 自定义响应拦截器
}
// 网络请求 全局loading,和UI框架解耦
export interface IAxiosLoading {
instance: any;
show(): void;
hide(): void;
}
// 网络请求错误类型
export enum IErrorType {
NETWORK = 'NETWORK', // 网络异常 404 500
SYSTEM = 'SYSTEM' // 系统异常 response.status=200, 系统报错
}
// 异常接口
export interface IError extends Error {
name: string; // 网络异常 系统异常
message: string; // 错误信息
type: IErrorType; // 异常类型
code: string | number // 错误码,网络异常错误码 系统异常错误码
}use(可选)
用于在 Vue 应用中挂载全局 $axios 实例。
Vue 2:挂载到
Vue.prototype上import Vue from 'vue' import { setup, use } from 'longmo-plugin-axios' use(setup(), Vue)Vue 3:挂载到
app.config.globalPropertiesimport { createApp } from 'vue' import App from './App.vue' import { setup, use } from 'longmo-plugin-axios' const app = createApp(App) use(setup({}), app) app.mount('#app')使用挂载的全局
$axios实例{ mounted() { this.$axios.get('/user') } }
缓存配置
本项目基于 axios-cache-interceptor 实现缓存功能,支持多种存储策略和缓存配置。
支持的存储类型
axios-cache-interceptor 内置了以下存储类型:
- Memory Storage(默认):内存存储,适用于所有环境,页面刷新后数据丢失
- Web Storage API:浏览器本地存储(localStorage/sessionStorage),持久化缓存
- 自定义存储:可以通过
buildStorage创建自定义存储(如 IndexedDB、Redis 等)
使用 localStorage 示例
import { setup, buildWebStorage } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
ttl: 5 * 60 * 1000, // 缓存有效期 5 分钟
storage: buildWebStorage(localStorage, 'axios-cache:')
}
})使用 sessionStorage 示例
import { setup, buildWebStorage } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
ttl: 5 * 60 * 1000,
storage: buildWebStorage(sessionStorage, 'axios-cache:')
}
})使用 IndexedDB 示例(需要安装 localforage)
import { setup, buildStorage } from 'longmo-plugin-axios'
import localforage from 'localforage'
// 创建 IndexedDB 实例
const forageStore = localforage.createInstance({
driver: [localforage.INDEXEDDB, localforage.LOCALSTORAGE],
name: 'my-app-cache',
storeName: 'axios_cache'
})
// 构建自定义存储
const indexedDbStorage = buildStorage({
async set(key, value) {
await forageStore.setItem(key, value)
},
async find(key) {
return (await forageStore.getItem(key)) ?? undefined
},
async remove(key) {
await forageStore.removeItem(key)
},
async clear() {
await forageStore.clear()
}
})
const _axios = setup({
cache: {
enabled: true,
ttl: 5 * 60 * 1000,
storage: indexedDbStorage
}
})自定义存储说明
您可以通过 buildStorage 函数创建任意类型的存储,只需实现以下接口:
interface StorageAdapter {
// 保存数据
set(key: string, value: any, currentRequest?: any): void | Promise<void>
// 查找数据
find(key: string, currentRequest?: any): any | Promise<any | undefined>
// 删除数据
remove(key: string, currentRequest?: any): void | Promise<void>
// 清空所有数据(可选)
clear?(): void | Promise<void>
}常见的自定义存储场景:
- IndexedDB:适合大量数据缓存,容量大(通常 50MB+)
- Redis:服务端缓存,多实例共享
- Memory + LRU:内存受限环境,自动淘汰旧数据
- FileSystem:Node.js 环境持久化缓存
更多关于存储的信息请参考:Storages
基础使用
import { setup } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
ttl: 5 * 60 * 1000, // 缓存有效期(毫秒)
}
})
_axios.get('https://httpbin.org/get') // 发起真实网络请求
_axios.get('https://httpbin.org/get') // 使用前一个请求的缓存,不发出实际的 HTTP 请求高级使用
import { applyInterceptors } from '@/api/setupAxiosInterceptors';
import { buildStorage, buildStringKeyGenerator, forageStorage, setup } from 'longmo-plugin-axios';
const cacheOptions = {
enabled: true,
methods: ['get', 'head', 'post'], // 支持缓存 post 请求
ttl: 60 * 1000, // 设置缓存有效期毫秒值
storage: buildStorage(forageStorage),
generateKey: buildStringKeyGenerator(),
// 必须设置为false,否则预检请求会报错
cacheTakeover: false
};
export function getCacheAxios(domain = '/') {
const axiosOptions = {
baseURL: domain,
'Content-Type': 'application/json;charset=UTF-8'
};
const http = setup({
...axiosOptions,
cache: cacheOptions
});
applyInterceptors(http);
return http;
}覆盖实例的配置参数
在使用特定的缓存配置设置了 axios-cache-interceptor 之后,您可以在单个请求中覆盖该配置。
对时效性要求比较高的接口,可以在请求中禁用缓存,设置 cache.enabled = false
import { setup } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
ttl: 5 * 60 * 1000, // 设置缓存有效期(毫秒)
}
})
// 设置该请求的缓存有效时间(毫秒)
_axios.get('https://httpbin.org/get', {
cache: {
ttl: 2 * 60 * 1000
}
})
// 手动禁用缓存,并调用真实的 HTTP 请求
_axios.get('https://httpbin.org/get', {
cache: {
enabled: false
}
})CacheOptions 配置说明
更多高级设置参数,请查看 axios-cache-interceptor 官方文档
{
/**
* 是否启用缓存
* @default true
*/
enabled?: boolean;
/**
* 缓存生存时间(毫秒)
* 可以是固定值或函数,函数接收响应对象并返回 TTL 值
* @default 1000 * 60 * 5 (5分钟)
*/
ttl?: number | ((response: CacheAxiosResponse) => number | Promise<number>);
/**
* 是否从响应头解析缓存时间(Cache-Control、Age 等)
* 如果启用,ttl 将作为兜底值,优先使用服务器返回的缓存头
* @default true
*/
interpretHeader?: boolean;
/**
* 防止浏览器双重缓存
* 会在请求中添加 Cache-Control、Pragma、Expires 头,阻止浏览器缓存
* @default true
*/
cacheTakeover?: boolean;
/**
* 允许缓存的 HTTP 方法
* @default ['get', 'head']
*/
methods?: Method[];
/**
* 缓存谓词,用于判断响应是否可以被缓存
*/
cachePredicate?: CachePredicate;
/**
* 缓存更新策略,用于在请求成功后更新其他缓存
*/
update?: CacheUpdater;
/**
* ETag 缓存支持
* @default true
*/
etag?: boolean | string;
/**
* If-Modified-Since 缓存支持
* @default true
*/
modifiedSince?: boolean;
/**
* 错误时使用缓存(stale-if-error)
* @default true
*/
staleIfError?: boolean | number | StaleIfErrorPredicate;
/**
* 强制发起新请求,绕过缓存检查
* @default false
*/
override?: boolean;
/**
* Vary 头处理
* @default true
*/
vary?: boolean | string[];
/**
* 水合回调,当需要从网络获取数据但存在过期缓存时调用
* 可用于先展示旧数据,再更新为新数据
*/
hydrate?: (cache: StorageValue) => void | Promise<void>;
}调试模式
开启调试模式可以帮助您了解缓存的工作机制,排查问题。
import { setup } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
debug: console.log, // 打印调试信息到控制台
ttl: 5 * 60 * 1000
}
})调试信息包括:
- 请求是否命中缓存
- 缓存的 TTL 信息
- 并发请求的处理
- 缓存的读写操作
详细调试指南请参考:Debugging
注意事项
Axios 拦截器执行顺序
Axios 拦截器对请求和响应的执行顺序不同:
- 请求拦截器按相反顺序执行——最后添加的拦截器最先运行(LIFO - 后进先出)
- 响应拦截器按正常顺序执行——第一个添加的拦截器首先运行(FIFO - 先进先出)
// 这个将在缓存拦截器之前运行
axios.interceptors.request.use((req) => req);
// 这个将在缓存拦截器之后运行
axios.interceptors.response.use((res) => res);
setupCache(axios);
// 这个将在缓存拦截器之后运行
axios.interceptors.request.use((req) => req);
// 这个将在缓存拦截器之前运行
axios.interceptors.response.use((res) => res);如果需要完全控制拦截器的注册顺序,可以使用 register: false 选项手动注册:
import { setupCache } from 'axios-cache-interceptor';
const axios = setupCache(Axios.create(), { register: false });
// 先注册自己的拦截器
axios.interceptors.request.use((req) => req);
axios.interceptors.response.use((res) => res);
// 再手动注册缓存拦截器
axios.interceptors.request.use(
axios.requestInterceptor.onFulfilled,
axios.requestInterceptor.onRejected
);
axios.interceptors.response.use(
axios.responseInterceptor.onFulfilled,
axios.responseInterceptor.onRejected
);扩展类型
使用 longmo-plugin-axios 时,你会注意到它的类型与默认的 AxiosInstance、AxiosRequestConfig 和 AxiosResponse 不同。
这是因为我们选择覆盖 Axios 的接口,而不是扩展,以避免与其他库的兼容性问题。
然而,这也意味着在与其他包集成或创建自定义拦截器时,你需要覆盖/扩展我们自己的类型,
如 TCAxiosInstance、TCAxiosRequestConfig 和 TCAxiosResponse,以符合你的需求。
具体步骤如下:
declare module 'longmo-plugin-axios' {
interface TCAxiosRequestConfig<R = unknown, D = unknown> {
customProperty: string;
}
}流与非 JSON 响应
axios-cache-interceptor 只能处理可序列化的数据类型。如果你需要缓存流或缓冲区,需要使用 transformResponse 将其转换为字符串或对象。
import { setup } from 'longmo-plugin-axios'
const _axios = setup({
cache: { enabled: true }
})
const response = await _axios.get('my-url-that-returns-a-stream', {
responseType: 'stream',
transformResponse(response) {
// 你需要实现这个转换函数
return convertStreamToStringOrObject(response.data)
}
})
response.data // 将是字符串或对象,可以被缓存如果仍然需要 response.data 作为流或缓冲区,就需要手动缓存。
清除缓存
// 清除特定请求的缓存
await _axios.storage.remove(requestId)
// 清除所有缓存
await _axios.storage.clear()例如,在用户退出登录时清除所有缓存:
function logout() {
// 清除 token 等操作...
// 清除所有缓存
_axios.storage.clear()
// 跳转到登录页
}自定义缓存 Key
buildKeyGenerator 方法接收 axiosConfig 作为参数,
返回值若是对象,则使用 hash 进行转换;若是字符串或者数字,统一转为字符串作为 key。
import { setup, buildKeyGenerator, buildURLWithAxiosConfig } from 'longmo-plugin-axios'
const _axios = setup({
cache: {
enabled: true,
debug: console.error,
generateKey: buildKeyGenerator((request) =>
buildURLWithAxiosConfig(request)
),
ttl: 1000 * 2, // 过期时间 2 秒
}
})
export default _axios更多关于 Request ID 的信息请参考:Request ID
TTL(Time To Live)说明
ttl 是 Time To Live(生存时间) 的缩写,它决定了 缓存条目在内存中可以保留多久(单位:毫秒)。
| 配置项 | 作用 |
|-------------------------|------------------------------------------------------------------|
| ttl: 99999 | 默认缓存有效期为 99,999 毫秒(约 100 秒) |
| interpretHeader: true | 优先从 HTTP 响应头(如 Cache-Control、Age)中解析真实的缓存时间,而不是直接用 ttl |
🧠 interpretHeader: true 时的逻辑(重点!)
当 interpretHeader 为 true(默认值),ttl 只是"兜底值",实际 TTL 会根据服务器返回的 HTTP 缓存头动态计算:
- 服务器返回:
Cache-Control: max-age=300 Age: 50 - 那么实际缓存时间 =
max-age - Age = 300 - 50 = 250 秒 - 这个 250 秒会覆盖你设置的
ttl: 99999 - 所以你在日志中看到:
log(`Cache TTL info: ${cacheInformation.ttl}`) // 输出的是 250000(毫秒),不是 99999!
📌 总结:ttl 的作用
| 场景 | ttl 的作用 |
|-----------------------------|--------------------------------------|
| interpretHeader: true(默认) | 备用值:当服务器没返回有效缓存头时才用 |
| interpretHeader: false | 强制使用:所有请求都按这个时间缓存 |
| 单位 | 毫秒(ms) |
| 默认值 | 如果不传 ttl,默认是 1000 * 60 * 5(5 分钟) |
缓存失效策略
当数据发生变化时(如创建、更新、删除操作),需要使相关缓存失效。
方法一:编程式更新缓存
// 使用自定义 ID 标识请求
function listPosts() {
return _axios.get('/posts', {
id: 'list-posts'
})
}
function createPost(data) {
return _axios.post('/posts', data, {
cache: {
update: {
// 在创建新文章后,更新列表缓存
'list-posts': (listPostsCache, createPostResponse) => {
if (listPostsCache.state !== 'cached') {
return 'ignore'
}
// 将新文章添加到列表
listPostsCache.data.posts.push(createPostResponse.data)
return listPostsCache
}
}
}
})
}方法二:删除缓存
function createPost(data) {
return _axios.post('/posts', data, {
cache: {
update: {
// 直接删除列表缓存,下次请求时会重新从服务器获取
'list-posts': 'delete'
}
}
})
}方法三:手动清除缓存
// 清除特定请求的缓存
await _axios.storage.remove('list-posts')
// 清除所有缓存
await _axios.storage.clear()更多关于缓存失效的信息请参考:Invalidating Cache
响应对象属性
使用 axios-cache-interceptor 后,响应对象会包含以下额外属性:
const response = await _axios.get('/api/data', {
id: 'my-request-id'
})
response.id // 请求 ID,用于缓存键
response.cached // boolean,表示是否来自缓存
response.stale // boolean,表示是否是过期缓存TODO
- [x] 默认关闭适配器
- [ ] 添加重试适配器
- [x] 抽离默认拦截器适配器
- [x] 升级 lru-cache 版本
- [x] 支持清除所有缓存方法(例如退出登录时需要清除所有缓存)
- [x] 支持检查响应是否是来自缓存
- [x] 支持强制更新某个接口的缓存
- [x] 添加缓存 key 自定义生成规则配置项
- 支持 GET 请求时添加缓存,PUT、PATCH、DELETE 请求时,使相同 URL 的缓存失效
