简介
Redux 是一个用于 JavaScript 应用程序的可预测状态容器。它被广泛应用于 React 应用程序中,但也可以与其他框架或库一起使用。Redux 的目标是让应用程序状态管理变得简单可扩展,方便调试,并且支持时间旅行调试等特性。本文将介绍 Redux 运行原理及其代码实现,旨在帮助读者理解 Redux 内部工作流程并能够在项目中更好地应用 Redux。
Redux 运行机制
Redux 的基本原理是建立一个单一数据源来管理整个应用程序的状态,该状态统一保存于一个 Store 对象中,并通过 reducers 来进行状态变更。这个单一源头数据的管理方式使得应用程序中所有组件的状态都可以以同一种方式访问和改变,避免了信息不一致等问题。
Redux Store
Redux 中的 Store 类似于数据中心,存储着整个应用程序的状态数据,组件可以通过 dispatch action 的方式去改变状态,也可以通过 subscribe 方法监听状态的变化。创建一个 Redux Store 的方式如下:
import { createStore } from 'redux'; const store = createStore(reducerFunction);
这里的 reducerFunction 是一个函数,用于描述应用程序状态的变化。我们后面会详细讲解它的作用。
Action
Action 是改变应用程序状态的描述,它描述了发生了什么样的变化。Action 是一个简单的 JavaScript 对象,它必须包含一个 type 属性和可选的 payload 属性。type 属性是一个字符串常量,用于描述变化的类型,payload 属性用于携带描述变化的参数。例如:
const addTodoAction = { type: 'ADD_TODO', payload: 'Learn Redux' };
Reducer
Reducer 是用于描述应用程序状态变化的函数。它接收两个参数:当前状态(state)和传入的 action。在 reducer 中,我们根据不同的 action.type 来返回不同的状态值,将会触发 Store 进行状态的更新。例如:
-- -------------------- ---- ------- ----- ------------ - - ------ -- -- -------- ------------------ - ------------- ------- - ------ ------------- - ---- ----------- ------ - ------ ---------------- --------------- -- -- -------- ---- -------- ------ ------ - -展开代码
在上面的例子中,当 ADD_TODO action 被 dispatch 时,currentState 中的 todos 数组会被更新,添加一个新的 todo。
Store.subscribe()
Store.subscribe() 方法用于监听 Store 中的状态变化。它接收一个函数作为参数,每当 Store 接收到一个新的 action 后,就会调用该函数。该函数通常被称作 listener,根据 listener 中的内容,我们可以对应用程序的状态变化做出相应的处理,例如重新渲染组件或发起网络请求等。
store.subscribe(() => { console.log('store state:', store.getState()); })
Store.dispatch()
Store.dispatch() 方法接收一个 action 作为参数,用于触发状态的变更。当 dispatch(action) 被调用时,Store 会调用传入的 reducer 函数以新的状态替换当前状态,同时通知所有注册了 listener 的函数进行更新。
store.dispatch(addTodoAction); // 触发一个 ADD_TODO action,更新状态
Middleware
在 Redux 中,Middleware 是一个扩展了 Store.dispatch() 的中间件。它允许在处理 dispatch 的 action 之前和之后添加额外的逻辑,例如记录日志或处理异步请求等。Middleware 在 Redux 中扮演着非常重要的角色,可以让我们自由定制 Store 的行为,本篇文章中不进行详细介绍。
Redux 源码解析
在 Redux 源码中,核心模块主要有三部分:createStore 函数、reducer 和 combineReducers 函数。下面我们将对这些关键组成部分的源码进行解析。
createStore()
createStore 函数接收 reducer 函数和可选的 preloadedState 参数,返回一个包含了 dispatch, getState, subscribe, replaceReducer 四个方法的 Store 对象。
-- -------------------- ---- ------- ------ ------- -------- -------------------- --------------- --------- - -- ------- -------------- --- ---------- -- ------ -------- --- ------------ - -------- - --------------- -------------- - ---------- - -- ------- -------- --- ------------ - -- ------- -------- --- ----------- - ----- --- --------------- --- -------- -- -- - ------------ - ------ ------------------------------ ---------------- - -- ------- ------- --- ----------- - ----- --- --------------- --- ------- -- -- - ------------ - --- ------------ - --------------- --- ---------------- - --- --- ------------- - ----------------- --- ------------- - ------ -------- ------------------------------ - -- -------------- --- ----------------- - ------------- - ------------------------- - - -------- ---------- - ------ ------------- - -------- ------------------- - -- ------- -------- --- ----------- - ----- --- --------------- -------- -- -- - ------------ - --- ------------ - ----- ------------------------------- ----------------------------- ------ -------- ------------- - -- --------------- - ------- - ------------ - ------ ------------------------------- ----- ----- - -------------------------------- --------------------------- --- -- - -------- ---------------- - -- ------------------------ - ----- --- ------ --- -- - -- ------- ----------- --- ------------ - ----- --- ------ --- -- - -- --------------- - ----- --- ------ --- -- - --- - ------------- - ----- ------------ - --------------------- -------- - ------- - ------------- - ------ - ----- --------- - ----------------- - --------------- --- ---- - - -- - - ----------------- ---- - ----- -------- - ------------- ----------- - ------ ------- - -------- --------------------------- - -- ------- ----------- --- ----------- - ----- --- --------------- --- ----------- -- -- - ------------ - ------- - ------------ ---------- ----- ------------------- --- - ---------- ----- ---------------- --- ------ - --------- ---------- --------- -------------- -- -展开代码
上述代码中,我们可以看到 createStore() 函数的实现还是比较简单的。主要包含四个方法 dispatch、getState、subscribe、replaceReducer,这些方法也是 Redux Store 的最核心的特性。此外,createStore() 函数中还使用了闭包存储了 createStore 函数闭包中的当前状态 currentState, 以及存储监听器的数组 currentListeners 和通过 ensureCanMutateNextListeners() 生成的数组 nextListeners。其中,nextListeners 数组是在添加或删除 listener 时进行操作,避免在循环中修改数组引起的问题。
Reducer
Reducer 用于描述应用程序状态的变化。在 Redux 中,reducer 是一个纯函数,它根据当前的状态和一个 action 来计算下一个状态,使得状态更可控也更容易测试。
-- -------------------- ---- ------- -------- ------------------ - --- ------- - ------ ------------- - ---- ----------- ------ - --------- - ----- ------------ ---------- ----- --- ---- -------------- ------ ---------------- ------ -- - -- ------ --- ------------- - ------ ----------------- ----- - ---------- --------------- --- - ------ ----- --- -------- ------ ------ - -展开代码
Reducer 将前一个状态 state 和 dispatch 过来的 action 处理后返回新的状态 nextState。为了保证一个纯函数,Reducer 函数中是禁止进行以下操作的:
- 修改传入的 state
- 执行有副作用的操作,如 async
- 调用不可变操作的 API,如 Date.now() 或 Math.random()
在 Reducer 中如果直接修改传入的 state,例如 state.push() 应该会返回下面这个错误:
Error: Reducer(...) returned undefined during initialization.
combineReducers()
combineReducers() 函数用于将多个 reducer 模块组合成一个单独的 reducer 函数。在组合 reducer 模块时,combineReducers() 函数会自动将各 reducer 模块的子状态根据 key 值进行合并。
-- -------------------- ---- ------- ------ - --------------- - ---- -------- ------ ------------ ---- ----------------- ------ ----------------------- ---- ---------------------------- ----- ----------- - ----------------- ------ ------------- ----------------- ----------------------- --- ------ ------- ------------展开代码
在 rootReducer 被执行时,redux 模块会自动调用 todosReducer 和 visibilityFilterReducer,并根据这两个 reducer 模块返回的子状态,生成一个新的状态树。
Redux 开发实践
在 Redux 的应用中,我们通常需要编写 actionCreator、reducer、组件的代码。下面我们以一个简单的待办清单为例,演示如何使用 Redux 管理应用程序的状态:
-- -------------------- ---- ------- -- ---------- ------ -------- ------------------- - ------ - ----- ----------- ---- -- - ------ -------- ----------------------- - ------ - ----- -------------- ----- -- - -- ----------- ----- ------------ - - ------ --- -- ------ -------- ------------- - ------------- ------- - ------ ------------- - ---- ----------- ------ - --------- ------ ---------------- - ----- ------------ ---------- ----- -- -- ---- -------------- ------ - --------- ------ ---------------------- ------ -- - -- ------ --- ------------- - ------ - -------- ---------- --------------- -- - ------ ----- -- -- -------- ------ ------ - - -- -------- ------ - ----------- - ---- -------- ------ - ------- - ---- ------------- ----- ----- - --------------------- ------ ------- ------ -- ------------ ------ ------ - --------- - ---- -------- ------ - ------- - ---- -------------- ------ - -------------- ---------------- - ---- ------------ ----- -------- ------- --------- - ----- - - ------ -- -- ----------------- - --- -- - --------------- ------ -------------- --- -- ------------- - -- -- - ------------------------------------------- --------------- ------ -- --- -- ---------------- - ------- -- - ----------------------------------- -- -------- - ------ - ----- ------ ------------------------ ----------------------------------- ------- ----------------------------------------- ---- ---------------------------- ------ -- - --- ----------- ----------- -- ------------------------------ ----- ----------------------- -------------- - -------------- - --------- ------------- -- ------- ----- --- ----- ------ - - - ----- --------------- - ------- -- -- ------ ----------- --- ----- ------------------ - - -------------- ---------------- -- ------ ------- -------- ---------------- ------------------ ------------展开代码
在上述代码中,action 中包含了 ADD_TODO 和 TOGGLE_TODO 两个 actionType,reducer改变对应的 newState。在 component.js 中,我们使用 connect 函数将 TodoList 组件与 Redux store 进行关联,使用 mapStateToProps 和 mapDispatchToProps 将 action 对象映射到 props 中并通过 props.addTodoAction 和 props.toggleTodoAction 来调用 action。
小结
通过对 Redux 的运行机制及源码的解析,我们了解了 Redux 实现状态管理的基本原理。Redux 提供了一种完整而简洁的方式来管理 JavaScript 应用程序的状态,能够方便地生成可靠且可拓展的应用程序。在开发实践中,请注意遵循 reducer 纯函数和避免直接修改 state 等规范。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67bb1774306f20b3a6a66fee