前端 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的错误)。例如,语法错误、运行时错误等。通过它可以方便地集中管理页面的错误处理。

不能捕获的错误

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

示例:使用 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;
        };

        // 这里故意使用一个未定义的函数调用
        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 参数说明

  • 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/awaitsetTimeoutsetInterval 中的异步错误处理

使用 .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();

setTimeoutsetInterval 中的错误处理

setTimeoutsetInterval 是异步的,它们的回调函数中的错误可以再回调函数内部使用 try...catch 来捕获这些错误。

示例:setTimeout 错误处理

setTimeout(() => {
    try {
        // 模拟错误
        nonExistentFunction();  // ReferenceError
    } catch (error) {
        console.error("Caught error in setTimeout:", error);
    }
}, 1000);

五、补充:全局监听未处理的 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 错误。
  • try...catch

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

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

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

    • 用途:用于捕获未处理的 Promise 错误,确保所有异步错误都有处理。
    • 应用:监听未被 .catch() 捕获的 Promise 拒绝错误。
posted @ 2024-12-17 10:24  雪旭  阅读(10)  评论(0编辑  收藏  举报