????????Web Worker?
是2008年h5提供的新功能,每一个新功能都是为了解决原有技术的的痛点,那么这个痛点是什么呢?
JavaScript 为什么要设计成单线程?
js
的工作内容有关:js
只是用来去做一些用户交互,并呈现效果内容。dom元素
的背景色改成红色,线程二将dom元素
的背景色改为绿色,那么,到底上红色还是绿色呢?????????但是随着前端的高速发展,前端承担着越来越多的功能,有时需要执行一些复杂的计算任务,但是JavaScript的单线程一旦执行某个耗时的任务,后面的任务都会阻塞,如果在前端能够做多线程的操作,那不就解决这个问题啦,于是,于是Web Worker
就应运而生了。
????????Web Worker
可以创建另外的线程去做一些操作(比如执行一些耗时的操作),这个操作不影响js
主线程(比如UI渲染
)的执行 。Web Worker
为 Web 内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,他们可以使用XMLHttpRequest执行 I/O (尽管responseXML和channel属性总是为空)。一旦创建,一个 worker 可以将消息发送到创建它的 JavaScript 代码,通过将消息发布到该代码指定的事件处理程序(反之亦然)......
????????Web Worker
创建的一些辅助线程,分别去帮主线程分担一些复杂的、耗时的js运算,这样的话,主线程后续的代码执行就不会阻塞,当辅助线程计算出复杂耗时运算结果后,再与主线程通信,将计算出的结果告知主线程。
Web Worker
新技术价值,简而言之:提升前端代码运算执行效率
?
在
Web Worker
内
,?worker 运行在另一个全局上下文中, 有它自己的执行上下文????????workers 和主线程间的数据传递,双方都使用?postMessage()
?方法发送各自的消息,使用?onmessage
?事件处理函数来响应消息(消息被包含在?message?事件的 data 属性中)。这个过程中数据并不是被共享而是被复制。
????????一个专用 worker 仅能被生成它的脚本所使用
const myWorker = new Worker("worker.js");
myWorker.postMessage([first.value, second.value]);
myWorker.onmessage = (e) => {
console.log(e.data);
};
onmessage = function(e) {
postMessage(e.data);
}
myWorker.terminate();
????????worker 线程会被立即终止。?
?????????Worker 线程能够访问一个全局函数?importScripts()
?来引入脚本,该函数接受 0 个或者多个 URI 作为参数来引入资源
importScripts(); /* 什么都不引入 */
importScripts("foo.js"); /* 只引入 "foo.js" */
importScripts("foo.js", "bar.js"); /* 引入两个脚本 */
importScripts("//example.com/hello.js"); /* 你可以从其他来源导入脚本 */
????????将你输入的 2 个数字作乘法。输入的数字会发送给一个专用 worker,由专用 worker 作乘法后,再返回给页面进行展示。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width" />
<title>Web Workers basic example</title>
</head>
<body>
<div class="controls" tabindex="0">
<form>
<div>
<label for="number1">Multiply number 1: </label>
<input type="text" id="number1" value="0" />
</div>
<div>
<label for="number2">Multiply number 2: </label>
<input type="text" id="number2" value="0" />
</div>
</form>
<p class="result">Result: 0</p>
</div>
<script>
const first = document.querySelector('#number1');
const second = document.querySelector('#number2');
const result = document.querySelector('.result');
if (window.Worker) {
// 1.创建一个worker 指定一个js脚本的 URI 来执行 worker 线程
const myWorker = new Worker("worker.js");
[first, second].forEach(input => {
input.onchange = function() {
// 2.主线程给worker发送数据,参数是数组格式
myWorker.postMessage([first.value, second.value]);
console.log('Message posted to worker');
}
})
// 3.主线程监听worker传递过来的信息
// 数据本身在e.data中
myWorker.onmessage = function(e) {
result.textContent = e.data;
console.log('Message received from worker');
}
} else {
console.log('Your browser doesn\'t support web workers.');
}
</script>
</body>
</html>
worker.js
// 1.监听主线程发过来的数据
onmessage = function(e) {
console.log('Worker: Message received from main script');
const result = e.data[0] * e.data[1];
if (isNaN(result)) {
// 2.给主线程发送数据
postMessage('Please write two numbers');
} else {
const workerResult = 'Result: ' + result;
console.log('Worker: Posting message back to main script');
postMessage(workerResult);
}
}
?备注:?
- 在主线程中使用时,
onmessage
?和?postMessage()
?必须挂在?worker
?对象上- 而在 worker 中使用时不用这样做。原因是,在 worker 内部,worker 是有效的全局作用域,相当于js环境中的window。
一个共享 worker 可以被多个脚本使用——即使这些脚本正在被不同的 window、iframe 或者 worker 访问。
示例:
????????在这个示例中有 2 个 HTML 页面,每个页面所包含一个 JavaScript 代码,这两个脚本使用同一个 worker 来完成实际需要的运算。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width" />
<title>Shared Workers basic example</title>
</head>
<body>
<h1>共享Worker示例-页面1</h1>
<div class="controls" tabindex="0">
<form>
<div>
<label for="number1">Multiply number 1: </label>
<input type="text" id="number1" value="0" />
</div>
<div>
<label for="number2">Multiply number 2: </label>
<input type="text" id="number2" value="0" />
</div>
</form>
<p class="result1">Result: 0</p>
<p><a href="index2.html" target="_blank">Go to 示例-页面2</a></p>
</div>
<script>
const first = document.querySelector("#number1");
const second = document.querySelector("#number2");
const result1 = document.querySelector(".result1");
if (!!window.SharedWorker) {
// 1.创建一个共享worker
const myWorker = new SharedWorker("worker.js");
// 4.使用端口对象调用postMessage给worker发送信息
first.onchange = function () {
myWorker.port.postMessage([first.value, second.value]);
console.log("Message posted to worker");
};
second.onchange = function () {
myWorker.port.postMessage([first.value, second.value]);
console.log("Message posted to worker");
};
// 2.通过port端口与worker通信
// 3.通过onmessage显式的打开端口连接
myWorker.port.onmessage = function (e) {
result1.textContent = e.data;
console.log("Message received from worker");
console.log(e.lastEventId);
};
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width" />
<title>Shared Workers basic example</title>
</head>
<body>
<h1>共享Worker示例-页面2</h1>
<div class="controls" tabindex="0">
<form>
<div>
<label for="number3">Square number: </label>
<input type="text" id="number3" value="0" />
</div>
</form>
<p class="result2">Result: 0</p>
</div>
<script>
const squareNumber = document.querySelector("#number3");
const result2 = document.querySelector(".result2");
if (!!window.SharedWorker) {
// 1.创建一个共享worker
const myWorker = new SharedWorker("worker.js");
// 4.使用端口对象调用postMessage给worker发送信息
squareNumber.onchange = function () {
myWorker.port.postMessage([squareNumber.value, squareNumber.value]);
console.log("Message posted to worker");
};
// 2.通过port端口与worker通信
// 3.通过onmessage显式的打开端口连接
myWorker.port.onmessage = function (e) {
result2.textContent = e.data;
console.log("Message received from worker");
};
}
</script>
</body>
</html>
// 在父级线程中,设置 onmessage 事件处理函数后
// 会执行worker的onconnect时间
onconnect = function (event) {
// 1.使用事件的 ports 属性来获取端口并存储在变量中
const port = event.ports[0];
// 2.为端口添加一个 onmessage 处理函数用来做运算并回传结果给主线程
port.onmessage = function (e) {
const workerResult = `Result: ${e.data[0] * e.data[1]}`;
port.postMessage(workerResult);
};
};
????????在vue项目里面不能直接使用Web Worker
,要使用Web Worker
有两种方式,一种是使用worker-loader,一种是使用vue-worker,下面对这两种方法详细介绍。
使用Webpack
中的worker-loader
插件去解析Web worker
,并且在vue.config.js
中去做相应配置。
参考:
使用 Web Workers - Web API 接口参考 | MDN