如何提前加载JS模块类以优先捕获页面错误?
可行的错误监控方案
方案1:改用同步脚本加载(最直接,无延迟)
把ErrorReporter写成普通同步脚本而非ES模块。普通<script>标签默认同步执行,放在<head>最顶部就能优先于其他代码初始化错误监听,不会错过前置错误。
步骤:
- 修改
ErrorReporter.js为非模块脚本,直接实例化并初始化:
class ErrorReporter { constructor() { this.errors = []; this.#setupListeners(); } #setupListeners() { // 监听普通脚本错误 window.addEventListener("error", (event) => { this.errors.push({ message: event.message, filename: event.filename, line: event.lineno, column: event.colno, stack: event.error?.stack }); }); // 监听未捕获的Promise错误 window.addEventListener("unhandledrejection", (event) => { this.errors.push({ type: "unhandledrejection", message: event.reason?.message || "未处理的Promise拒绝", reason: event.reason, stack: event.reason?.stack }); }); // 页面卸载时发送错误 window.addEventListener("beforeunload", () => { if (this.errors.length) { // 使用sendBeacon确保请求不被页面中断 navigator.sendBeacon("/api/report-errors", JSON.stringify(this.errors)); } }); } } // 直接实例化,自动启动监听 window.errorReporter = new ErrorReporter();
- 在HTML的
<head>最顶部引入该脚本:
<head> <script src="ErrorReporter.js"></script> <!-- 其他脚本、样式等 --> </head>
方案2:保留ES模块+临时错误缓存(兼容模块需求)
如果必须用ES模块,可通过内联脚本先注册临时错误监听,提前缓存模块加载前的错误,等模块初始化后再合并到ErrorReporter中,避免遗漏。
步骤:
- 在HTML的
<head>最顶部添加内联临时监听:
<head> <script> // 临时缓存模块加载前的错误 window.__tempErrors = []; // 临时监听普通错误 const tempErrorHandler = (event) => { window.__tempErrors.push({ message: event.message, filename: event.filename, line: event.lineno, column: event.colno, stack: event.error?.stack }); }; window.addEventListener("error", tempErrorHandler); // 临时监听Promise错误 const tempRejectionHandler = (event) => { window.__tempErrors.push({ type: "unhandledrejection", message: event.reason?.message || "未处理的Promise拒绝", reason: event.reason, stack: event.reason?.stack }); }; window.addEventListener("unhandledrejection", tempRejectionHandler); // 存下临时处理器,方便后续移除 window.__tempErrorHandlers = { tempErrorHandler, tempRejectionHandler }; </script> <!-- 加载你的模块脚本 --> <script type="module" src="ErrorReporter.js"></script> </head>
- 修改
ErrorReporter.js模块,合并临时错误并替换监听:
export class ErrorReporter { constructor() { // 合并临时缓存的错误 this.errors = [...window.__tempErrors]; // 移除临时监听,避免重复收集 const { tempErrorHandler, tempRejectionHandler } = window.__tempErrorHandlers; window.removeEventListener("error", tempErrorHandler); window.removeEventListener("unhandledrejection", tempRejectionHandler); this.#setupListeners(); // 清理临时变量 delete window.__tempErrors; delete window.__tempErrorHandlers; } #setupListeners() { // 注册正式的错误监听 window.addEventListener("error", (event) => { this.errors.push({ message: event.message, filename: event.filename, line: event.lineno, column: event.colno, stack: event.error?.stack }); }); window.addEventListener("unhandledrejection", (event) => { this.errors.push({ type: "unhandledrejection", message: event.reason?.message || "未处理的Promise拒绝", reason: event.reason, stack: event.reason?.stack }); }); window.addEventListener("beforeunload", () => { if (this.errors.length) { navigator.sendBeacon("/api/report-errors", JSON.stringify(this.errors)); } }); } } // 实例化并挂载到全局 window.errorReporter = new ErrorReporter();
关键注意事项
- 用
navigator.sendBeacon替代普通请求:beforeunload事件中,普通fetch或XMLHttpRequest可能因为页面卸载被浏览器中断,sendBeacon是浏览器专门为这类场景设计的,会异步发送请求且不阻塞页面卸载,成功率更高。 - 覆盖两种错误类型:除了
error事件(普通脚本错误),务必监听unhandledrejection事件,捕获未处理的Promise错误,避免遗漏这类常见错误。
内容的提问来源于stack exchange,提问作者Jacob Chmielowiec
相关产品推荐
相关产品推荐

