“Script error.”的产生原因和解决办法

背景

在 script 标签中加载托管到第三方 cdn 上的资源的时候,可能会出现 script error. 的报错。由于该错误不提供完整的报错信息(错误堆栈),问题的排查往往无从下手。

script error. 生成的原因

这个错误也叫做"跨域错误",一般情况会发生在 script 标签中加载托管到第三方 cdn 上的资源的时候,常用的场景是在 cdn 上托管 js资源。

方便大家理解,假设下面 html 页面部署在 http://127.0.0.1:5500 (可以在 vscode 使用 live server 生成)域名下:

HTML 复制代码
<!doctype html>
<html>

<head>
  <title>Test page in http://test.com</title>
</head>

<body>
  <script src="https://cdn.jsdelivr.net/gh/RED523/frontend-cdn@v1.0.0/js/app.js"></script>
  <script>
    window.onerror = function (message, url, line, column, error) {
      console.log(message, url, line, column, error);
    }
    foo();
  </script>
</body>

</html>

假设 foo 方法里面调用了一个未定义的 bar 方法

JS 复制代码
function foo() {
  bar(); // ReferenceError: bar is not a function
}

运行页面之后,捕获到的异常如下

vbscript 复制代码
Script error.  0 0 null

其实这边并不是 JavaScript 的一个 bug,这是浏览器安全策略的一个表现,浏览器会刻意隐藏其他域的 js 资源抛出的错误,这样可以有效避免敏感信息无意中被不可控的第三方脚本捕获。因此,浏览器只允许同域下的脚本捕获到具体的错误信息,而其他脚本只知道发生了错误,但是不知道错误的具体细节。

解决方式

解法一:开启跨域资源共享 CORS

为捕获跨域 JavaScript 的错误,可以执行以下两个步骤

  1. 步骤 1:添加 crossorigin="anonymous 属性
j's 复制代码
<script src="https://cdn.jsdelivr.net/gh/RED523/frontend-cdn@v1.0.0/js/app.js" crossorigin="anonymous"></script>

此步骤的作用是告知浏览器以匿名的方式获取目标脚本。也就是请求资源的时候不会向服务器发送潜在的用户身份信息(cookie,HTTP 证书等)

  1. 添加跨域 HTTP 请求头
arduino 复制代码
Access-Control-Allow-Origin: * 
// 或者
Access-Control-Allow-Origin: http://127.0.0.1:5500/

说明:大部分主流CDN默认添加了Access-Control-Allow-Origin属性。以下是 jsdelivr 的示例:

完成上述两个步骤之后,就可以通过 window.onerror 捕获跨域 js 文件的错误了。回到前面的案例,重新运行页面之后,捕获到的结果是:

ruby 复制代码
Uncaught ReferenceError: bar is not defined, https://cdn.jsdelivr.net/gh/RED523/frontend-cdn@v1.0.0/js/app.js, 2, 3, ReferenceError: bar is not defined

解法二(可选):使用 try catch

如果难以在 HTTP 请求头中添加跨域属性,可以使用 try catch 的方式

之前的案例,添加 try catch

html 复制代码
<!doctype html>
<html>
<head>
  <title>Test page in http://test.com</title>
</head>
<body>
  <script src="https://cdn.jsdelivr.net/gh/RED523/frontend-cdn@v1.0.0/js/app.js"></script>
  <script>
  window.onerror = function (message, url, line, column, error) {
    console.log(message, url, line, column, error);
  }
  try {
    foo(); // 调用app.js中定义的foo方法
  } catch (e) {
    console.log(e);
    throw e; // 重新抛出错误以触发全局window.onerror
  }
  </script>
</body>
</html>

运行页面之后,捕获到的结果是:

可见,try catch 中的 log 语句捕获到了"完整"的报错信息,window.onerror 也可以捕获完整的信息。尽管这种方式也可以捕获完整的信息,但推荐采用解法 1。

留个疑问

错误代码最终是来自跨域的 js 文件,那么有人知道,window.onerror里面,ur l参数,为什么不是打印 cdn.jsdelivr.net/gh/RED523/f... 呢?

相关推荐
Byron070728 分钟前
从 0 到 1 搭建 Vue 前端工程化体系:提效、提质、降本实战落地
前端·javascript·vue.js
哆啦code梦32 分钟前
前端存储三剑客:localStorage、sessionStorage与Cookie解析
前端·前端存储
徐小夕@趣谈前端1 小时前
Web文档的“Office时刻“:jitword共建版2.0发布!让浏览器变成本地生产力
前端·数据结构·vue.js·算法·开源·编辑器·es6
Data_Journal1 小时前
如何使用 Python 解析 JSON 数据
大数据·开发语言·前端·数据库·人工智能·php
德育处主任Pro1 小时前
纯前端网格路径规划:PathFinding.js的使用方法
开发语言·前端·javascript
墨笔.丹青1 小时前
基于QtQuick开发界面设计出简易的HarmonyUI界面----下
开发语言·前端·javascript
董世昌411 小时前
深度解析浅拷贝与深拷贝:底层逻辑、实现方式及实战避坑
前端·javascript·vue.js
扶苏10021 小时前
vue使用event.dataTransfer实现A容器数据拖拽复制到到B容器
前端·vue.js·chrome
David凉宸1 小时前
Vue 3 项目的性能优化策略:从原理到实践
前端·vue.js·性能优化
小马_xiaoen2 小时前
Proxy 与 Reflect 从入门到实战:ES6 元编程核心特性详解
前端·javascript·ecmascript·es6