Web Worker 同源策略报错解决方案

背景

最近有朋友在研究 webpack 5 打包 umd 库的时候,发现 web worker 无法内联到一个 js 文件中,本来如果项目依赖都在一个域名下,使用没有问题。但是 web worker 有严格的同源策略限制,当使用 其他域名下的 CDN 来加载这个 umd 库时,在异步加载 web worker 文件时候会报同源策略的错误,大致的报错如下:

Uncaught DOMException: Failed to construct 'Worker': Script at 'XXX/js/worker.js' cannot be accessed from origin 'http://127.0.0.1:5500'.

因为 webpack 4 支持配置worker-loader的参数为 inline:'fallback',比如这样配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.worker\.(c|m)?js$/i,
        loader: "worker-loader",
        options: {
          inline: "fallback",
        },
      },
    ],
  },
};

截止目前发文,webpack 5 是不支持这个配置的。没有了 inline功能,只能想别的办法了。

解决方案

更新 worker 使用方式

使用 new Worker(URL) 就可以新建一个 worker 进程了,不过这个 URL 可以作为切入点,可以探索下可能的改造思路。改造之前我们熟悉下一般的使用场景。

  1. 直接引用

正常情况直接使用new Worker一个本地文件就能使用 worker 了,比如我们新建一个本地文件

// worker.js
console.log("local worker");

然后在 html 文件中引用这个 worker.js 文件

<!-- index.html -->
<script type="module">
  new Worker("./worker.js");
</script>

这样是正常输出的,请注意,浏览器控制台需要切换到 worker 进程才能看到打印。

下图展示通过 console 下方切换 JavaScript context,或者直接在 Sources 下方切换 Threads 都可以。

web worker

所以问题就出在这里,直接引用的话,本地同域的文件是没问题,如果是远程 CDN 上的文件,问题就出来了

<!-- index.html -->
<script type="module">
  new Worker(
    "https://gcore.jsdelivr.net/gh/openHacking/static-files@main/js/worker.js"
  );
</script>

运行后控制台报了同源策略的错误

Uncaught DOMException: Failed to construct 'Worker': Script at 'https://gcore.jsdelivr.net/gh/openHacking/static-files@main/js/worker.js' cannot be accessed from origin 'http://127.0.0.1:5500'.
    at http://127.0.0.1:5500/index.html:14:9

所以我们现在想办法规避掉这个错误

  1. 传入 blob

我们发现 new Worker 第一个入参是一个脚本地址,也支持 blob 地址。我们可以想办法把 js 文件的内容转化为 blob,再将 blob 转化为 URL 即可。我们先用一个纯字符串脚本做实验

<!-- index.html -->
<script type="module">
  // 字符串转化为blob
  const str2blob = (txt) => new Blob([txt]);
  // blob转化为URL
  const url = URL.createObjectURL(str2blob('console.log("string worker")'));
  // 新建Worker
  new Worker(url);
</script>

这样可以正常输出string worker

  1. 解析远程脚本

更进一步,我们的 worker 脚本不可能只有几个字符串,或者说用字符串写代码也不规范、没法调试,所以还是需要加载一个类似 worker.js 的脚本文件才可以,这里我们尝试用 fetch 解析文件为 blob

<script type="module">
  fetch(
    "https://gcore.jsdelivr.net/gh/openHacking/static-files@main/js/worker.js"
  )
    .then((response) => response.blob())
    .then((blob) => {
      const url = URL.createObjectURL(blob);
      new Worker(url);
    });
</script>

这样就实现我们的远程 CDN 加载 worker 文件规避同源策略报错了。

不使用 CDN

当然上面这个思路是基于对源代码的改造,有的情况下我们不好对源代码进行改造,那么只能将所有 js 资源文件放在项目目录中,也就是同一个域名下,根源上防止同源策略报错

去除 worker

如果你还是需要使用 CDN,又不想对源代码改动太大,那就要看你对 worker 这一块提供需求重不重要了。不重要的话,你可以将源代码中使用到 worker 的部分删除掉来规避报错。

总结

以上就是小编总结的关于 web worker 使用的一些经验,如果你有更好的发现和思路,欢迎分享出来。

参考

评论