fetch 可以与 AbortController 搭配使用来取消请求。
(function() { const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { this._url = url; // 保存请求的URL return originalOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(body) { this.addEventListener('load', function() { if (this.status >= 200 && this.status < 300) { // 请求成功,上报成功信息 reportRequest('success', this._url, this.status, this.responseText); } else { // 请求失败,上报失败信息 reportRequest('failure', this._url, this.status, this.responseText); } }); this.addEventListener('error', function() { // 请求错误,上报错误信息 reportRequest('error', this._url, this.status, this.statusText); }); return originalSend.apply(this, arguments); }; function reportRequest(type, url, status, response) { // 这里进行上报,可以通过fetch或其他方式发送上报请求 console.log(`Request ${type}: ${url}, Status: ${status}, Response: ${response}`); // 例子:fetch('/report', { method: 'POST', body: JSON.stringify({ type, url, status, response }) }) } })();那fetch如何实现监听呢?同样也是重写。
(function() { const originalFetch = window.fetch; window.fetch = function() { const args = arguments; const url = args[0]; // 堆代码 duidaima.com return originalFetch.apply(this, arguments) .then(response => { if (response.ok) { // 请求成功,上报成功信息 response.clone().text().then(responseText => { reportRequest('success', url, response.status, responseText); }); } else { // 请求失败,上报失败信息 response.clone().text().then(responseText => { reportRequest('failure', url, response.status, responseText); }); } return response; }) .catch(error => { // 请求错误,上报错误信息 reportRequest('error', url, 0, error.message); throw error; }); }; function reportRequest(type, url, status, response) { // 这里进行上报,可以通过fetch或其他方式发送上报请求 console.log(`Request ${type}: ${url}, Status: ${status}, Response: ${response}`); // 例子:fetch('/report', { method: 'POST', body: JSON.stringify({ type, url, status, response }) }) } })();看到这里,其实实现类似ARMS这样的API请求,很简单,ARMS底层一定也是依赖类似这样的代码块来实现原子能力,在此基础上,设计了一套可插拔、模块化的系统监控架构,来进行系统全方位的监听。
import TraceApiPlugin from '@ali/trace-plugin-api'; import TracePerfPlugin from '@ali/trace-plugin-perf'; import TracePvPlugin from '@ali/trace-plugin-pv'; import TraceResourceErrorPlugin from '@ali/trace-plugin-resource-error'; import TraceSdk from '@ali/trace-sdk'; import { isProd } from './constants'; const traceEnv = isProd ? 'prod' : 'test'; const trace = TraceSdk({ pid: 'relax-drink', // 必填 请在https://arms.alibaba-inc.com/arms/project/create 申请项目ID errorEnable: true, // 非必填 默认已经开启了js报错,若关闭则设置errorEnable:false env: traceEnv, // 可选 prod | pre | daily | string ignoreErrors: [/^Script error\.?$/], // 可选,用法参考"初始化配置说明" aplusUrl: 's-gm.mmstat.com', // 可选 默认s-gm.mmstat.com, 海外(新加坡)埋点配置 sg.mmstat.com plugins: [ // 使用插件 [TracePvPlugin, { autoPV: false }], [TraceApiPlugin, { sampling: 0.1 }], // 接口监控插件 [TracePerfPlugin, { enableLCP: true }], [TraceResourceErrorPlugin], // 前端资源异常监控插件 ], useSendBeacon: false, }); // 执行 trace.install();代码块中包含了ARMS的其他能力,包括js error、性能、资源的监控,我们本节只基于接口请求做示例。
function TraceSdk(config) { // 默认配置 const defaultConfig = { errorEnable: true, env: 'prod', aplusUrl: 's-gm.mmstat.com', useSendBeacon: false, ignoreErrors: [], plugins: [], }; // 合并用户提供的配置和默认配置 const finalConfig = { ...defaultConfig, ...config }; // 初始化插件 function initializePlugins(plugins) { plugins.forEach(([PluginConstructor, options]) => { const plugin = new PluginConstructor(options); plugin.initialize(); }); } // 安装SDK function install() { console.log('Trace SDK 安装中...'); setupErrorHandling(); initializePlugins(finalConfig.plugins); } // 设置错误处理(简单实现) function setupErrorHandling() { if (finalConfig.errorEnable) { window.addEventListener('error', (event) => { const errorMessage = event.message; if ( !finalConfig.ignoreErrors.some((regex) => regex.test(errorMessage)) ) { console.error('捕获到错误:', errorMessage); // 可在此执行错误报告逻辑 } }); } } // 公开的API return { install, }; } // 示例插件构造函数(模拟) function TraceApiPlugin(options) { this.options = options || {}; this.initialize = function () { const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url, async, user, pass) { this._url = url; // 保存请求的URL return originalOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function (body) { this.addEventListener('load', function () { if (this.status >= 200 && this.status < 300) { // 请求成功,上报成功信息 reportRequest('success', this._url, this.status, this.responseText); } else { // 请求失败,上报失败信息 reportRequest('failure', this._url, this.status, this.responseText); } }); this.addEventListener('error', function () { // 请求错误,上报错误信息 reportRequest('error', this._url, this.status, this.statusText); }); return originalSend.apply(this, arguments); }; function reportRequest(type, url, status, response) { // 这里进行上报,可以通过fetch或其他方式发送上报请求 console.log( `Request ${type}: ${url}, Status: ${status}, Response: ${response}`, ); // 例子:fetch('/report', { method: 'POST', body: JSON.stringify({ type, url, status, response }) }) } }; }如果想启用更多的能力,则ARMS内部肯定会有类似TraceJsErrorPlugin、TracePerfPlugin等,直接在初始化阶段注入到TraceSDK实例初始化即可。