有这么一个场景,当加载一个网页时,它突然变得无响应,直到所有的资源完全加载完毕才响应。但是,当资源加载时,用户可能无法执行页面上的某些功能,比如单击、选择或拖动元素。这个时候你可能会想到使用 Web workers帮助我们解决这个问题。
在本文中,我们将学习如何在 React 应用程序中使用web workers。我们还将学习通过 useWorkerizedReducer 在web worker 中使用 useReducer Hook。
web worker
web worker 是一个JavaScript脚本,它在后台运行,不会干扰其他脚本的执行。
因为JavaScript是单线程语言,它不能同时运行多个脚本,这对于运行大型计算脚本来说是一个问题。Web worker帮助在后台加载繁重的计算脚本,而不会影响页面的性能。
语法
const worker = new Worker(new URL("./worker.js", import.meta.url), {
type: "module",
});
Worker
构造函数接受两个参数;第一个是 worker 文件名
,第二个是worker 的类型
。类型可以是 classic
,或者是 module
。如果未指定类型,则默认类型为 classic
。
在本文中,我们将使用module
类型,因为 reducer
只能在组件中使用。要在 web worker 组件中使用 import
函数,我们必须将import.meta. URL
添加到 URL
构造函数中。
useReducer
useReducer是一个React Hook,用于存储和更新状态。它接受三个参数:reducer, initial state
,作为最后一个参数,initial function
,它是可选的:
const [state, dispatch] = useReducer(reducer, initialArg, init);
useReducer
返回一个包含当前 state
值的数组,以及一个 dispatch
函数,你可以向该 dispatch
函数提供要执行的操作。
dispatch
函数接受指定要执行的操作类型的对象。它本质上是将 action
的类型传递给 reducer
函数,而 reducer 函数用于更新 state
。
reducer 函数
reducer 是一个接受两个参数的函数,当前 state
和 action
对象。它使用接收到的 action
来确定 state
的更改并返回新 state
。
下面的代码演示了如何使用 reducer 函数来改变 state
:
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
每当 reducer
函数被 action.type
触发时,它就会将新的 state
作为 reducer
函数内部的更改返回。
action
是一种对象类型,它指示 reducer
如何更改 state
。它必须具有 type
属性。可以在条件语句中使用 action.type
来决定 state
如何更改。
为了让 reducer
函数在 web worker 中运行,我们必须使用useWorkerizedReducer
。
useWorkerizedReducer
useWorkerizedReducer
类似于 useReducer
,除了它允许 reducer 在 worker 中执行,还允许我们创建一个动态的 React 应用。
useWorkerizedReducer
允许在不影响应用程序响应的情况下将长时间运行的计算放置在 reducer 中。useWorkerizedReducer 负责向 worker 提供 useReducer 的功能。
通过将 reducer
的 state
复制到主线程,useWorkerizedReducer 在工作线程和主线程之间架起了桥梁。reducer 操作 worker 的 state 对象,使用 postMessage()
来保持复制主线程的当前状态。
实战:构建一个简单的计数器应用程序
为了学习如何在web worker中放置 Reducer,让我们创建一个简单的计数器程序,它将在当前 state 发生改变时返回。
首先,打开命令行,输入以下命令:
npx create-react-app my-app
cd my-app
npm start
在成功安装应用程序之后,我们需要将 useWorkerizedReducer 作为程序的依赖项来使用它。安装 “useWorkerizedReducer”
,在终端中执行如下命令:
npm i use-workerized-reducer
现在我们已经成功安装了useWorkerizedReducer
,让我们接着创建一个worker.js
文件。
创建 worker.js
因为我们在 worker.js 文件中使用了 reducer,所以我们将在 src 文件夹中创建 worker.js 文件: 单击“创建新文件”,将其命名为 worker.js,然后将其保存到 src 文件夹中,如下所示:
现在我们已经创建了 worker.js 文件,让我们在其中添加下面的 reducer 代码:
// worker.js
import { initWorkerizedReducer } from "use-workerized-reducer";
initWorkerizedReducer(
"counter", // reducer's name
async (state, action) => {
switch (action.type) {
case "increment":
state.counter += 1;
break;
case "decrement":
state.counter -= 1;
break;
default:
throw new Error();
}
}
);
在上面的代码中,我们从 use-workerizedreducer 中导入了initWorkerizedReducer
。
initWorkerizedReducer()
接受两个参数:
- 第一个是 reducer 的名称:counter。
- 第二个是一个异步函数。
现在我们已经准备好了 worker.js 文件,我们需要从 use- workerizedreducer /react
中导入 useWorkerizedReducer
,这让我们可以从 worker 文件中调用 reducer 函数:
// main.js
import { render, h, Fragment } from "react";
import { useWorkerizedReducer } from "use-workerized-reducer/react";
const worker = new Worker(new URL("./worker.js", import.meta.url), {
type: "module",
});
function App() {
// 一个 worker 可以包含多个不同名的 reducer
const [state, dispatch, busy] = useWorkerizedReducer(
worker,
"counter", // Reducer name
{ counter: 0 } // Initial state
);
return (
<>
Count: {state.counter}
>
);
}
export default App;
useWorkerizedReducer 函数接受三个参数并返回单个值:
- 当前state是第一个参数;
- action 是第二个参数;
- 初始状态是第三个参数。
处理的数据是 state
,dispatch
函数执行传递给 reducer 函数的action
。
Busy将一直为 true,直到 worker 的初始状态 counter 成功复制到 worker。在此之后,如果 actions
仍在处理中,则 Busy 返回 true,否则返回 false。
reducer 根据 action 类型改变状态。action 类型 increment, decrement和reset都是在 dispatch 时更新 state 的 action 类型。
初始状态为{counter: 0}
。当递增操作类型被 dispatch 时,我们简单地设置 count {state.Count + 1}
。
结尾
在这篇文章中,我们简要介绍了 web worker 和 useReducer,以及如何构造和添加 web worker 文件到 React 应用程序中。
我们还讨论了useWorkerizedReducer,它为 web worker 带来了useReducer 的功能。最后,我们介绍了在useWorkerizedReducer的帮助下在 web worker中使用 reducer。