前端 JavaScript 脚本加载错误处理

  在前端开发中,JavaScript 错误处理是确保应用稳定性和良好用户体验的关键。无论是加载外部脚本、执行异步操作,还是处理运行时错误,我们都需要确保错误能够被及时捕获和妥善处理。否则,未处理的错误可能导致页面崩溃或不完整的功能,影响用户体验。

本文将介绍几种常见的 JavaScript 错误处理方法,包括如何监听外部脚本的加载错误、捕获执行过程中的同步和异步错误,以及如何应对 Promise 和 async/await 中的错误。通过这些方法,我们可以提升应用的健壮性,减少因错误导致的问题。

一、使用 onerror 监听脚本加载错误

适用范围

onerror 主要用于单个 <script> 标签,用于捕获该脚本加载失败的错误。常用于处理外部 JavaScript 文件加载失败的场景。需要注意的是,onerror 仅能捕获 脚本加载失败的错误,例如文件不存在、路径错误等,不能捕获 脚本执行中的错误

跨域限制

当加载外部脚本时,如果涉及跨域请求,必须确保外部服务器设置了正确的 CORS(跨域资源共享) 头,以允许浏览器加载该脚本并触发相关的错误事件。

示例:监听外部脚本加载错误

假设我们要加载一个外部 JavaScript 文件,并希望在脚本加载失败时进行处理。我们可以在 <script> 标签中直接使用 onerror 属性来监听错误。

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Script Error Handling</title>
</head>
<body>
    <script>
        // 错误处理函数
        function handleScriptError(event) {
            console.error("Script failed to load:", event.target.src);
            alert("Sorry, the script could not be loaded.");
        }
    </script>

    <!-- 引入外部脚本 -->
    <script src="https://example.com/nonexistent.js" onerror="handleScriptError(event)"></script>
</body>
</html>
复制代码

在上面的示例中,我们尝试加载一个不存在的脚本 https://example.com/nonexistent.js。当该脚本无法加载时,onerror 事件会被触发,调用 handleScriptError 函数,输出错误信息,并通过 alert 提示用户。

二、使用 window.onerror 捕获全局错误

适用范围

window.onerror 是一个全局错误处理程序,用于捕获页面中的所有 JavaScript 错误(包含外部js的错误和异步的setTimeoutsetInterval错误)。例如,语法错误、运行时错误等。通过它可以方便地集中管理页面的错误处理。

不能捕获的错误

window.onerror 无法捕获外部资源加载错误(如 <script> 标签的加载错误)以及 Promise 错误和 try...catch 已捕获的错误

示例:使用 window.onerror 捕获全局错误。
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>window.onerror Example</title>
</head>
<body>
    <h1>Welcome to my website!</h1>
    <script>
        // 设置 window.onerror 来捕获全局的 JavaScript 错误
        window.onerror = function (message, source, lineno, colno, error) {
            console.error("Error message:", message);          // 错误信息
            console.error("Error source:", source);            // 错误发生的文件
            console.error("Error line:", lineno);              // 错误发生的行号
            console.error("Error column:", colno);             // 错误发生的列号
            console.error("Error object:", error);             // 错误对象(包含详细信息)

            // 返回 true 来表示错误已被处理(防止浏览器默认处理)
            return true;
        };

        // 这里故意使用一个未定义的函数调用
        causeError();
    </script>
</body>
</html>
复制代码

在上述示例中,window.onerror 捕获了调用 causeError() 时抛出的 ReferenceErrorwindow.onerror 提供了错误信息、错误源文件、行号、列号等详细信息,便于调试。

示例:使用 window.onerror 捕获异步操作(如 setTimeoutsetInterval)中发生的 异常

复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>window.onerror Example</title>
</head>

<body>
    <h1>Welcome to my website!</h1>
    <script>
        // 设置 window.onerror 来捕获全局的 JavaScript 错误
        window.onerror = function (message, source, lineno, colno, error) {
            console.error("Error message:", message);          // 错误信息
            console.error("Error source:", source);            // 错误发生的文件
            console.error("Error line:", lineno);              // 错误发生的行号
            console.error("Error column:", colno);             // 错误发生的列号
            console.error("Error object:", error);             // 错误对象(包含详细信息)

            // 返回 true 来表示错误已被处理(防止浏览器默认处理)
            return true;
        };

        // 这里故意使用一个未定义的函数调用
        //异步报错
        setTimeout(() => {
            nonExistentFunction()
        }, 300)
    </script>
