ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue 封装 axios 类的使用(小bug 改进):TaoToken 统一 Key 接入 settings.json 配置骨架

Vue 封装 axios 类的使用(小bug 改进):TaoToken 统一 Key 接入 settings.json 配置骨架 1. Vue 封装 axios 类的使用小bug 改进TaoToken 统一 Key 接入 settings.json 配置骨架Vue 项目里封装 axios 类几乎是每个前端都会走一遍的路。封装本身不难难的是封装完之后那些“看起来能跑、实际一用就出问题”的小 bug请求拦截器越加越多、token 每次请求都重新注册一遍、post 的 Content-Type 和实际传参对不上、静态方法拿不到实例导致 this 丢失。这些问题单看都不致命但堆在一起排查起来能耗掉一整个下午。这篇就围绕一个真实的 Vue axios 封装类把上面这些坑一个个拆开讲清楚同时把 TaoToken 的统一 Key 通道接进来用一份可复制的settings.json配置骨架让 baseURL、超时、鉴权头这些参数集中管理不再散落在代码里。适合正在写 Vue 请求层、或者已经封装完但总出小毛病的前端同学。读完之后你应该能拿到一份能直接跑、并且知道每个参数为什么这么写的封装代码。2. 先看原封装里那几个真实的小 bug原始代码结构其实挺典型一个Http基类内部axios.create建实例挂一个响应拦截器再暴露一个request方法业务层api继承Http通过静态方法getinstance返回实例。逻辑是通的但有几个地方一上量就露馅。第一个 bug 是请求拦截器被重复注册。原代码把this.axios.interceptors.request.use(...)写在了request方法内部只要调用一次带auth的请求就往实例上挂一个拦截器。调十次就挂十个token 被反复读取、config 被反复打印控制台刷屏不说拦截器链还会越来越长。正确做法是在构造函数里注册一次用闭包或实例属性读取 token。第二个 bug 是auth判断和拦截器耦合。原逻辑是“需要鉴权才注册拦截器”但拦截器一旦注册就永久生效后续不带 auth 的请求也会走这段逻辑。应该反过来拦截器常驻在拦截器内部根据 config 上的自定义标记决定要不要加 token。第三个 bug 是 post 的dataType分支里json和qs的 header 写死了但formData分支用Object.keys(data)遍历如果data是undefined会直接抛错。isnewuser和robrecord这两个请求就是 post 且没传 data正好踩中。第四个 bug 是静态方法getinstance每次调用都new api()虽然能拿到实例但每次都是新对象拦截器、配置全部重建等于没起到“复用”的作用。应该用单例或者干脆把方法挂到原型上通过模块导出复用。第五个是 baseURL 硬编码在config.js里换环境、换通道都要改代码。这就是为什么要把配置抽到settings.json配合 TaoToken 的统一 Key 通道让请求层只认配置、不认环境。3. TaoToken 前置统一 Key 与 settings.json 骨架TaoToken 在这里扮演的角色是“统一 API 通道”。你不需要在代码里到处写不同的服务地址和 Key而是把通道地址和 Key 集中到一份配置里axios 封装只负责读取。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。先准备一份settings.json放在项目根目录或src/config/下作为请求层的唯一配置源{ api: { baseUrl: https://taotoken.net/api, timeout: 15000, keyHeader: Authorization, keyPrefix: Bearer , tokenStorageKey: taotoken_key }, request: { retry: 1, withCredentials: false } }几个字段说明一下。baseUrl指向 TaoToken 的 API 通道所有业务请求的 url 都拼在它后面。timeout设 15 秒比原来的 10 秒宽松一点避免网络抖动直接判失败。keyHeader和keyPrefix决定 Key 怎么放进请求头Bearer前缀是常见写法。tokenStorageKey是本地存储里 Key 的键名登录后写进去拦截器从这里读。注意settings.json里不要写死真实 Key只放结构和非敏感默认值。真实 Key 通过登录流程写入 localStorage或者由构建时的环境变量注入。拿到 Key 的入口在控制台的 API Keys 页面地址是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。生成之后先别急着贴进代码按上面的方式走存储读取。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有请求头格式和返回结构的说明封装时对照着写能少踩坑。4. 可复制的 axios 封装代码下面这份http.js把前面提到的 bug 全部修掉并且从settings.json读配置。先装依赖npm install axios querystringify然后是封装本体import axios from axios; import qs from querystringify; import settings from /config/settings.json; const { baseUrl, timeout, keyHeader, keyPrefix, tokenStorageKey } settings.api; class Http { constructor() { this.axios axios.create({ baseURL: baseUrl, timeout, withCredentials: settings.request.withCredentials, }); // 请求拦截器只注册一次内部按标记决定是否加 Key this.axios.interceptors.request.use( (config) { if (config.needAuth) { const key localStorage.getItem(tokenStorageKey); if (key) { config.headers[keyHeader] ${keyPrefix}${key}; } } return config; }, (err) Promise.reject(err) ); // 响应拦截器统一剥壳401 交给业务层处理 this.axios.interceptors.response.use( (response) response.data, (err) { const status err.response err.response.status; if (status 401) { localStorage.removeItem(tokenStorageKey); } return Promise.reject(err); } ); } request({ url, data, method get, auth false, dataType json }) { const config { needAuth: auth }; if (method get) { return this.axios.get(url, { params: data, ...config }); } if (method post) { if (dataType json) { return this.axios.post(url, data || {}, { ...config, headers: { Content-Type: application/json }, }); } if (dataType formData) { const formData new FormData(); Object.keys(data || {}).forEach((key) { formData.append(key, data[key]); }); return this.axios.post(url, formData, { ...config, headers: { Content-Type: multipart/form-data }, }); } if (dataType qs) { return this.axios.post(url, qs.stringify(data || {}), { ...config, headers: { Content-Type: application/x-www-form-urlencoded }, }); } } return Promise.reject(new Error(不支持的 method: ${method})); } } export default Http;关键改动点拦截器移到构造函数只注册一次用config.needAuth这个自定义字段控制是否加 Key而不是靠“注册不注册拦截器”data || {}兜底避免isnewuser这类无参 post 报错needAuth通过展开运算符透传给 axios不会污染 header。业务层api.js改成单例导出不再每次newimport Http from /utils/http; class Api extends Http { login(data) { return this.request({ url: /user/oauth2, method: post, data, dataType: formData, }); } isNewUser() { return this.request({ url: /user/isNew, method: post, auth: true, }); } addRobInfo(data) { return this.request({ url: /ticket/add, method: post, data, dataType: formData, auth: true, }); } robRecord() { return this.request({ url: /ticket/index, method: post, auth: true, }); } } export default new Api();页面里用起来就干净了import api from /api; async function loadRecord() { try { const res await api.robRecord(); console.log(抢票记录, res); } catch (e) { console.error(请求失败, e); } }5. 验证请求与成功结果配置写完先做一次最小验证确认 Key 通道和封装都通。在main.js或任意组件里临时调用import api from /api; localStorage.setItem(taotoken_key, 你的Key); api.isNewUser().then((res) { console.log(isNewUser 返回, res); });打开浏览器 Network 面板找到这条请求检查三件事。第一Request URL 应该是https://taotoken.net/api/user/isNew说明 baseURL 拼接正确。第二Request Headers 里应该有Authorization: Bearer 你的Key说明拦截器读到了存储里的 Key 并加上了前缀。第三Response 返回的是剥壳后的data不是完整的 axios response 对象说明响应拦截器生效。如果这三项都对再测一个带 formData 的login看 Content-Type 是不是multipart/form-data参数是不是以表单字段形式发出。最后测一个无参 post 的robRecord确认不再抛Object.keys的错。三条都过封装就算稳了。想直接验证模型通道是否可用可以到模型对话页面发一条测试消息地址是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 确认 Key 在对话场景下也能正常鉴权。6. 本篇常见错排查请求头里没有 Authorization。先确认localStorage.getItem(tokenStorageKey)的键名和写入时一致settings.json里是taotoken_key写入时别写成token。再确认调用时传了auth: trueisNewUser和robRecord都传了login没传是故意的。post 报Cannot convert undefined or null to object。这是Object.keys(data)在data为 undefined 时抛的。代码里已经用data || {}兜底如果你改过这行检查一下兜底还在不在。拦截器重复执行、控制台打印多次。说明拦截器被注册了多次。检查是不是把interceptors.request.use又写回了request方法里。正确位置只在构造函数。401 之后页面没跳登录。响应拦截器里只做了清除 Key跳转逻辑要按你的路由方案补。Vue Router 可以在拦截器里import router后router.push(/login)注意避免循环依赖。baseURL 改了但请求地址没变。settings.json是构建时读取的改完要重启 dev server。如果用了缓存清一下node_modules/.vite再启动。长期做编码和 Agent 类项目的话可以考虑 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 把 Key 通道和编码工作流绑在一起省去反复配环境的功夫。Claude Code 相关的接入说明在 https://taotoken.net/doc/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 需要的话对照配置。封装这件事改到最后往往不是代码写得多漂亮而是每个参数都能说清楚为什么在那。把拦截器收进构造函数、把配置抽进settings.json、把 Key 交给统一通道这三步做完后面加接口基本就是复制粘贴的事。
返回列表