webWorker 浏览器后台的“摸鱼大师”

156 阅读4分钟

developer.mozilla.org/zh-CN/docs/…

Web Worker

Web Worker 允许你在后台线程中运行 JavaScript 代码,而不会阻塞主线程。这意味着你可以执行复杂的计算或处理大量数据,而不会影响用户界面的响应速度。Web Worker 主要用于以下场景:

  • 处理大量数据
  • 执行复杂计算
  • 处理文件操作

操作

  • 生成webworker
new Worker("worker.js") 
  • 主线程通信
myWorker.postMessage({data:buffer},[buffer]); //这里是大写M,因为这是个函数直接调用,传入参数
myWorker.onmessage = () => {}; //这里是小写的m因为这是个属性名,需要我们挂载内容
  • webworker 通信
postMessage() // 在全局对象self上调用 post / onmessage
onMessage
  • 关闭webworker
//主线程关闭
myWorker.terminate();
//worker自己关闭
(self.)close()

特性

  1. 通过 importScripts()导入脚本,生成webworker的时候选择类型classical / modules

  2. 可以通过结构化克隆或者直接转移数据操作权限(共享内存来实现获取数据)only for Transferable Objects

    1. postMessage 的第二个参数是需要转交的数据(转交数据后主线程无法访问)

Share Worker

share worker是一种特殊的webworker,api基本于webworker相同,可以认为继承自web worker,

webworker是一个脚本专用的,而share worker是可以被共享的

操作

  • 开启share worker
const myWorker = new SharedWorker("worker.js","shareWorker Name") 
  • 主线程通信
myWorker.port.start() // 如果使用addEventListner,需要手动start
myWorker.port.onmessage = () => {} // 或者👇
myWorker.port.addEventListener('message',()=>{})
myWorker.port.postMessage() 
  • share worker 通信
onconnet= (e) =>{
    const port = e.port[0] // 每一个页面使用一个port
    port.onmessage = () => {} //或者👇
    port.addEventListener)=>{})
    port.postMessage()
    port.start() //除非使用addEventlistener,否则是隐式调用的
}

特性

  • sharedWorker 是独立于页面的生命周期的,能够在多个页面之间共享内容
  • 通过 name或者url 判断是否是同一个 sharedWorker
  • chrome://inspect/#workers 可以查看当前shareWorker
  • 每一个页面使用一个port ( 需要自己标识 )

Service Worker

Service Worker 是一种特殊类型的 Web Worker,主要用于配合好CacheAPI 控制网络请求和缓存资源。它们在用户访问网站时充当代理服务器,可以拦截网络请求并根据缓存策略返回资源。Service Worker 主要用于以下场景:

  • 离线支持
  • 缓存管理
  • 后台同步
  • 推送通知

操作

  • 注册 serviceWorkerContainer.register()
  • 事件 :intall | activate | message | fetch | sync | push

注册成功后通过监听事件,拦截fetch返回cache中的信息实现细粒度的缓存( 配合Cache API )

特性

  • 事件驱动的,不使用的时候会终止
  • 使用ES6 Promise
  • 需要HTTPS支持

基于Service worker我们甚至可以开发离线应用

Worklet

worklet是浏览器的轻量级后台线程,可以分为paintWorklet , animationWorklet,audioWorklet等

worklet之间也通过消息机制传递信息

// 在主线程中注册 Paint Worklet
if (CSS.paintWorklet) {
    CSS.paintWorklet.addModule('path/to/your/paint-worklet.js');
}
// paint-worklet.js
class MyCustomPaint {
    static get inputProperties() {
        return ['--color'];
    }

    paint(ctx, geom, properties) {
        const color = properties.get('--color').toString();
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, geom.width, geom.height);
    }
}

registerPaint('my-custom-paint', MyCustomPaint);

其他

安全策略

worker具有独立的执行上下文,所以不受限于创建它的 document 的安全策略,worker 的安全策略需要在发送worker 代码的请求本身设置 Content-Security-Policy

结构化克隆算法

浏览器中用于创建副本的算法,主要用于web workers , service workers等

  • 他会深度克隆对象属性
  • 支持 Date , RegExp, Blob, File, FileList, ArrayBuffer, ArrayBufferView 等
  • 循环引用将会报错
  • Dom,Function, Error,WeakMap,WeakSet不能被结构化克隆
  • 对于复杂,大型的数据接口,性能开销比较大

在worker脚本中使用 ts

ts - js 的转译是webpack等打包工具执行的,直接使用,但是对worker传入ts需要额外的配置

  1. 编写worker的ts声明文件
declare module "*.worker.ts" {
    class WebpackWorker extends Worker {
        constructor()
    }
}

2. 配置tsconfig.json

{
"compilerOptions":{"lib":["WebWorker"]}
}

3. 以模块的形式导入TS文件并使用无参数实例化

const worker = new Worker() 

使用messageChannel通信

除了使用 web worker 提供的方式进行通信(worker.postMessage)

还能够使用 messageChannel 开启通信,这种方式能够在 iframe 之间或者 webworker 之间通信

  • 主线程创建 messageChannel
const channel = new messageChannel()
const {port1,port2} = channel // messageChannel实例固定有两个端口
const worker = new Worker('worker.js')
worker.postMessage({port:port1})
  • worker中使用
self.onmessage = (e) => {
    const {port} = e.data 
    port.onmessage = ()=>{}
    port.postMessage({})
}

部分内容来自网络,侵删 小弟才疏学浅,难免错漏,请各位读者多多担待,不吝赐教。