今天闲着没事,于是想试一下在React应用中使用Web Worker,用来测试Web Worker运行在单独的线程中,不影响主线程。
于是我用Create React App生成了一个React初始工程,然后在src
目录添加了一个文件worker.js
:
let i = 0;
while (i < 200000) {
postMessage("Web Worker Counter: " + i);
i++;
}
接着,我在App.js
写了一些简单的代码:
import React, { useState } from "react";
import "./App.css";
let worker;
function App() {
const [workerOutput, setWorkerOutput] = useState("");
const [mainThreadOutput, setMainThreadOutput] = useState("");
const testWorker = () => {
if (typeof Worker !== "undefined") {
if (typeof worker === "undefined") {
worker = new Worker('./worker.js');
}
worker.onmessage = function (event) {
setWorkerOutput(event.data);
};
} else {
setWorkerOutput("Web Workers are not supported in your browser");
}
};
const terminateWorker = () => {
worker && worker.terminate();
worker = undefined;
};
const testMainThread = () => {
for (let i = 0; i < 2000000; i++) {
setMainThreadOutput("Main Thread Counter: " + i);
}
};
return (
<div className="App">
<div className="output-cont">
<button onClick={testWorker}>start worker</button>
<h3 id="workerOutput">{workerOutput}</h3>
<button onClick={terminateWorker}>terminate worker</button>
</div>
<br />
<div className="output-cont">
<button onClick={testMainThread}>start blocking main thread</button>
<h3 id="mainThreadOutput">{mainThreadOutput}</h3>
</div>
<br />
<div className="output-cont">
<button
onClick={() => {
alert("browser responsive!");
}}
>
test browser responsiveness
</button>
</div>
</div>
);
}
export default App;
我想测试当Web Worker中执行一些繁重的计算工作的时候,用户依然可以和页面进行交互,比如响应用户点击事件。
一切看起来都很完美!
但是当我点击start worker
按钮的时候,浏览器报告了一个错误:
Uncaught SyntaxError: Unexpected token '<'
于是我去网上寻找解决方案,最终在Create React App的GitHub仓库找到了方法。
照着他的方法,我把代码改成了这样:
const workerCode = () => {
let i = 0;
while (i < 200000) {
postMessage("Web Worker Counter: " + i);
i++;
}
};
let code = workerCode.toString();
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
const blob = new Blob([code], { type: 'application/javascript'});
const worker_script = URL.createObjectURL(blob);
module.exports = worker_script;
import React, { useState } from "react";
import "./App.css";
import worker_script from './worker'; <--- 导入
let worker;
function App() {
const [workerOutput, setWorkerOutput] = useState("");
const [mainThreadOutput, setMainThreadOutput] = useState("");
const testWorker = () => {
if (typeof Worker !== "undefined") {
if (typeof worker === "undefined") {
worker = new Worker(worker_script); <--- 使用
}
worker.onmessage = function (event) {
setWorkerOutput(event.data);
};
} else {
setWorkerOutput("Web Workers are not supported in your browser");
}
};
...剩余代码
}
export default App;
再次运行程序,这一次终于正常了。
让我们逐步看一下做了哪些修改:
const workerCode = () => {
let i = 0;
while (i < 200000) {
postMessage("Web Worker Counter: " + i);
i++;
}
};
↑首先把要在Worker中执行的代码放到一个函数里面。
let code = workerCode.toString();
↑接着把函数转为字符串。
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
↑然后截取函数体部分。
const blob = new Blob([code], { type: 'application/javascript'});
↑把这部分字符串转为Blob对象。
const worker_script = URL.createObjectURL(blob);
↑再把这个Blob对象转为DOMString。
这样,就可以在别的文件通过import
来访问这个文件对象了:
import worker_script from './worker';
由于本人水平有限,对这里面的具体技术细节还不是很了解,等我熟悉这里面的细节,我会回来更新这篇文章。
感谢阅读!