</body>

</html>
复制代码

示例:使用 window.onerror 捕获外部的js错误。

window.onerror 必须在任何 JavaScript 错误发生之前定义,否则它无法捕获已经发生的错误。如果您将 window.onerror 放在 <script src="..."></script> 之后加载外部 JS 文件,外部文件中的错误将不会触发 window.onerror

复制代码
<script>
    // 设置 window.onerror 来捕获全局的 JavaScript 错误
    window.onerror = function (message, source, lineno, colno, error) {
        console.error("Error message:", message);          // 错误信息
        console.error("Error source:", source);            // 错误发生的文件
        console.error("Error line:", lineno);              // 错误发生的行号
        console.error("Error column:", colno);             // 错误发生的列号
        console.error("Error object:", error);             // 错误对象(包含详细信息)

        // 返回 true 来表示错误已被处理(防止浏览器默认处理)
        return true;
    };
</script>
<script src=" http://localhost:5173/src/assets/index.js"></script>
复制代码

外部的js文件,在外边js内部触发错误,也可以触发window.oerror事件

function fn() {
    throw new Error("This is a test error");
  }
fn()
export default fn;

跨域脚本的限制

如果外部脚本是跨域引入的(如 CDN 上的脚本),浏览器出于安全考虑会限制错误信息的捕获,此时 window.onerror 只能获取到模糊的错误信息:

捕获到错误: Script error.
错误文件: ""
错误位置: 0:0

解决方法:

为跨域脚本添加 CORS 响应头 和 crossorigin 属性

  1. 服务端配置

    • 确保跨域脚本的响应头包含:

Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: *

客户端配置

  • 在引入脚本时添加 crossorigin 属性:

<script src="https://cdn.example.com/external.js" crossorigin></script>

这样,window.onerror 就能捕获到完整的错误信息。

window.onerror 参数说明

  • message:错误信息(例如 Uncaught ReferenceError)。
  • source:错误发生的 JavaScript 文件的 URL。
  • lineno:错误发生的行号。
  • colno:错误发生的列号。
  • error:包含详细错误信息的 Error 对象。

通过返回 true,可以阻止浏览器的默认错误处理,避免重复输出错误信息。如果不想阻止默认行为,可以将 return true 删除。

三、使用 try...catch 捕获执行过程中的错误

适用范围

try...catch 用于捕获 JavaScript 执行过程中的错误,尤其适用于同步代码块中的错误。它对于动态加载脚本时非常有效,但 不能捕获 <script> 标签加载错误

示例:使用 try...catch 捕获执行中的错误

<script>
    try {
        // 这会抛出错误,因为 nonExistentFunction 并未定义
        nonExistentFunction(); 
    } catch (error) {
        console.error("Caught error:", error);
    }
</script>

在上面的示例中,try...catch 捕获了 nonExistentFunction() 抛出的 ReferenceError,并输出错误信息。

四、Promise、async/await 中的异步错误处理

使用 .catch() 处理 Promise 错误

当使用 Promise 时,异步操作的错误可以通过 .catch() 来捕获。.catch() 用于捕获 Promise 被拒绝(rejected)时抛出的错误。

示例:使用 Promise.catch() 处理错误

复制代码
const myPromise = new Promise((resolve, reject) => {
    // 模拟异步操作
    const success = false;
    if (success) {
        resolve('Operation succeeded');
    } else {
        reject('Operation failed');
    }
});

// 通过 catch 捕获错误
myPromise
    .then(result => console.log(result))
    .catch(error => console.error('Error caught:', error));  // 捕获错误
复制代码

如果你不使用 .catch(),而是直接使用 .then(),未处理的错误将导致 未处理的 Promise 拒绝错误,这在某些环境中可能会导致应用崩溃。

使用 async/await 捕获异步错误

async/await 是基于 Promise 的语法糖,它使得异步代码看起来更像同步代码。通常我们会在 async 函数中使用 try...catch 来捕获异步操作中的错误。

示例:async/await 的错误处理

复制代码
async function fetchData() {
    try {
        const response = await fetch('https://example.com/api');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error occurred:', error);  // 捕获错误
    }
}

fetchData();
复制代码

五、补充:全局监听未处理的 Promise 错误

