前言
Redux 是一个用于应用程序状态管理的 JavaScript 库,它为应用程序中的所有组件提供了一个状态存储中心,并提供了一种可预测的状态管理机制。Redux 的核心思想是单一状态树(Single State Tree)和状态只读(State is read-only),这使得状态变化的透明性得到了很好的保障。
然而,Redux 在实践中仍然需要一些优秀的辅助工具来创建更加灵活和高效的应用程序。中间件就是这样的一个工具,本文将详细介绍 Redux 中间件的实现原理和应用。
Redux 中间件的概念
简单来说,Redux 中间件是一个在 action 被发起之后,到达 reducer 之前执行的函数。中间件可以被用来替代或者增强 Redux 的行为。Redux 中间件提供了多种有用的能力,比如日志记录、异步请求处理、捕获错误等等。
中间件是 Redux 模式的核心。Redux 模式不依赖于任何特定的“源”或“存储”实现。中间件使我们能够使用不同的源和存储实现,同时在不修改 Redux 库的源代码的情况下扩展其功能。
Redux 中间件通常采用一个高阶函数的形式进行封装:
const middleware = store => next => action => {
// 中间件逻辑
}这段代码定义了一个接收参数 store 的函数,该函数返回一个接收 next 参数的函数,最终返回一个接收 action 参数的函数。
其中:
- 参数
store是 Redux 的 store 对象,可以通过它获取和修改应用程序的状态; - 参数
next是 Redux 的dispatch函数,可以用它触发下一个中间件或者 reducer; - 参数
action是待处理的 action 对象。
Redux 中间件的应用
接下来,我们将通过一个实例来看看 Redux 中间件的应用场景。
假设我们的应用程序需要实现一个异步的获取用户列表的功能,我们可以采用下面的代码来触发 action:
dispatch({ type: 'FETCH_USERS' });为了实现异步操作,可以采用 Redux Thunk 中间件。Redux Thunk 允许您像常规 action 一样发起异步操作,而不需要在 action 创建器中添加新的 action 类型或处理逻辑。这意味着代码中不再需要写这种形式的 action:
dispatch({ type: 'FETCH_USERS_REQUEST' });
dispatch({ type: 'FETCH_USERS_SUCCESS', payload: [/* ... */] });
dispatch({ type: 'FETCH_USERS_FAILED', error: { message: '...' } });下面是如何使用 Redux Thunk 来实现异步获取用户列表:
// www.javascriptcn.com code example
const fetchUsers = () => {
return async dispatch => {
dispatch({ type: 'FETCH_USERS_REQUEST' });
const result = await api.fetchUsers();
if (result.err) {
dispatch({ type: 'FETCH_USERS_FAILED', error: { message: result.err.message } });
} else {
dispatch({ type: 'FETCH_USERS_SUCCESS', payload: result.data });
}
};
};在这里,我们定义了一个名为 fetchUsers 的 action 创建器,它返回一个函数。当调用该函数时,它将异步调用 API 并分发对应的结果 action。
通过应用 Redux Thunk 中间件,我们不仅可以实现异步操作,而且可以在其中执行许多与业务逻辑相关的操作,例如将 API 调用同步化、添加 loading 状态、记录日志信息等等。
Redux 中间件的实现原理
现在我们已经了解了 Redux 中间件的基本概念和应用场景,接下来让我们来看看它的实现原理。
首先,我们需要明确一个事实:在 Redux 中间件机制中,每一个中间件都被设计成需要将 next 函数作为参数,并且每个中间件必须调用这个函数,以便将流程控制权交给下一个中间件或者 reducer。
具体来说,当我们在 store 中通过 store.dispatch 发起一个 action 时,实际上是执行了以下操作:
- 将 action 对象传递给第一个中间件函数;
- 第一个中间件函数可以选择对 action 进行修改、忽略它、分发多个 action 或者使用异步方式处理它;
- 如果第一个中间件函数没有将 action 对象分发到下一个中间件或者 reducer 中,则 action 被阻截并停止流程;
- 如果第一个中间件函数调用了
next(action)函数,则将控制权移交给下一个中间件函数; - 最终,当所有的中间件函数执行完毕,最后一个中间件函数调用
next(action)函数时,Redux 中间件的控制权传递给了 reducer 函数,从而更新了应用程序的状态。
因此,如果我们需要创建一个中间件,我们需要编写一个类似于下面的函数:
const middleware = store => next => action => {
// 处理 action 对象,可以对它进行增删改查操作
// 可以对 action 进行异步处理,然后再调用 next(action)
// 如果不调用 next(action),则 action 将被拦截
// 返回值可以是任意类型,但通常是 action 对象,next(action) 的返回值,或者 Promise 对象
};在这个函数中,我们可以对 action 进行所有的增删改查操作,也可以使用异步方式处理它。随后,将 action 对象传递给下一个中间件或 reducer。
为了使一个中间件能够接收和分发多个 action,我们通常需要将传入的 action 对象进行修改。并且,为了完成我们自己的异步流程,我们通常需要在调用 next(action) 函数之前提前执行一些操作。
比如,下面的代码演示了如何实现一个名为 logger 的中间件,它也是最常用的中间件之一:
const logger = store => next => action => {
console.log('dispatching', action);
const result = next(action);
console.log('new state', store.getState());
return result;
};在这里,我们打印了被分发的 action 对象和更新后的 store 对象,因此,当我们应用这个中间件时,将会在控制台中看到这些日志信息。
最后,需要注意的是,中间件是可以相互组合的。当应用多个中间件时,将按照它们的顺序执行。同时,第一个中间件函数的返回值将被传递给第二个中间件函数,以此类推。这样以来,每个中间件都可以取得上一个中间件的输出,并继续处理它到下一个中间件函数。
结语
本文介绍了 Redux 中间件的概念、应用场景和实现原理。中间件是 Redux 模式的核心之一,它为 Redux 库增加了很多灵活性和可扩展性。在应用程序中,中间件可以用来管理异步操作、调试日志、处理异常等各种复杂的任务。
当然,除了 Redux 自带的中间件,还有很多开源的第三方中间件可供选择,例如 Redux Saga、Redux Observable、Redux Throttle Debounce 等等。选择合适的中间件,可以大大提高我们的开发效率。
最后,当我们使用 Redux 中间件的时候,需要注意:中间件的使用应该是根据实际需要进行的,并非所有的应用都需要中间件。高度的自动化并不一定是最佳的实践。我们应该谨慎地选择和使用中间件,以有效地维护我们的应用程序。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d75f46a941bf7134d42854