前言
Redux 是一个流行的前端状态管理库,它通过单向数据流的设计,让前端的数据流变得简单、可控、易于维护。本文将介绍 Redux 的源码分析,包括 Redux 运行的流程和原理解析,以及如何实现一个简单的 Redux 库。
Redux 运行流程
Redux 的运行流程可以归纳为三个步骤:定义状态、定义修改状态的行为和管理状态的改变。下面一一介绍:
1. 定义状态
Redux 的 state 可以理解为前端应用程序的状态树,它包含了所有组件所需要的数据。因为 Redux 是一个单向数据流的设计,所以 state 是只读的。我们可以通过 store.getState() 方法来获取当前状态。
2. 定义修改状态的行为
在 Redux 中,修改状态的行为被称为 Action,它是一个简单的 JavaScript 对象,包含两个属性:type 和 payload(可选项)。其中 type 用于描述这个 Action 的类型,payload 用于传递需要修改的数据。我们可以通过 store.dispatch() 方法来将一个 Action 分发给 Redux。
3. 管理状态的改变
在 Redux 中,状态的改变被称为 Reducer。Reducer 是一个纯函数,它接收当前状态和 Action 作为参数,并返回一个新的状态。Reducer 应该是无副作用的,它只处理传入的参数,不会改变任何外部的状态。Reducer 是通过 store.subscribe() 方法来注册的。
Redux 实现原理解析
了解了 Redux 的运行流程,下面我们来具体分析一下 Redux 的实现原理。
Store
在 Redux 中,Store 是 Redux 的核心部件。它包含了整个应用程序的状态树,并对外提供了以下几个 API:
- getState():获取当前的状态树;
- dispatch(action):分发一个 Action,更新状态树;
- subscribe(listener):注册一个状态改变监听器。
我们来看一下 Store 的实现。
-- -------------------- ---- ------- -------- -------------------- --------------- --------- - --- -------------- - -------- --- ------------ - --------------- --- ---------------- - --- --- ------------- - ----------------- --- ------------- - ------ -------- ------------------------------ - -- -------------- --- ----------------- - ------------- - ------------------------- - - -------- ---------- - ------ ------------- - -------- ------------------- - --- ------------ - ----- ------------------------------- ----------------------------- ------ -------- ------------- - -- --------------- - ------- - ------------ - ------ ------------------------------- ----- ----- - -------------------------------- --------------------------- --- -- - -------- ---------------- - -- --------------- - ----- --- --------------- --- --- -------- ----------- - --- - ------------- - ----- ------------ - ---------------------------- -------- - ------- - ------------- - ------ - ----- --------- - ----------------- - --------------- --- ---- - - -- - - ----------------- ---- - ----- -------- - ------------- ----------- - ------ ------- - ------ - --------- --------- ---------- --------------- -- -
可以看到,Store 有三个关键变量:currentReducer、currentState 和 currentListeners。其中 currentReducer 用于记录当前使用的 Reducer,currentState 用于记录当前的状态树,currentListeners 用于记录注册的监听器。
Store 的 subscribe 方法实际上是一个观察者模式的实现,它将注册的监听器保存在 currentListeners 数组中。每当 dispatch 发生时,它会通过 nextListeners 获取到注册的最新的监听器,然后更新 currentListeners 数组,并遍历监听器数组以执行相应的监听器方法。
dispatch 方法非常重要,它负责处理 Action,并且调用 Reducer 方法来更新状态树。当 dispatch 方法执行时,如果有其他 dispatch 函数正在执行,则会抛出一个异常,因为一个 Reducer 只能输出一个状态树。
Reducers
在 Redux 中,Reducers 是一个纯函数,它负责处理 Action,并返回新的状态树。Reducers 必须是一个纯函数,没有任何副作用。一个纯函数的定义是:同样的输入(Action 和当前状态树),返回的结果必须一致,不会有任何副作用。
Reducers 通常用 switch 语句对 Action 进行处理,例如:
-- -------------------- ---- ------- -------- ----------- - --- ------- - ------ ------------- - ---- ----------- ------ - --------- - --- ---------- ----- ------------ ---------- ------ -- -- ---- -------------- ------ ---------------- -- ------- --- --------- - - -------- ---------- --------------- - - ---- -- -------- ------ ------ - -
我们来看一下 Reducer 的实现。
-- -------------------- ---- ------- -------- ------------------------- - ----- ----------- - ---------------------- ----- ------------- - --- --- ---- - - -- - - ------------------- ---- - ----- --- - --------------- -- ------- ------------- --- ----------- - ------------------ - -------------- - - ----- ---------------- - --------------------------- ------ -------- ----------------- - --- ------- - -- --------------------- --- ------------- - ----- -------------- - -------------------------------------- ------ -------------- ------- ------------------ -- -- ---------------- - ------------------------ - - --- ---------- - ------ ----- --------- - --- --- ---- - - -- - - ------------------------ ---- - ----- --- - -------------------- ----- ------- - ------------------- ----- ------------------- - ----------- ----- --------------- - ---------------------------- -------- -- ------- --------------- --- ------------ - ----- ------------ - ---------------------------------- -------- ----- --- -------------------- - -------------- - ---------------- ---------- - ---------- -- --------------- --- -------------------- - ---------- - ---------- -- ----------------------- --- -------------------------- ------ ---------- - --------- - ------ -- -
可以看到,combineReducers 函数将多个 Reducer 合并为一个大的 Reducer,它以一个代表整个应用程序状态树的对象作为参数。在 combination 函数的内部,通过遍历所有的 Reducer,获取每个 Reducer 的名称和对应的函数。然后循环调用每个 Reducer 来获取新的状态值,并记录下每个 Reducer 是否有改变 state 的值。最后,根据 hasChanged 的值来决定返回新的 state 还是旧的 state。
Actions
在 Redux 中,Actions 代表着用户的行为。它是一个简单的 JavaScript 对象,包含两个属性:type 和 payload(可选项)。type 属性用于描述这个 Action 的类型,payload 是一个可选项,用于传递需要修改的数据。
我们来看一下如何定义一个 Action。
const ADD_TODO = 'ADD_TODO'; { type: ADD_TODO, text: 'Build my first Redux app', }
在实际使用中,我们通常会将 Action 定义为一个函数。
function addTodo(text) { return { type: ADD_TODO, text, }; }
Middleware
在 Redux 中,Middleware 是指一个函数,它接收 store 的 dispatch 和 getState 方法,并返回一个函数,该函数接收下一个 Middleware 的 dispatch 和 getState 方法,并返回一个新的 dispatch 函数。Middleware 可以使用这个返回的 dispatch 函数来修改 Action,来阻止 Action,来执行异步任务等,最后通过调用 next 来将 Action 传递下去。
Middleware 通常在一个编写应用程序时被用到,它能够拦截 action 分发,执行额外的处理,然后将 action 发送到 reducers 进行处理。
我们来看一下如何编写一个 Middleware。
const loggerMiddleware = (store) => (next) => (action) => { console.log('dispatching', action); const result = next(action); console.log('next state', store.getState()); return result; };
在上面的例子中,loggerMiddleware 接收 store,并返回一个函数,该函数接收 next 和 action,最后返回一个新的 dispatch 函数。该函数当 dispatch 应用一个 action 时,在 console 上打印 action 和新的 state。
完整的 Redux 实现
截止到目前为止,我们已经了解了 Redux 的核心部件,下面我们将看一下如何使用它们来构建一个完整的 Redux 应用程序。
-- -------------------- ---- ------- -------- ------------- - -- ------- - ------ ------------- - ---- ------------ ------ ----- - --------------- ---- ------------ ------ ----- - --------------- -------- ------ ------ - - ----- ----- - --------------------- -------- -------- - ----- ----- - ----------------- ---------------------------------------- - ------ - --------- ------------------------ -------------------------------------------------------------- -- -- - ---------------- ----- ------------ -------- - --- --- -------------------------------------------------------------- -- -- - ---------------- ----- ------------ -------- - --- ---
在上面的例子中,我们定义了 reducer 和 store,然后定义了两个监听事件,分别对应增加和减少。在事件的回调函数中,我们通过 store.dispatch() 方法分发了一个 Action,触发了 Reducer 的调用,最后通过 store.subscribe() 注册的 render 方法来更新 UI。
结语
本文介绍了 Redux 的源码分析和实现原理,包括:Store、Reducers、Actions 和 Middleware。通过本文的阅读,相信读者已经了解了 Redux 的实现原理,以及如何使用它构建一个完整的 Redux 应用程序。最后,希望本文对读者有所帮助。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6781fb85935627c900f2577d