在Web开发中,有时我们的应用需要处理复杂的计算或大量数据的操作,由于JavaScript语言采用的是单线程,如果有多个同步计算任务执行,则在这段同步计算逻辑执行完之前,它下方的代码不会执行,从而造成了阻塞,用户的交互也可能无响应。
如果把大量耗时的计算放到 Web Worker 执行,在这段逻辑计算运行期间不影响执行下面的任务,用户的操作也可以响应,就不会出现页面卡顿的现象了。
Web Worker 的基本应用
javascript
// 创建一个 Worker 实例
const myWorker = new Worker("worker.js");
// 向 Worker 发送数据
myWorker.postMessage('Message posted to worker');
// 监听 Worker 返回的数据
myWorker.onmessage = function(event) {
console.log('Received message from worker: ' + event.data);
};
在 worker 中接收到消息后,我们可以写这样一个事件处理函数代码作为响应(worker.js
)
worker.js
onmessage = (e) => {
console.log("Message received from main script");
const workerResult = `Result: ${e.data}`;
console.log("Posting message back to main script");
postMessage(workerResult);
};
Web Worker 编译
这里以 webpack
为例。
方式一:web worker 独立编译
webpack.config.js
配置目标(target)指定环境为 webworker
,编译成一个 web worker。
webpack.config.js
module.exports = {
// ...
target: 'webworker',
};
方式二:原生 Worker 支持
当把资源的 new URL
和 new Worker
结合起来时,webpack 会自动为 web worker 创建一个新的入口点(entrypoint)。
javascript
new Worker(new URL("./worker.js", import.meta.url))
代码高亮实例
下面是一个利用 remark-shaku-code-annotate
实现代码高亮的示例。
创建代码高亮 web worker
安装包
css
npm i @stefanprobst/remark-shiki remark remark-html remark-shaku-code-annotate shiki
创建 worker.js
worker.js
import { remark } from 'remark';
import { remarkShakuCodeAnnotate } from 'remark-shaku-code-annotate';
const processor = remark().use(remarkShakuCodeAnnotate, {
theme: 'github-light',
langs: ['javascript', 'css', 'jsx', 'html', 'typescript', 'tsx'],
paths: {
themes: './static/shiki/themes',
wasm: './static/shiki/dist',
languages: './static/shiki/languages',
},
});
onmessage = (e) => {
// 接收需要高亮的代码文本
const code = e.data;
// 将代码文本高亮
processor.process(code).then((data) => {
// 将高亮后的代码文本(html)发送给主程序
postMessage(data.toString());
});
};
使用 webpack
编译
webpack.config.js
module.exports = function (env, args = {}) {
const config = {
// ...
// 编译成 web worker
target: 'webworker',
plugins: [
// ...
// 将需要用到的 shiki 下的资源文件拷贝至编译输出目录
new CopyWebpackPlugin({
patterns: [
{
from: 'shiki/dist/*',
context: path.resolve(__dirname, 'node_modules'),
to: 'static/',
},
{
from: 'shiki/languages/*',
context: path.resolve(__dirname, 'node_modules'),
to: 'static/',
},
{
from: 'shiki/themes/*',
context: path.resolve(__dirname, 'node_modules'),
to: 'static/',
},
],
}),
],
};
return config;
};
创建 worker 实例
在主程序中将编译的 web worker
拷贝至根目录的 remark-shaku-web-worker
下。
配置 webpack.config.js
webpack.config.js
module.exports = function (env, args = {}) {
const config = {
// ...
plugins: [
// ...
// 将需要用到的 shiki 下的资源文件拷贝至编译输出目录
new CopyWebpackPlugin({
patterns: [
{
from: '**/*',
context: path.resolve(__dirname, 'packages/remark-shaku-web-worker/dist'),
to: 'remark-shaku-web-worker/',
},
],
}),
],
};
return config;
};
在主程序中创建 worker 实例,将代码文本转换成高亮 html
javascript
const myWorker = new Worker('/remark-shaku-web-worker/worker.js');
myWorker.onmessage = (e) => {
// 高亮后的代码文本(html)
const textContent = e.data;
};
myWorker.postMessage('需高亮的代码文本');