除了在每个 Promise 或 async/await 中处理错误,你还可以通过监听全局的 unhandledrejection 来捕获未处理的 Promise 错误。这有助于确保所有异步错误都有处理。

示例:捕获未处理的 Promise 错误

window.addEventListener('unhandledrejection', function(event) {
    console.error('Unhandled promise rejection:', event.reason);
});

六、捕获外部 JavaScript 脚本执行时的错误

window.onerror 在 Vue 项目和Reacr项目这种框架项目中也可以用来捕获外部 JavaScript 脚本执行时的错误,包括第三方库或外部脚本的运行时错误。

适用范围:

  • 外部脚本加载错误

    • 如果一个外部脚本因为路径错误、网络问题等原因加载失败(例如返回 404 错误),window.onerror 可以捕获这些错误。
    • 但是需要注意,window.onerror 只能捕获加载错误,不会捕获其他类型的错误(例如,脚本加载成功后发生的运行时错误)。
  • 外部脚本执行错误

    • 如果外部脚本加载成功,但在执行过程中发生了 JavaScript 错误,window.onerror 也能捕获这些错误。
    • 比如,第三方脚本抛出了一个运行时错误(如 ReferenceError),window.onerror 会捕获这个错误,并提供错误信息。

重要注意事项:

  • window.onerror 捕获的是 全局错误,包括在外部脚本执行过程中抛出的错误。
  • window.onerror 不能 捕获在外部脚本加载时的 CORS(跨域资源共享)相关错误。如果外部服务器没有正确设置 CORS 头部,浏览器可能会阻止脚本的加载并抛出错误,此时需要通过 script.onerror 来捕获加载错误。
  • 对于 Promise 相关的错误,window.onerror 无法直接捕获。如果外部脚本中有 Promise 被拒绝的情况,应该使用 Promise.catch() 或者全局监听 unhandledrejection 事件来捕获。

不能捕获的错误

虽然 window.onerror 仍然可以捕获一些全局错误,但在现代框架中渲染阶段组件的错误无法捕获到。

  • 对于框架内部的错误,window.onerror 通常捕获不到
  • 框架有自己的错误处理机制
    • Vue: errorHandler
    • React: ErrorBoundary

Vue的错误处理:

Vue.config.errorHandler = (err, vm, info) => {
  // 渲染错误处理
  // err: 错误对象
  // vm: 组件实例
  // info: 错误信息
}

React的错误处理:

class ErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    // 渲染期间的错误捕获
    // error: 错误对象
    // errorInfo: 错误详细信息
  }
}

这些机制确实主要处理组件渲染过程中的错误,而不是全局的 JavaScript 执行错误。对于其他类型的错误,仍然需要使用 window.onerror 或其他全局错误捕获机制。

七、总结

  • onerror

    • 用途:用于捕获 <script> 标签的资源加载错误。
    • 限制:无法捕获脚本执行中的错误。
    • 跨域限制:加载跨域脚本时需要服务器设置 CORS 头,允许浏览器加载并触发相关的错误事件。
  • window.onerror

    • 用途:用于捕获全局(包含外部引入的js) JavaScript 错误以及异步操作(如 setTimeoutsetInterval)中发生的 异常。(如语法错误和运行时错误)。前提是它们没有被 try...catch 捕获。
    • 限制:无法捕获外部资源加载错误(如 <script> 标签的加载错误)以及 Promise 错误。
    • 跨域:为跨域脚本添加 CORS 响应头 和 crossorigin 属性
  • try...catch

    • 用途:适用于同步代码的错误处理,能够捕获执行过程中的错误。
    • 应用:适用于函数调用、代码执行中的错误,或在 setTimeoutsetInterval 等异步操作的回调中捕获错误。
    • 限制:无法捕获 <script> 标签的加载错误。
  • Promise 的 .catch()

    • 用途:用于捕获 Promise 的拒绝错误,确保异步操作中的错误得到处理。
    • 应用:捕获异步操作的失败(例如,网络请求失败)。
  • async/await 与 try...catch

    • 用途:用于捕获异步操作中的错误,尤其是在 async 函数中处理 await 操作时。
    • 应用:处理 async 函数中的异常和错误。
  • unhandledrejection

    • 用途:用于捕获未处理的 Promise 错误,确保所有异步错误都有处理。
    • 应用:监听未被 .catch() 捕获的 Promise 拒绝错误。
posted @   雪旭  阅读(272)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示