/* eslint-disable prefer-rest-params */ import type { ErrorReporter } from '../ErrorReporter'; import { ErrorReportItemData } from '../types'; import { Collector } from '@tencent/merlin-core'; import { buildIssueFromLiteAppError } from '../utils'; import { MayBeVue } from './types'; /** LiteApp 全局错误收集器 * @desc 用于兜底和发现未知错误的收集器,对于已发现的错误,尽量用 `ErrorReporter.reportError()` 来上报,而不是都依赖此兜底收集器 * * 集成了 Vue 的全局错误上报,若手动传入 Vue 实例,则以传入实例为准,否则从全局获取当前实例(全局实例仅在实例化后并被挂载到 globalThis.Vue 后才存在,这种方式一般用于在页面组件 init Reporter 的情况) * * 不要和 VueErrorCollector 一同使用 */ export class GeneralLiteErrorCollector extends Collector { private errorListener = this.privateErrorListener.bind(this); private lastCaughtError: Error | null = null; private vue?: MayBeVue; constructor(options?: { /** Vue 3 传入 vue 的 app 实例,Vue 2 传入全局 Vue 对象 */ vue?: MayBeVue; }) { super(); this.vue = options?.vue; } init(reporter: ErrorReporter) { super.init(reporter); if (this.vue) { if (this.vue.config && 'errorHandler' in this.vue.config) { this.vue.config.errorHandler = (err: Error, _vm: any, info?: string) => { this.privateErrorListener(err, info ? `vue:${info}` : 'vue'); }; } else { console.warn('[Merlin] 无法解析 Vue 版本'); } } else { const globalVue = globalThis?.Vue as any; if (globalVue) { if (typeof globalVue.getCurrentInstance === 'function' && globalVue.version?.startsWith?.('3')) { // Vue 3 const app = globalVue.getCurrentInstance(); if (app?.appContext?.config) { app.appContext.config.errorHandler = (err: Error, _vm, info?: string) => { this.privateErrorListener(err, info ? `vue:${info}` : 'vue'); }; } } else if (globalVue.version?.startsWith?.('2') && globalVue.config) { // Vue 2 globalVue.config.errorHandler = (err: Error, _vm, info?: string) => { this.privateErrorListener(err, info ? `vue:${info}` : 'vue'); }; } else { console.warn('[Merlin] 无法解析 Vue 版本'); } } } lite.addEventListener('error', this.errorListener); } destroy() { if (this.vue?.config?.errorHandler) { this.vue.config.errorHandler = undefined; } else { const globalVue = globalThis?.Vue as any; if (globalVue) { if (typeof globalVue.getCurrentInstance === 'function' && globalVue.version?.startsWith?.('3')) { // Vue 3 const app = globalVue.getCurrentInstance(); if (app?.appContext?.config) { app.appContext.config.errorHandler = undefined; } } else if (globalVue.version?.startsWith?.('2') && globalVue.config) { // Vue 2 globalVue.config.errorHandler = null; } } } lite.removeEventListener('error', this.errorListener); super.destroy(); } private privateErrorListener(error: Error, origin: string) { if (error === this.lastCaughtError) { return; } this.lastCaughtError = error || null; if (this.reporter?.logError) { console.error(origin, error); } this.reporter?.log('GeneralLiteErrorCollector.errorListener', arguments); this.reporter?.capture(buildIssueFromLiteAppError(error, origin)); } }