|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8" />
|
|
|
<script>
|
|
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
|
|
CSS.supports('top: constant(a)'))
|
|
|
document.write(
|
|
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
|
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
|
|
|
|
|
var _window = typeof window !== 'undefined' ? window : {};
|
|
|
</script>
|
|
|
<!-- 早期错误捕获 - 在应用初始化之前就开始监听错误 -->
|
|
|
<script>
|
|
|
(function() {
|
|
|
'use strict';
|
|
|
|
|
|
// 错误上报接口
|
|
|
var ERROR_REPORT_API = 'https://tj-h5.hnxdfe.com/api/H5/test';
|
|
|
|
|
|
// 临时存储错误,等到应用初始化后统一上报
|
|
|
window.__ERROR_BUFFER__ = [];
|
|
|
|
|
|
// 判断是否应该忽略的错误
|
|
|
function shouldIgnoreError(error) {
|
|
|
// 忽略请求中止错误(页面跳转时常见)
|
|
|
if (error && error.errMsg === 'request:fail abort') {
|
|
|
return true;
|
|
|
}
|
|
|
if (error && error.message && error.message.includes('abort')) {
|
|
|
return true;
|
|
|
}
|
|
|
if (error && error.name === 'AbortError') {
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 发送错误到服务器
|
|
|
function sendError(errorInfo) {
|
|
|
try {
|
|
|
// 优先使用 sendBeacon(即使页面关闭也能发送)
|
|
|
if (navigator.sendBeacon) {
|
|
|
var data = JSON.stringify(errorInfo);
|
|
|
var blob = new Blob([data], { type: 'application/json' });
|
|
|
return navigator.sendBeacon(ERROR_REPORT_API, blob);
|
|
|
}
|
|
|
|
|
|
// 备用:使用 fetch
|
|
|
fetch(ERROR_REPORT_API, {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json'
|
|
|
},
|
|
|
body: JSON.stringify(errorInfo),
|
|
|
keepalive: true
|
|
|
}).catch(function() {
|
|
|
// 静默失败
|
|
|
});
|
|
|
} catch (e) {
|
|
|
// 静默失败,避免二次错误
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 构建错误信息
|
|
|
function buildErrorInfo(error, type) {
|
|
|
var errorInfo = {
|
|
|
type: type || 'error',
|
|
|
message: '',
|
|
|
stack: '',
|
|
|
url: '',
|
|
|
ua: '',
|
|
|
timestamp: new Date().toISOString(),
|
|
|
phase: 'early', // 标记为早期错误
|
|
|
rawError: null // 保留原始错误对象(用于调试)
|
|
|
};
|
|
|
|
|
|
try {
|
|
|
errorInfo.url = window.location.href || '';
|
|
|
errorInfo.ua = navigator.userAgent || '';
|
|
|
|
|
|
// 如果是 Error 实例
|
|
|
if (error instanceof Error) {
|
|
|
errorInfo.message = error.message || String(error);
|
|
|
errorInfo.stack = error.stack || '';
|
|
|
}
|
|
|
// 如果是字符串
|
|
|
else if (typeof error === 'string') {
|
|
|
errorInfo.message = error;
|
|
|
}
|
|
|
// 如果是对象
|
|
|
else if (error && typeof error === 'object') {
|
|
|
try {
|
|
|
// 检查 message 是否是 "{}" 或空字符串
|
|
|
var msg = error.message || '';
|
|
|
|
|
|
if (msg === '{}' || msg === '' || msg === 'undefined' || msg === 'null') {
|
|
|
// 尝试从其他字段获取信息
|
|
|
if (error.reason) {
|
|
|
errorInfo.message = String(error.reason);
|
|
|
} else if (error.name) {
|
|
|
errorInfo.message = error.name;
|
|
|
} else if (error.toString && typeof error.toString === 'function') {
|
|
|
var str = error.toString();
|
|
|
if (str && str !== '[object Object]') {
|
|
|
errorInfo.message = str;
|
|
|
} else {
|
|
|
errorInfo.message = '未知错误对象(无有效信息)';
|
|
|
}
|
|
|
} else {
|
|
|
// 尝试序列化对象
|
|
|
var jsonStr = JSON.stringify(error);
|
|
|
if (jsonStr && jsonStr !== '{}') {
|
|
|
errorInfo.message = jsonStr;
|
|
|
} else {
|
|
|
errorInfo.message = '错误对象为空';
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
errorInfo.message = msg;
|
|
|
}
|
|
|
|
|
|
errorInfo.stack = error.stack || '';
|
|
|
|
|
|
// 如果没有 stack,尝试构建堆栈信息
|
|
|
if (!errorInfo.stack && error.filename) {
|
|
|
errorInfo.stack = 'at ' + error.filename;
|
|
|
if (error.lineno) {
|
|
|
errorInfo.stack += ':' + error.lineno;
|
|
|
if (error.colno) {
|
|
|
errorInfo.stack += ':' + error.colno;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} catch (e) {
|
|
|
errorInfo.message = '解析错误对象失败: ' + String(e);
|
|
|
errorInfo.stack = '';
|
|
|
}
|
|
|
}
|
|
|
// 如果是 null 或 undefined
|
|
|
else if (error === null || error === undefined) {
|
|
|
errorInfo.message = '错误对象为 ' + String(error);
|
|
|
}
|
|
|
// 其他情况
|
|
|
else {
|
|
|
errorInfo.message = String(error) || '未知错误';
|
|
|
errorInfo.stack = '';
|
|
|
}
|
|
|
|
|
|
// 保留原始错误对象的前几个键(用于调试)
|
|
|
if (error && typeof error === 'object') {
|
|
|
var keys = Object.keys(error).slice(0, 5);
|
|
|
if (keys.length > 0) {
|
|
|
errorInfo.rawError = {};
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
|
var key = keys[i];
|
|
|
if (typeof error[key] !== 'function') {
|
|
|
errorInfo.rawError[key] = String(error[key]).substring(0, 100);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果 message 还是空的或 {}
|
|
|
if (!errorInfo.message || errorInfo.message === '{}' || errorInfo.message === 'undefined') {
|
|
|
errorInfo.message = '无法获取错误详情(错误对象为空或无效)';
|
|
|
}
|
|
|
} catch (e) {
|
|
|
errorInfo.message = '构建错误信息异常: ' + String(e);
|
|
|
errorInfo.stack = '';
|
|
|
}
|
|
|
|
|
|
return errorInfo;
|
|
|
}
|
|
|
|
|
|
// 捕获 JavaScript 错误
|
|
|
window.addEventListener('error', function(event) {
|
|
|
// 检查是否应该忽略此错误
|
|
|
var errorToCheck = event.error || event;
|
|
|
if (shouldIgnoreError(errorToCheck)) {
|
|
|
console.warn('[早期错误捕获] 忽略请求中止错误', errorToCheck);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
var errorInfo;
|
|
|
if (event.error) {
|
|
|
errorInfo = buildErrorInfo(event.error, 'error');
|
|
|
} else {
|
|
|
errorInfo = buildErrorInfo({
|
|
|
message: event.message,
|
|
|
filename: event.filename,
|
|
|
lineno: event.lineno,
|
|
|
colno: event.colno
|
|
|
}, 'error');
|
|
|
}
|
|
|
|
|
|
// 发送错误
|
|
|
sendError(errorInfo);
|
|
|
|
|
|
// 同时存储到缓冲区
|
|
|
window.__ERROR_BUFFER__.push(errorInfo);
|
|
|
}, true);
|
|
|
|
|
|
// 捕获 Promise rejection 错误
|
|
|
window.addEventListener('unhandledrejection', function(event) {
|
|
|
// 检查是否应该忽略此错误
|
|
|
if (shouldIgnoreError(event.reason)) {
|
|
|
console.warn('[早期错误捕获] 忽略请求中止错误', event.reason);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
var errorInfo = buildErrorInfo({
|
|
|
message: 'Promise rejection',
|
|
|
reason: event.reason
|
|
|
}, 'promise');
|
|
|
|
|
|
// 发送错误
|
|
|
sendError(errorInfo);
|
|
|
|
|
|
// 同时存储到缓冲区
|
|
|
window.__ERROR_BUFFER__.push(errorInfo);
|
|
|
});
|
|
|
|
|
|
// 上报缓冲区的错误(供后续应用调用)
|
|
|
window.flushErrorBuffer = function() {
|
|
|
if (window.__ERROR_BUFFER__ && window.__ERROR_BUFFER__.length > 0) {
|
|
|
var buffer = window.__ERROR_BUFFER__;
|
|
|
window.__ERROR_BUFFER__ = [];
|
|
|
return buffer;
|
|
|
}
|
|
|
return [];
|
|
|
};
|
|
|
|
|
|
console.log('[早期错误捕获] 已初始化');
|
|
|
})();
|
|
|
</script>
|
|
|
<title></title>
|
|
|
<!--preload-links-->
|
|
|
<!--app-context-->
|
|
|
</head>
|
|
|
<body>
|
|
|
<div id="app"><!--app-html--></div>
|
|
|
<script type="module" src="/main.js"></script>
|
|
|
</body>
|
|
|
</html>
|