美文网首页
Web Woeker和Shared Worker的使用以及案例

Web Woeker和Shared Worker的使用以及案例

作者: 鹏多多 | 来源:发表于2023-10-31 16:44 被阅读0次

1、前言

最近做的项目出现了界面卡顿的问题,经过一番排查,发现是因为有个数据做了一些格式化和生成转换,本来只有 1000 条数据,处理完之后变成了 N 万条数据(业务需求),导致页面渲染很慢,甚至会崩溃。于是就想着优化一下。初始化的时候不加载,等需要的时候,再使用 Web Worker 来处理数据,避免主线程卡顿。

2、介绍 Web Worker

在介绍之前,先说一下Web Worker是为什么而诞生的。

因为 JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。

Web Worker 的作用,就是为 JavaScript 创造多线程环境。它是 HTML5 标准的一部分,它赋予了开发者利用 JavaScript 操作多线程的能力。允许主线程创建 Worker 线程,将一些任务分配给 Worker 线程运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

3、使用须知及兼容性

在使用 Worker 前,需要先了解一些规则和浏览器的兼容性,避免出现一些问题。

3.1、使用须知

  1. 资源耗费:Worker 线程一旦新建成功就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是也造成了 Worker 比较耗费资源,建议使用完毕就关闭。

  2. 同源限制:分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

  3. DOM 限制:Worker 线程所在的全局对象是 self,它与主线程不一样,无法读取主线程所在网页的 window,DOM,document,parent 等全局对象,但可以读取主线程的:navigator 和 location 对象。

  4. 脚本限制:Web Worker 中可以使用 XMLHttpRequest 和 Axios 发送请求。

  5. 通信联系:Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

  6. 文件限制:Worker 线程中无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

3.2、兼容性

浏览器 兼容性 最低兼容版本
Chrome 完全兼容 4.0 (2008 年)
Firefox 完全兼容 3.5 (2009 年)
Safari 完全兼容 3.1 (2007 年)
Edge 完全兼容 79 (2020 年)
IE 部分兼容 10 (2012 年)
Opera 完全兼容 10.5 (2010 年)

4、使用 Web Worker

直接使用 JavaScript 原生的 Worker()构造函数,它的参数如下:

参数 说明
path 有效的 js 脚本的地址,必须遵守同源策略
options.type 可选。用以指定 worker 类型。该值可以是 classic 或 module,默认 classic
options.credentials 可选。指定 worker 凭证。该值可以是 omit, same-origin,或 include。如果未指定,或者 type 是 classic,将使用默认值 omit (不要求凭证)
options.name 可选。在 DedicatedWorkerGlobalScope 的情况下,用来表示 worker 的 scope 的一个 DOMString 值,主要用于调试目的。

4.1、创建 Web Worker

主线程:

const myWorker = new Worker('/worker.js')

// 接收消息
myWorker.addEventListener('message', (e) => {
    console.log(e.data)
})

// 向 worker 线程发送消息
myWorker.postMessage('Greeting from Main.js')

4.2、与主线程通信

worker 线程:

// 接收到消息
self.addEventListener('message', (e) => {
    console.log(e.data)
})

// 一顿计算后 发送消息
const calculateDataFn = () => {
    self.postMessage('ok')
}

4.3、终止 Web Worker

两个线程里都可以操作,自由选择。

  • 在主线程中操作:
// 创建worker
const myWorker = new Worker('/worker.js')
// 关闭worker
myWorker.terminate()
  • 在 worker 线程中操作:
self.close()

4.4、监听错误信息

Web Worker 提供了两个事件监听错误回调,error 和 messageerror。

事件 描述
error 当 worker 内部出现错误时触发
messageerror 当 message 事件接收到无法被反序列化的参数时触发
  • 在主线程中操作:
// 创建worker
const myWorker = new Worker('/worker.js')

myWorker.addEventListener('error', (err) => {
    console.log(err.message)
})

myWorker.addEventListener('messageerror', (err) => {
    console.log(err.message)
})
  • 在 worker 线程:
self.addEventListener('error', (err) => {
    console.log(err.message)
})
self.addEventListener('messageerror', (err) => {
    console.log(err.message)
})

5、使用 Shared Worker

SharedWorker 允许多个页面共享同一个后台线程,从而实现更高效的资源利用和协同计算。如下,是一个例子,page1page2 共享一个后台线程:

  • sharedWorker.js
/**
 * @description 所有连接这个worker的集合
 */
const portsList = []

/**
 * @description 连接成功回调
 */
self.onconnect = (event) => {
    // 当前触发连接的端口
    const port = event.ports[0]
    // 添加进去
    portsList.push(port)
    // 接收到消息的回调
    port.onmessage = (event) => {
        // 获取传递的消息
        const { message, value } = event.data
        // 计算
        let result = 0
        switch (message) {
            case 'add':
                result = value * 2
                break
            case 'multiply':
                result = value * value
                break
            default:
                result = value
        }
        // 给所有连接的目标发送消息
        portsList.forEach((port) => port.postMessage(`${message}结果是:${result}`))
    }
}
  • sharedWorkerHook.js
const sharedWorker = new SharedWorker(new URL('../../utils/webworker.js', import.meta.url), 'test')

export default sharedWorker
  • page1
<template>
  <div @click="sendMessage">点击1</div>
</template>

<script>
import sharedWorkerHook from './sharedWorkerHook'

export default {
  name: '',
  data() {
    return {}
  },
  computed: {},
  created() {},
  mounted() {
    sharedWorkerHook.port.start()
    // 接收SharedWorker返回的结果
    sharedWorkerHook.port.onmessage = event => {
      console.log(event.data)
    }
  },
  methods: {
    sendMessage() {
      sharedWorkerHook.port.postMessage({ message: 'add', value: 1 })
    }
  }
}
</script>
  • page2
