首页 > 其他分享 >Web Woeker和Shared Worker的使用以及案例

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

时间:2023-11-01 16:55:06浏览次数:33  
标签:Web Worker worker js 线程 Woeker 使用

目录

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('.diggit')[0].click(),有惊喜哦


公众号

往期文章

个人主页

标签:Web,Worker,worker,js,线程,Woeker,使用
From: https://www.cnblogs.com/-pdd/p/17803523.html

相关文章

  • 创建一个Web服务器并保持其运行,可以使用Python的Flask库。以下是一个基本的示例: ```p
    创建一个Web服务器并保持其运行,可以使用Python的Flask库。以下是一个基本的示例:```pythonfromflaskimportFlask,requestimportosapp=Flask(__name__)@app.route('/webhook',methods=['POST'])defwebhook():  data=request.get_json()  #在这里添加你的......
  • 使用​​Flask​​库来创建一个Webhook服务器,该服务器可以接收HTTP请求
    在Python中,你可以使用Flask库来创建一个Webhook服务器,该服务器可以接收HTTP请求,处理请求体,并发送响应。以下是一个简单的示例:fromflaskimportFlask,request,jsonifyapp=Flask(__name__)@app.route('/webhook',methods=['POST'])defwebhook():#获取请求体中的数......
  • 图扑 HT for Web 手机端运维管理系统
    随着信息技术的快速发展,网络技术的应用涉及到人们生活的方方面面。其中,手机运维管理系统可提供数字化、智能化的方式,帮助企业和组织管理监控企业的IT环境,提高运维效率、降低维护成本、增强安全性、提升服务质量,并支持企业实现数字化转型,满足客户需求和市场竞争力至关重要。本文......
  • 图扑 HT for Web 手机端运维管理系统
    随着信息技术的快速发展,网络技术的应用涉及到人们生活的方方面面。其中,手机运维管理系统可提供数字化、智能化的方式,帮助企业和组织管理监控企业的IT环境,提高运维效率、降低维护成本、增强安全性、提升服务质量,并支持企业实现数字化转型,满足客户需求和市场竞争力至关重要。本文将......
  • 「2023·最新盘点」十大热门WebStorm主题
    WebStorm 是jetbrains公司旗下一款JavaScript开发工具。被广大中国JS开发者誉为"Web前端开发神器""最强大的HTML5编辑器""最智能的JavaSscriptIDE"等。与IntelliJIDEA同源,继承了IntelliJIDEA强大的JS部分的功能。在IDE中,开发者的感觉舒服很重要。WebStorm定制程度高,您可以按照......
  • javaweb--MyBatis
    持久层框架,用于简化JDBC开发负责将数据保存到数据库的那一层代码JavaEE三层架构:表现层、业务层、持久层免除了几乎所有JDBC代码及设置参数和获取结果集的工作。1、导入查询user表中所有的数据createDATABASEmybatis;usemybatis;droptableifexiststb_user;createtable......
  • tinygo webassembly 试用
    主要是简单测试下tinygo的使用,同时基于vite进行web的集成构建wasm生成注意只测试标注类型支持比较多,其他的就没添加,其他类型的需要自己处理,这点上wasm-pack处理的比较好main.gopackagemain //go:wasm-module//exportaddfuncadd(x,yuint32)uint......
  • java webassembly 集成试用
    wasmerio这个组织实现了不少webassembly周边的工具,以下是一个简单的java集成试用安装命令因为java包默认没有发布到中央仓库,需要自己本地安装localmavenmvninstall:install-file-Dfile=./wasmer-jni-amd64-darwin-0.3.0.jar-DgroupId=org.wasmer-Dartifac......
  • springboot web使用mybatis访问mysql库
    思想:重点:springboot项目可以配置mybatis必须的内容。默认配置文件为“main/resources/application.properties”(yml为其另一种写法,感兴趣自己去查)一切编码跟普通的mybatis相同。注意:xml文件最好放在资源文件夹resources下面,以便编译时直接复制。由此引起需要在springboot......
  • web中静态资源和动态资源的区别
    **静态资源:**可以理解为前端的固定页面,这里面包含HTML、CSS、JS、图片等等,不需要查数据库也不需要程序处理,直接就能够显示的页面,也就是说不需要从后台通过读取数据库信息就可以将在html上的所有数据全部显示出来,他的访问数据由于是不需要从数据库拉取数据,故而访问速度很快。**动态......