上一篇
场景还原:凌晨2点,你正在赶一个紧急需求——用户提交表单后需要实时获取后端数据并动态渲染,结果发现代码里散落着十几处重复的fetch
调用,每个错误处理都写得不一样...😱 这时候你突然意识到:早该把Ajax封装成统一工具了!
减少70%重复代码
不用再在每个页面写try-catch
、Content-Type
设置、错误提示等样板代码
统一错误处理
所有接口自动拦截401跳登录页、500显示友好提示,告别「每个接口报错样式不同」的尴尬
方便全局控制
想给所有请求加个Loading动画?改一处配置就行!
// 封装前 vs 封装后对比 // 😵 原始写法(每个请求都要重复) fetch('/api/data') .then(res => res.json()) .catch(err => console.error('啊哦,出错了:', err)) // 🚀 封装后写法 ajax.get('/api/data').then(data => { // 直接拿到处理好的数据! })
class Ajax { constructor(baseURL = '') { this.baseURL = baseURL } request(method, url, data) { return new Promise((resolve, reject) => { fetch(`${this.baseURL}${url}`, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(async res => { const result = await res.json() if (res.ok) { resolve(result) } else { reject(result) // 这里可以统一处理业务错误 } }) .catch(err => { reject({ message: '网络异常,请检查连接' }) // 统一网络错误格式 }) }) } get(url) { return this.request('GET', url) } post(url, data) { return this.request('POST', url, data) } // ...其他方法同理 }
// 在constructor中增加 this.interceptors = { request: [], response: [] } // 修改request方法 const config = { method, headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json' } }
const controller = new AbortController() setTimeout(() => controller.abort(), 10000) // 10秒超时 fetch(url, { ...config, signal: controller.signal })
// 添加拦截器示例 ajax.interceptors.request.use(config => { console.log('请求发出前打日志', config) return config }) ajax.interceptors.response.use(response => { if (response.code === 401) { location.href = '/login' // 自动跳转登录 } return response })
const pendingRequests = new Map() function generateRequestKey(config) { return `${config.method}-${config.url}` } // 在发送请求前检查 const key = generateRequestKey(config) if (pendingRequests.has(key)) { pendingRequests.get(key).abort() // 取消前一个相同请求 } pendingRequests.set(key, controller)
postFormData(url, formData) { return this.request('POST', url, formData, { headers: { 'Content-Type': 'multipart/form-data' // 注意这里不能手动设置! } }) }
const cache = new Map() getWithCache(url) { if (cache.has(url)) { return Promise.resolve(cache.get(url)) } return this.get(url).then(data => { cache.set(url, data) return data }) }
Content-Type的坑
Content-Type
并带上boundary,此时手动设置会报错! 错误处理优先级
catch(err => { if (err.name === 'AbortError') { // 手动取消的请求 } else if (!navigator.onLine) { // 断网情况 } else { // 其他错误 } })
TypeScript加持更安全
interface ResponseData<T = any> { code: number data: T message?: string } class Ajax { get<T>(url: string): Promise<ResponseData<T>> { // ... } }
虽然fetch API
仍是基础,但现代项目可以考虑:
但无论如何,理解底层封装原理永远是一个高级前端的核心竞争力!
最后的小彩蛋 🥚:在Node.js环境下,可以基于这个封装思路轻松改造为通用HTTP工具(只需把fetch
替换成node-fetch
或axios
)
// 服务端用法示例 const serverAjax = new Ajax('https://api.example.com') serverAjax.get('/users').then(users => { console.log('获取到的用户数据:', users) })
本文由 节歆 于2025-08-02发表在【云服务器提供商】,文中图片由(节歆)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/510372.html
发表评论