<template>
  <div @click="sendMessage">点击2</div>
</template>

<script>
import sharedWorkerHook from './sharedWorkerHook'

export default {
  name: '',
  data() {
    return {}
  },
  computed: {},
  created() {},
  mounted() {
    sharedWorkerHook.port.start()
    // 接收SharedWorker返回的结果
    sharedWorkerHook.port.onmessage = event => {
      console.log(event.data)
    }
  },
  methods: {
    sendMessage() {
      sharedWorkerHook.port.postMessage({ message: 'multiply', value: 1 })
    }
  }
}
</script>

4.5、调试 Shared Worker

sharedWorker 中调试,使用 console 打印信息,不会出现在主线程的的控制台中,需要在 Chrome 浏览器地址栏输入 chrome://inspect/,进入调试面板才能看到,步骤如下:

  1. 在 Chrome 浏览器地址栏输入 chrome://inspect,并回车进入
  2. 左边菜单栏,点击 sharedWorker
  3. 右边菜单栏,点击 inspect,即可打开调试面板
调试 Shared Worker

6、使用中的一些坑

在使用中,虽然查阅了一些文档和博客,但是还是出现了以下问题,记录一下。

6.1、Web Woeker 中引入了其余文件

有一些场景,需要放到 worker 进程去处理的任务很复杂,就会引入其余文件,这时候,可以在worker线程中利用importScripts()方法加载我们需要的js文件

importScripts('./utils.js')

如果引入的是ESModule模式,需要在初始化的时候,指定type的模式。

const worker = new Worker('/worker.js', { type: 'module' })

6.2、在 WebPack 或 Vite 中使用

在webpack和vite中使用,步骤如下:

6.2.1、webpack中使用

第一步:安装插件:worker-plugin

npm install worker-plugin -D

第二步:在vue.config.js的configureWebpack.plugins中配置

const WorkerPlugin = require('worker-plugin')

module.exports = {
  outputDir: 'dist',
  // 其余配置......
  configureWebpack: {
    devServer: {
      open: false,
      host: 'localhost',
      // 其余配置......
    },
    plugins: [
      // 其余配置......
      new WorkerPlugin()
    ]
  }
}

第三步:使用

const webWorker = new Worker(new URL('../utils/worker.js', import.meta.url), {
  type: 'module'
})

5.2.2、vite中使用

第一步:安装插件:worker-plugin

npm install worker-plugin -D

第二步:在vite.config.js的plugins中配置

import worker from 'worker-plugin'

const viteConfig = defineConfig((mode: ConfigEnv) => {
    return {
        plugins: [vue(), worker()],
        server: {
            host: '0.0.0.0',
            port: 7001,
            open: true
        }
    }
})

export default viteConfig

第三步:使用

const webWorker = new Worker(new URL('../utils/worker.ts', import.meta.url), {
  type: 'module'
})

5.3、sharedWorker的引入问题

在使用sharedWorker的过程中,发现sharedWorker进程里,始终只有一个实例,但是我确实在几个页面都初始化了同一个sharedWorker的js文件,最后调试发现,原因是通过插件引入之后名字变了,一个是xxxx0.js,一个是xxxx1.js,所以导致我每次初始化的时候,都不认为是同一个实例,所以消息无法同步。

  • 解决办法:如[使用 Shared Worker](#5-使用 Shared Worker)里的例子一样,专门用一个文件new好这个sharedWorker,然后导出,在需要的页面导入后再执行即可。

7、后语

在本文中,我们学习了Web Worker的作用和使用方法,以及如何在Vue中使用Web Worker,最后,我们还学习了Shared Worker的使用方法。

其实webworker家族里还有一位更加强大的成员,那就是Service Worker。它可以做的东西很多,比如拦截全局的fetch事件、缓存静态资源/离线缓存用于首屏加载、后台同步,消息推送等等,奈何篇幅有限,下回再做讲解。


本次分享就到这儿啦,我是@鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

PS:在本页按F12,在console中输入document.querySelectorAll('._2VdqdF')[0].click(),有惊喜哦

往期文章

个人主页

相关文章

  • web worker

    web Worker web Worker 文件单独写在一个文件中,在main worker 中 执行的文件里使用...

  • web work

    Web Worker 什么是Web worker?为什么要用它?我们必须要先了解这些,才能更好的去使用这个技术。 ...

  • Web Worker使用

    最近对Web Worker进行了系统学习,主要看了阮大的教程[https://www.ruanyifeng.com...

  • web worker的介绍和使用

    简介 什么是web worker呢?从名字上就可以看出,web worker就是在web应用程序中使用的worke...

  • web worker的使用

    web worker的使用方法 使用命令http-server启动 然后就可以通过http://127.0.0.1...

  • Web Worker的使用

    什么是 Web Worker? Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主...

  • web worker 处理多文件并行上传

    一 web worker: 什么是web worker Web Worker为Web内容在后台线程中运行脚本提供...

  • 在vue中怎么使用Web Worker

    在vue项目中不能直接使用web worker,需要使用vue-worker这个库。它提供了run、create等...

  • Javascript:Web Worker基础

    参考资料Web Workersweb worker详解Blob对象 Web Worker分为专属Worker(De...

  • 深入分析Web worker, Service worker,

    Web worker,service worker和worklet。所有这些就是我所说的“ Javascript ...

网友评论

      本文标题:Web Woeker和Shared Worker的使用以及案例

      本文链接:https://www.haomeiwen.com/subject/zzziidtx.html