Web worker
Web Worker 可以开启一个独立于 Web 应用程序主执行线程的后台线程,可以在独立线程中执行费时的处理任务,使主线程的运行不会被阻塞/放慢。
worker()构造函数接收一个参数,参数为一个具名的 JavaScript 文件,该文件包含需要在 worker 中运行的代码。不能在 worker 线程操作 DOM,或使用 window 对象中的某些方法和属性。
在主线程与 worker 之间传递的数据是通过深拷贝,而不是共享来完成的。
主线程和 worker 之间数据传递:
postMessage() 双方都使用该方法发送各自的消息
onmessage 使用该事件处理函数来响应消息
Worker 的类型:
- 专用 Worker //var worker = new Worker(“./worker.js”);
- 共享 Worker //var worker = new SharedWorker(“./sharedworker.js”);
- Service Worker // navigator.serviceWorker.register(“./sw.js”)
专用 worker
一个专用 worker 仅能被生成它的脚本所使用。
//html页面主线程部分
<body>
<div>
<input id="first" type="number" /> * <input id="second" type="number" /> =
<input id="result" type="text" readonly />
<button id="button">终止worker</button>
</div>
<script>
if (window.Worker) {
var worker = new Worker("./dedicatedworker.js");
const first = document.querySelector("#first");
const second = document.querySelector("#second");
const result = document.querySelector("#result");
const changeHandle = () => {
const data = { first: first.value, second: second.value };
worker.postMessage(data); //向worker传数据
};
first.onchange = changeHandle;
second.onchange = changeHandle;
//接收worker发送的数据
worker.onmessage = function (e) {
result.value = e.data;
};
//终止worker
document.querySelector("#button").addEventListener("click", () => {
worker.terminate();//终止worker
});
}
</script>
</body>
//dedicatedworker.js部分
//接收主线程的数据,数据在e.data中
onmessage = (e) => {
const { first, second } = e.data;
let res;
if (first && second) {
res = first * second;
} else {
res = "";
}
//将处理结果发送给主线程
postMessage(res);
};
共享 worker
一个共享 worker 可以被多个脚本使用——即使这些脚本正在被不同的 window、iframe 或者 worker 访问。如果共享 worker 可以被多个浏览上下文调用,所有这些浏览上下文必须属于同源(相同的协议,主机和端口号)。
一个共享 worker 通信必须通过 port 对象供脚本与 worker 通信(在专用 worker 中这一部分是隐式进行的)。
使用 sharedworker 实现跨标签也通信:
//页面一
<body>
<input type="text" name="" id="content">
<button id="btn">发送信息</button>
<script>
const content = document.querySelector('#content');
const btn = document.querySelector('#btn');
// 创建一个 worker
const worker = new SharedWorker('worker.js');
btn.onclick = function(){
worker.port.postMessage(content.value);
}
</script>
</body>
//页面二
<body>
<script>
const worker = new SharedWorker("worker.js");
worker.port.start();
worker.port.onmessage = function(e){
if(e.data){
console.log(`来自 worker 的数据:`,e.data);
}
}
setInterval(function(){
worker.port.postMessage("get");
},1000)
</script>
</body>
//worker.js内容
var data = ""; // 存储用户发送过来的信息
onconnect = function (e) {
var port = e.ports[0];
port.onmessage = function (e) {
// 说明要将接收到数据返回给客户端
if (e.data === "get") {
port.postMessage(data);
data = "";
} else {
data = e.data;
}
};
};
Service Worker
本质上充当 Web 应用程序、浏览器与网络之间的代理服务器。旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用采取适当的动作、更新来自服务器的资源。
Service worker 仅限在 HTTPS 上运行。为了促进本地开发,浏览器也认为 localhost 是一个安全的来源。
Service Worker 实际上是浏览器和服务器之间的代理服务器,它最大的特点是在页面中注册并安装成功后,运行于浏览器后台,不受页面刷新的影响,可以监听和截拦作用域范围内所有页面的 HTTP 请求。
Service Worker 的目的在于离线缓存,转发请求和网络代理。
Service Worker 注册:navigator.serviceWorker.register
语法:navigator.serviceWorker.register(scriptURL,options)
scriptURL: service worker 脚本的 URL
options:{scope: ‘/’} 表示定义 service worker 注册范围的 url,即该 service worker 有效作用范围
使用 Service Worker 实现跨标签页通信:
//页面一
<body>
<input type="text" name="" id="content">
<button id="btn">发送数据</button>
<script>
const conetnt = document.querySelector("#content");
// 注册 service worker
navigator.serviceWorker.register('sw.js')
.then(()=>{
console.log("service worker 注册成功");
});
btn.onclick = function(){
navigator.serviceWorker.controller.postMessage({
value : content.value
})
}
</script>
</body>
//页面二
<body>
<script>
// 注册 service worker
navigator.serviceWorker.register('sw.js')
.then(()=>{
console.log("service worker 注册成功");
});
navigator.serviceWorker.onmessage = function({data}){
console.log(data);
}
</script>
</body>
//在两个页面之间,相当于一个中转站
// 消息就会到达这里
self.addEventListener("message", async (event) => {
// 首先获取到所有注册了 service worker 的客户端
const clients = await self.clients.matchAll();
clients.forEach(function (client) {
client.postMessage(event.data.value);
});
});
Service Worker 实现缓存:
const putInCache = async (request, response) => {
const cache = await caches.open("v1");
await cache.put(request, response);
};
const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
// 首先,尝试从缓存中获取资源
const responseFromCache = await caches.match(request);
if (responseFromCache) {
return responseFromCache;
}
// 然后尝试从网络中获取资源
try {
const responseFromNetwork = await fetch(request);
// 响应可能会被使用
// 我们需要将它的拷贝放入缓存
// 然后再返回该响应
putInCache(request, responseFromNetwork.clone());
return responseFromNetwork;
} catch (error) {
const fallbackResponse = await caches.match(fallbackUrl);
if (fallbackResponse) {
return fallbackResponse;
}
// 当回落的响应也不可用时,
// 我们便无能为力了,但我们始终需要
// 返回 Response 对象
return new Response("Network error happened", {
status: 408,
headers: { "Content-Type": "text/plain" },
});
}
};
self.addEventListener("fetch", (event) => {
event.respondWith(
cacheFirst({
request: event.request,
fallbackUrl: "/gallery/myLittleVader.jpg",
})
);
});