将 web worker 脚本放在项目代码中

1,236 阅读2分钟

本文示例:www.risu-p.com/planet/#/wo…

web worker 为 JavaScript 创造了多线程环境。主线程可以创建 worker 线程来在后台执行 耗时、高延迟、轮询 等任务。worker线程 与 主线程间通过消息通讯

学习 web worker 可以知道,new Worker(url) 用于创建一个 worker 线程,并执行url对应的 js 脚本。而分配给 Worker 线程运行的脚本文件(即url参数),需是与主线程的脚本文件同源的网路资源

既然 worker 脚本需要是一个 同源url 的 .js 文件,是不是意味只能单独部署 worker 脚本文件呢?

本文探究3个将 woker 脚本放在项目代码中,随项目一起部署的方法:

方法一:放在 /public 目录下

将 worker 脚本执行的 .js 文件作为静态资源放在项目中(例如,通常/public/assets作为存放静态资源的目录,打包时直接被拷贝到/build/assets中,具体看项目自己的配置),即可通过同源 url 直接访问

这种方法只是将 worker脚本 同项目一起部署

方法二:嵌入 <script />

new Workerurl 参数,也支持 blob url,这提供了将 worker脚本代码 放在项目代码中的另一途径

方法二、三 都基于 blob url 实现,原理就是:得到脚本代码的字符串内容,然后即可将字符串内容转换为 blob url 传给 Worker

<html>
   <body>
    <script id="worker" type="app/worker">
      self.addEventListener('message', function (event) {
        postMessage('Got a message from the main thread');
      }, false);
    </script>
  </body>
</html>

上面是一段嵌入网页的脚本,注意必须指定 <script /> 标签的 type 属性是一个浏览器不认识的值,上例是 app/worker

然后,读取到这段嵌入页面脚本的字符串内容,转换为blob url,用 Worker 来处理

const blob = new Blob([document.querySelector('#worker').textContent], {type: "application/javascript"});
const url = window.URL.createObjectURL(blob);

const worker = new Worker(url);
worker.onmessage = function (e) {
    ...
};

上面代码中,先将嵌入网页的脚本代码,转换为一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面

方法三:函数 toString()

通过得到函数的字符串内容,从而转换为二进制对象,得到 blob url

function createWorker(f) {
  const blob = new Blob(['(' + f.toString() +')()'], {type: "application/javascript"});
  const blobUrl = window.URL.createObjectURL(blob);
  const worker = new Worker(blobUrl);
  return worker;
}

/* 使用 */
createWorker(function () {
  self.addEventListener('message', function (event) {
    postMessage('Got a message from the main thread');
  }, false);
})

上述方法通过 func.toString() 得到了脚本内容的字符串版本,也可以截取得到:

// 该函数的内容,即为需要在worker中执行的脚本
const workerScriptWrapper = () => {
  self.addEventListener('message', function (event) {
    postMessage('Got a message from the main thread');
  }, false);
};

// 将上述函数转换为字符串
const wrapperCode = workerScriptWrapper.toString();
// 对字符串进行分割,取到worker执行的脚本
const workerCode = wrapperCode.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));

// 将字符串转换为Blob URL
const blob = new Blob([workerCode], {type: "application/javascript"});
const blobUrl = URL.createObjectURL(blob);