ES6+Webpack 下使用 Web Worker
大家都知道 HTML 5 新增了很多 API,其中就包括 Web Worker,在普通的 js 文件上使用 ES5 编写相关代码应该是完全没有问题了,只需要在支持 H5 的浏览器上就能跑起来。
那如果我们需要在 ES6+Webpack 的组合环境下使用 Web Worker呢?其实也很方便,只需要注意一下个别点,接下来记录一下我踩过的坑。
至于 Web Worker 的基础知识和基本 api 我就放到最后面当给还不了解或者没有系统使用过的读者们去简单阅读一下。
1. 快速创建工程环境
假设你已经有一份 ES6+Webpack 的代码工程环境,而且是可以顺利跑起来的;如果没有,可以 clone 我的 github 仓库:github.com/irm-github/…
2. 安装及使用 worker-loader
2.1 安装依赖:
$ npm install -D worker-loader
# 或
$ yarn add worker-loader --dev
复制代码
2.2 代码中直接使用 worker-loader
// main.js
var MyWorker = require("worker-loader!./file.js");
// var MyWorker = require("worker-loader?inline=true&fallback=false!./file.js");
var worker = new MyWorker();
worker.postMessage({a: 1});
worker.onmessage = function(event) { /* 操作 */ };
worker.addEventListener("message", function(event) { /* 操作 */ });
复制代码
优点:写 worker 逻辑的脚本文件可以任意命名,只要传进 worker-loader
中处理即可; 缺点:每引入一次 worker 逻辑的脚本文件,就需要写一次如上所示的代码,需要多写 N(N>=1) 次的 "worker-loader!"
2.3 在 webpack 的配置文件中引入 worker-loader
{
module: {
rules: [
{
// 匹配 *.worker.js
test: /\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
name: '[name]:[hash:8].js',
// inline: true,
// fallback: false
// publicPath: '/scripts/workers/'
}
}
}
]
}
}
复制代码
其中配置,可以设置 inline
属性为 true
将 worker 作为 blob 进行内联; 要注意,内联模式将额外为浏览器创建 chunk
,即使对于不支持内联 worker 的浏览器也是如此;若这种浏览器想要禁用这种行为,只需要将 fallback
参数设置为 false
即可。
3. 同源策略
Web Worker 严格遵守同源策略,如果 webpack 的静态资源与应用代码不是同源的,那么很有可能就被浏览器给墙掉了,而且这种场景也经常发生。对于 Web Worker 遇到这种情况,有两种解决方案。
3.1 第一种
通过设置 worker-loader
的选项参数 inline
把 worker 内联成 blob 数据格式,而不再是通过下载脚本文件的方式来使用 worker:
App.js
import Worker from './file.worker.js';
复制代码
webpack.config.js
{
loader: 'worker-loader'
options: { inline: true }
}
复制代码
3.2 第二种
通过设置 worker-loader
的选项参数 publicPath
来重写掉 worker 脚本的下载 url,当然脚本也要存放到同样的位置:
App.js
// This will cause the worker to be downloaded from `/workers/file.worker.js`
import Worker from './file.worker.js';
复制代码
webpack.config.js
{
loader: 'worker-loader'
options: { publicPath: '/workers/' }
}
复制代码
4. devServer 模式下报错 "window is not defined"
若使用了 webpack-dev-server
启动了本地调试服务器,则有可能会在控制台报错: "Uncaught ReferenceError: window is not defined"
反正我是遇到了,找了很久未果,当时还是洗了把脸冷静下来排查问题,尝试着先后在 worker-loader
、webpack-dev-server
和 webpack
的 github 仓库的 issues 里面去找,最后果然在 webpack
的 github 仓库里找到了码友的提问,官方给出了答案:
只需要在 webpack 的配置文件下的 output 下,加一个属性对:globalObject: 'this'
output: {
path: DIST_PATH,
publicPath: '/dist/',
filename: '[name].bundle.[hash:8].js',
chunkFilename: "[name].chunk.[chunkhash:8].js",
globalObject: 'this',
},
复制代码
5. Web Worker 出现的背景
JavaScript 引擎是单线程运行的,JavaScript 中耗时的 I/O 操作都被处理为异步操作,它们包括键盘、鼠标 I/O 输入输出事件、窗口大小的 resize
事件、定时器(setTimeout
、setInterval
)事件、Ajax 请求网络 I/O 回调等。当这些异步任务发生的时候,它们将会被放入浏览器的事件任务队列中去,等到 JavaScript 运行时执行线程空闲时候才会按照队列先进先出的原则被一一执行,但终究还是单线程。
平时看似够用的异步编程(promise
、async/await
),在遇到很复杂的运算,比如说图像的识别优化或转换、H5游戏引擎的实现,加解密算法操作等等,它们的不足就将逐渐体现出来。长时间运行的 js 进程会导致浏览器冻结用户界面,降低用户体验。那有没有什么办法可以将复杂的计算从业务逻辑代码抽离出来,让计算运行的同时不阻塞用户操作界面获得反馈呢?
HTML5 标准通过了 Web Worker 的规范,该规范定义了一套 api,它允许一段 js 程序运行在主线程之外的另一个线程中。工作线程允许开发人员编写能够长时间运行而不被用户所中断的后台程序, 去执行事务或者逻辑,并同时保证页面对用户的及时响应,可以将一些大量计算的代码交给web worker运行而不冻结用户界面。
5. Web Worker 的类型
之前一直认为不就那一种类型吗,哪里还会有多类型的 Worker。答案是有的,其可分为两种类型:
- 专用 Worker, Dedicated Web Worker
- 共享 Worker, Shared Web Worker
「专用 Worker」只能被创建它的页面访问,而「共享 Worker」可以在浏览器的多个标签中打开的同一个页面间共享。
在 js 代码中,Woker
类代表 Dedicated Worker
;Shared Worker
类代表 Shared Web Worker
。
下面的一些示例代码我就直接用 ES5 去写了,上面教了大家怎么使用在 ES6+Webpack 的环境下,迁移这种工作大家就当练习,多动动手。
6. 如何创建 Worker
很简单
// 应用文件 app.js
var worker = new Worker('./my.worker.js'); // 传入 worker 脚本文件的路径即可
复制代码
7. 如何与 worker 通信
就通过两个方法即可完成:
应用文件 app.js
// 创建 worker 实例
var worker = new Worker('./my.worker.js'); // 传入 worker 脚本文件的路径即可
// 监听消息
worker.onmessage = function(evt){
// 主线程收到工作线程的消息
};
// 主线程向工作线程发送消息
worker.postMessage({
value: '主线程向工作线程发送消息'
});
复制代码
worker 文件 my.worker.js
// 监听消息
this.onmessage = function(evt){
// 工作线程收到主线程的消息
};
this.postMessage({
value: '工作线程向主线程发送消息'
});
复制代码
8. Worker 的全局作用域
使用 Web Worker 最重要的一点是要知道,它所执行的 js 代码完全在另一作用域中,与当前主线程的代码不共享作用域。在 Web Worker 中,同样有一个全局对象和其他对象以及方法,但其代码无法访问 DOM,也不能影响页面的外观。
Web Worker 中的全局对象是 worker 对象本身,也即 this
和 self
引用的都是 worker 对象,说白了,就像上一段在 my.worker.js
的代码,this
完全可以换成 self
,甚至可以省略。
为便于处理数据,Web Worker 本身也是一个最小化的运行环境,其可以访问或使用如下数据:
- 最小化的
navigator
对象 包括onLine
,appName
,appVersion
,userAgent
和platform
属性 - 只读的
location
对象 setTimeout()
,setInterval()
,clearTimeout()
,clearInterval()
方法XMLHttpRequest
构造函数
9. 如何终止工作线程
如果在某个时机不想要 Worker 继续运行了,那么我们需要终止掉这个线程,可以调用在主线程 Worker 的 terminate
方法 或者在相应的线程中调用 close
方法:
应用文件 app.js
var worker = new Worker('./worker.js');
// ...一些操作
worker.terminate();
复制代码
Worker 文件 my.worker.js
self.close();
复制代码
10. Worker 的错误处理机制
具体来说,Worker 内部的 js 在执行过程中只要遇到错误,就会触发 error
事件。发生 error
事件时,事件对象中包含三个属性:filename
, lineno
和 message
,分别表示发生错误的文件名、代码行号和完整的错误消息。
worker.addEventListener('error', function (e) {
console.log('MAIN: ', 'ERROR', e);
console.log('filename:' + e.filename + '-message:' + e.message + '-lineno:' + e.lineno);
});
复制代码
11. 引入脚本与库
Worker 线程能够访问一个全局函数 importScripts()
来引入脚本,该函数接受 0 个或者多个 URI 作为参数来引入资源;以下例子都是合法的:
importScripts(); /* 什么都不引入 */
importScripts('foo.js'); /* 只引入 "foo.js" */
importScripts('foo.js', 'bar.js'); /* 引入两个脚本 */
复制代码
浏览器加载并运行每一个列出的脚本。每个脚本中的全局对象都能够被 worker 使用。如果脚本无法加载,将抛出 NETWORK_ERROR
异常,接下来的代码也无法执行。而之前执行的代码(包括使用 window.setTimeout()
异步执行的代码)依然能够运行。importScripts()
之后的函数声明依然会被保留,因为它们始终会在其他代码之前运行。
注意: 脚本的下载顺序不固定,但执行时会按照传入
importScripts()
中的文件名顺序进行。这个过程是同步完成的;直到所有脚本都下载并运行完毕,importScripts()
才会返回。
ES6+Webpack 下使用 Web Worker的更多相关文章
- Webpack 下使用 web workers 及 基本原理 和 应用场景
_ 阅读目录 一:web workers的基本原理 二:web Workers 的基本用法 三:在webpack中配置 Web Workers 四:Web Worker的应用场景 回到顶部 一:web ...
- Web Worker javascript多线程编程(一)
什么是Web Worker? web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验. 一般来说Javascript ...
- 关于Web Worker你必须知道的7件事
介绍 通过使用Web Worker, 我们可以在浏览器后台运行Javascript, 而不占用浏览器自身线程.Web Worker可以提高应用的总体性能,并且提升用户体验.如果你想在自己的Web应用中 ...
- 【转】Web Worker javascript多线程编程(一)
原文:https://www.cnblogs.com/peakleo/p/6218823.html -------------------------------------------------- ...
- 前端每周清单第 49 期:Webpack 4 Beta 尝鲜,React Windowing 与 setState 分析,Web Worker 实战
前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点:分为新闻热点.开发教程.工程实践.深度阅读.开源项目.巅峰人生等栏目.欢迎关注[前端之巅]微信公众号(ID: fron ...
- web worker 实践
1.web worker 在浏览器中JavaScript主线程与UI渲染线程是互斥的.即UI渲染线程会阻塞JavaScript线程的运行. web worker允许创建工作线程,并可以与JavaScr ...
- JavaScript多线程之HTML5 Web Worker
在博主的前些文章Promise的前世今生和妙用技巧和JavaScript单线程和浏览器事件循环简述中都曾提到了HTML5 Web Worker这一个概念.在JavaScript单线程和浏览器事件循环简 ...
- web Worker使js实现‘多线程’?
大家都知道js是单线程的,在上一段js执行结束之前,后面的js绝对不会执行,那么为什么标题说js实现‘多线程’,虽然说加了引号,可是标题也不能乱写不是,可恶的标题党? 姑且抛开标题不说,先说我们经常会 ...
- javascript 多线程Web Worker不引用外部js文件的方法
最近在Android开发中 Webview通过调用JavascriptInterface的方式与App交互 在交互的过程中,有些App上的操作时间会比较长,Web中调用的话会造成程序假死的情况 于是想 ...
随机推荐
- 学习6__STM32--SPI外设之中断收发---
<目标> STM32双机 SPI中断收发通信 <描述> # STM32双机配置为一主一从模式 # 采用主机中断发送,从机中断接收 # 收发机制采用不间断收发(发送为空就发送,接 ...
- LOJ#2983. 「WC2019」数树
传送门 抄题解 \(Task0\),随便做一下,设 \(cnt\) 为相同的边的个数,输出 \(y^{n-cnt}\) \(Task1\),给定其中一棵树 设初始答案为 \(y^n\),首先可以发现, ...
- 【疑点】js中的break,continue和return到底怎么用?
转: [疑点]js中的break,continue和return到底怎么用? 为什么要说个?好像很简单,但是我也会迷糊,不懂有时候为什么要用return,然而break和continue也经常和他放在 ...
- webpack插件自动加css3前缀
想要webpack帮忙自动加上“-webkit-”之类的css前缀,我们需要用到postcss-loader和它的插件autoprefixer 1.安装 npm i postcss-loader au ...
- 在线Python学习网站
目前我们使用的Python集成环境是Anaconda3,然后使用Jupyter Notebook和Spyder两个开发环境 Goole推出了在线的开发环境,在线网站: https://colab.re ...
- SQL记录-PLSQL事务
PL/SQL事务 数据库事务是一个工作的原子单元,其可以由一个或多个相关的SQL语句组成.所谓的原子性就是数据库的修改所带来的构成事务的SQL语句可以集体被提交,即永久到数据库或从数据库中(撤消) ...
- bzoj千题计划262:bzoj4868: [六省联考2017]期末考试
http://www.lydsy.com/JudgeOnline/problem.php?id=4868 假设 最晚出成绩的是第i天 预处理 cnt[i] 表示 有多少个学生 期望出成绩的那一天 &l ...
- 流媒体技术学习笔记之(十一)Windows环境运行EasyDarwin
流媒体平台框架下载安装 Github下载 下载地址:https://github.com/EasyDarwin/EasyDarwin/releases 解压安装 选择Windows 安装平台的安装包( ...
- html5 canvas 对角线渐变
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 用phpStorm的数据库工具来管理你的数据库
phpStorm是一个功能强大的IDE,不仅对PHP提供了支持,而且对前端HTML.CSS.JavaScript的支持也是非常不错的.此外,phpStorm还集成了很多实用的功能,下面就phpStor ...