小王最近接手了一个老项目,发现页面里到处散落着重复的AJAX错误处理逻辑:
$.ajax({ error: function(xhr) { if(xhr.status === 401) { alert('登录过期啦!') jumpToLogin() } } })
更糟的是,不同开发者写的错误提示样式五花八门——有的用alert,有的用Toast,还有的直接在页面角落塞个红色小字,这时候如果能统一拦截所有AJAX请求,就像给整个项目装上"中央空调",那该多清爽?
所有现代AJAX库(包括jQuery/axios)底层都基于浏览器原生XMLHttpRequest
对象,我们只需要"劫持"它的open()
和send()
方法:
const originalOpen = XMLHttpRequest.prototype.open const originalSend = XMLHttpRequest.prototype.send XMLHttpRequest.prototype.open = function(method, url) { console.log(`拦截到请求:${method} ${url}`) // 在这里可以添加全局请求头等操作 this.setRequestHeader('X-Requested-With', 'CustomAJAX') return originalOpen.apply(this, arguments) } XMLHttpRequest.prototype.send = function(body) { const startTime = Date.now() this.addEventListener('loadend', () => { console.log(`请求耗时:${Date.now() - startTime}ms`) }) return originalSend.apply(this, arguments) }
(function() { const originalFetch = window.fetch const originalXHR = window.XMLHttpRequest // 拦截Fetch API window.fetch = async function(...args) { try { const start = Date.now() const response = await originalFetch(...args) console.log(`[FETCH] ${args[0]} 耗时${Date.now() - start}ms`) return response } catch (err) { handleError(err) throw err } } // 拦截XMLHttpRequest window.XMLHttpRequest = class extends originalXHR { open(method, url) { this._method = method this._url = url super.open(method, url) } send(data) { this._startTime = Date.now() this.addEventListener('loadend', () => { const duration = Date.now() - this._startTime if (this.status >= 400) { handleError(new Error(`${this._method} ${this._url} ${this.status}`)) } console.log(`[XHR] ${this._url} 耗时${duration}ms`) }) super.send(data) } } function handleError(err) { console.error('全局捕获AJAX错误:', err) // 这里可以统一展示错误提示 showToast('网络请求失败,请稍后重试') // 上报错误到监控系统 reportToMonitor(err) } })()
const apiPerformance = {}
function recordApiTime(url, cost) {
if (!apiPerformance[url]) {
apiPerformance[url] = { count: 0, total: 0 }
}
apiPerformance[url].count++
apiPerformance[url].total += cost
}
// 在拦截器中调用
recordApiTime(url, Date.now() - startTime)
2. **自动重试机制**
```javascript
const MAX_RETRY = 2
function enhancedFetch(url, options = {}, retryCount = 0) {
return fetch(url, options).catch(err => {
if (retryCount < MAX_RETRY && isRetryable(err)) {
return enhancedFetch(url, options, retryCount + 1)
}
throw err
})
}
function isRetryable(err) {
return err.message.includes('timeout') ||
err.message.includes('Network Error')
}
axios.interceptors.request.use(config => { config.headers['X-Trace-Id'] = generateUUID() return config }) axios.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { router.push('/login') } return Promise.reject(error) } )
$.ajaxSetup({ beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRF-TOKEN', getCSRFToken()) }, error: function(xhr) { if (xhr.status === 403) { showPermissionDeniedModal() } } })
执行顺序问题
拦截器代码必须放在其他业务代码之前执行,建议作为第一个加载的脚本
避免循环调用
在错误处理中慎用AJAX请求,可能导致无限循环
性能影响
高频请求场景建议精简拦截逻辑,或用条件判断跳过非必要处理
TypeScript支持
如果需要类型提示,可以扩展全局接口:
declare global { interface XMLHttpRequest { _method: string _url: string _startTime: number } }
全局拦截就像给AJAX请求装上了行车记录仪,既能统一处理共性需求(如错误提示、日志记录),又能灵活扩展个性化功能(如接口耗时统计、自动重试),下次当你发现项目里有重复的AJAX处理逻辑时,不妨试试这套"中央管控"方案,让代码更加干净利落。
(本文实现方案基于2025年8月主流浏览器API标准)
本文由 史以蕊 于2025-08-02发表在【云服务器提供商】,文中图片由(史以蕊)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/513455.html
发表评论