在现代 Web 应用中,前端状态管理变得越来越复杂,以至于一个良好的状态管理方案成为了现代前端框架的基础。Redux 作为一种可预测的状态容器,已经成为了 React 生态系统的一个重要组件,尤其是在构建大型应用的过程中。
本文将详细介绍 Redux 数据流原理,让读者更深入地理解 Redux 的使用方式,以及如何在你的应用中使用 Redux。
Redux 数据流的基本概念
在开始深入 Redux 数据流的内部机制之前,让我们简要了解一下一些基本概念。
Action
Action 是一个普通的 JavaScript 对象,用来描述“发生了什么”这一事件。它至少包括一个 type 属性,表示这个 action 的类型。例如:
{ type: 'ADD_TODO', payload: { text: 'Learn Redux', completed: false } }
Reducer
Reducer 是处理状态更新的函数,它接受当前的状态和一个 action 作为参数,并返回一个新的状态。它通常是一个纯函数,不会修改输入参数,也不会产生副作用。
function todos(state = [], action) { switch (action.type) { case 'ADD_TODO': return [...state, action.payload] default: return state } }
Store
Store 是负责管理整个应用状态的对象,它包含了当前的状态树和一些操作状态的方法。在 Redux 中,Store 并不直接修改状态树,而是通过 Dispatcher 中间件将 action 分发给 Reducer 进行处理。
Dispatcher
Dispatcher 是 Redux 中的中间件,它负责将 action 分发给对应的 Reducer 进行处理。你可以使用 Redux 自带的 applyMiddleware 函数来创建一个 Store,这个函数接受一个或多个中间件作为参数。
通过这些基本概念,我们可以定义 Redux 数据流的工作流程:
- React 组件发出一个 action。
- Dispatcher 中间件将这个 action 分发给 Reducer 进行处理。
- Reducer 根据 action 的类型和 payload 处理状态并返回一份新的状态。
- Store 更新自己的状态树,并通知 React 组件进行重新渲染。
这个过程中不涉及任何网络请求或异步操作,因此 Redux 这种可预测的状态容器非常适合应用程序状态管理。
Redux 数据流的内部实现
在上面的过程中,我们已经了解了 Redux 数据流的基本工作流程。在实际应用中,Redux 还包含很多复杂的实现细节,下面我们将详细介绍其中的关键点。
Redux 核心 API
在开始介绍 Redux 的内部实现之前,我们首先要了解一下 Redux 提供的一些核心 API:
createStore(reducer, [preloadedState], [enhancer])
createStore 是 Redux 的核心 API,它用来创建一个 Store 对象,参数分别为 reducer、preloadedState 和 enhancer,其中 reducer 和 preloadedState 已经在上文中介绍过了,enhancer 是一个增强器函数,用于增强 Store 的功能。
combineReducers(reducers)
combineReducers 是用来合并多个 Reducer 的函数,它可以将多个 Reducer 合并为一个 Reducer,使得 Store 只需要调用一个 Reducer 即可更新整个应用状态。
applyMiddleware(...middlewares)
applyMiddleware 是 Redux 中间件的核心函数,它可以接受一个或多个中间件作为参数,并返回一个增强版的 Store,使得 Store 能够支持中间件功能。
Redux 数据流的内部实现
Redux 的内部实现是基于这些核心 API 的封装和组合实现的。在 createStore 中,Redux 使用了一个闭包来保存了 Store 的状态树和 Reducer 函数,同时还把 dispatch 和 subscribe 方法暴露给外部使用。
-- -------------------- ---- ------- -------- -------------------- --------------- --------- - -- ---------- ------- -- --- ------------ - -------------- ----- -------------- - ------- ----- --------- - -- -- -- ------ --- -------- ---------------- - ------------ - ---------------------------- ------- -------------------------- -- ----------- ------ ------ - -- -- ----- ----- -------- ------------------- - ------------------------ ------ -------- ------------- - ----- ----- - --------------------------- ----------------------- -- - - -- ------ -------- -------- ---- ----- --- --- ----- - - --------- ---------- -------- - -- ---------- - ----- - ------------------------------ --------------- - ------ ----- -
当我们调用 createStore 函数时,Redux 会创建一个带着 currentState、currentReducer 和 subscribers 属性的空对象,然后将 dispatch、subscribe 和 getState 方法挂载于这个对象上。当 enhancer 参数存在时,会先将 increase 强化一遍,再将强化后的函数赋值给 store。
接下来,让我们来看一下 combineReducers 函数的实现。
-- -------------------- ---- ------- -------- ------------------------- - ------ -------- ----------------- - --- ------- - ----- --------- - -- --- ---------- - ----- --------------------------------- -- - ----- ------- - ------------- ----- ------------------- - ---------- ----- --------------- - ---------------------------- ------- -------------- - --------------- ---------- - ---------- -- --------------- --- ------------------- -- ------ ---------- - --------- - ----- - -
combineReducers 函数接受一个对象,该对象包含了多个 Reducer。在使用 combineReducers 函数时,Redux 会根据对象的 key 选择对应的 Reducer,并将 state 对象中对应的 key 作为参数传递给这个 Reducer,这样就可以将每个 key 的状态也传递给对应的 Reducer 进行处理。
最后,让我们看一下 applyMiddleware 函数的实现:
-- -------------------- ---- ------- -------- ------------------------------- - ------ ----------- -- --------- --------------- -- - ----- ----- - -------------------- --------------- --- -------- - -------------- --- ----- - -- ----- --------- - - --------- --------------- --------- ------ -- ---------------- - ----- - -------------------------- -- ---------------------- -------- - --------------------------------- ------ - --------- -------- - - -
applyMiddleware 函数的核心思想是将 Store 中的 dispatch 方法传给中间件,并在中间件中重写 dispatch 方法,以支持洋葱模型的中间件链操作。
在 applyMiddleware 函数中,我们将 createStore 函数作为参数,然后通过闭包的方式保存传入 createStore 函数返回的 Store 对象。接着,我们重写 dispatch 方法,将其传递给中间件进行加工,最后返回一个增强版的 Store,这个 Store 支持中间件中间链操作。
Redux 实践指南
在实际开发中,我们需要遵循以下原则来更好地使用 Redux:
- Store 应该只存储非常重要的业务数据,不要把所有的状态都放进 Store 中。
- 明确 reducer 的职责,并严格遵守设计模式。
- 避免在 reducer 中进行异步操作,这可能会导致状态更新不及时。
- 在 action 中添加 payload 属性,以存储非常重要的业务数据。
- 考虑使用 redux-thunk 来处理 action 的异步操作。
在下面这个示例中,我们将演示如何在 React 应用中使用 Redux:
-- -------------------- ---- ------- ------ - ----------- - ---- ------- ------ - -------- -------- - ---- ------------- -- ------ ----- ----- -------- - ---------- -- ------- -------- ----------- - --- ------- - ------ ------------- - ---- --------- ------ ---------- --------------- -------- ------ ----- - - -- ------ -------- -------- ------------------- --------- - ------ - ------ - ----- --------- -------- - ----- --------- - - - -- ----- ----- ----- - ------------------ -- ----- --------- -------- ---------- ------ ------- -- - ----- ------------ - ------- -- - ---------------------- ----- ---- - ----------------------- ------------------- -------------------- - ------ - ----- -------- --------- ----- ------------------------ ------ ----------- ----------- ----------------- ----- -- -- ------ -- ------- ----------------- ------------- ------- ---- --------------- -- - --- ------------------------------ --- ----- ------ - - -- --- ----- -- ----- -------- ---------------------- - ------ - ------ ----------- - - -- --- -------- -- ----- -------- ---------------------------- - ------ - -------- ------ -- ----------------------------- - - -- --------- --------- ----- ----------------- - ------------------------ ----------------------------- -- --- -------- ----- - ------ - --------- -------------- ------------------ -- ----------- - - ------ ------- ---
在这个示例中,我们首先定义了 ADD_TODO 这个 action 类型和对应的 Reducer,然后定义了 addTodoAction 函数用来创建 ADD_TODO 类型的 action。
接着,我们创建了一个 store 对象,并使用 react-redux 包中提供的 connect 函数将 React 组件和 Redux Store 关联起来,其中 mapStateToProps 函数将 Store 中的 todos 状态映射到 props 中,mapDispatchToProps 函数将 addTodoAction 作为 props 中的函数。
最后,我们使用 Provider 组件将 App 组件包裹起来,这样,整个应用中的组件都可以通过 connect 函数关联到 Store 对象上。
总结
Redux 是一种可预测的状态容器,它将应用程序状态集中到一个单一的对象中,并让开发者通过一个叫做 Action 的普通 JavaScript 对象来描述“发生了什么”这一事件。
在使用 Redux 时,我们需要合理地定义 Reducer 做好异常处理,并遵循设计模式,同时避免在 reducer 中进行异步操作,而可以考虑使用 redux-thunk 来处理 action 的异步操作。
在实际使用中,我们可以结合 react-redux 库来方便地使用 Redux,同时,我们需要遵循上文中提到的一些原则,这样才能更好地在应用中使用 Redux。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/645067df980a9b385b974c2b