Redux 源码深度解析

阅读时长 15 分钟读完

Redux 是一个面向数据流的 JavaScript 库,用于管理 Web 应用程序的状态。通过 Redux,在管理应用程序的状态时,可以实现统一的数据管理方式。本文将对 Redux 库的源码进行深度解析,让读者更好的理解 Redux 的设计和实现原理,以便更好地使用和扩展 Redux。

Redux 源码文件结构

Redux 的源码主要分为五个部分,分别是:

  1. createStore.js:Redux 库的核心,用于创建 store 对象来管理状态;
  2. combineReducers.js:用于将多个 reducer 合并为一个 reducer;
  3. bindActionCreators.js:通过封装 action creator,简化 action creator 的使用;
  4. compose.js:用于将多个函数组合成一个函数;
  5. applyMiddleware.js:用于扩展 Redux 的功能。

这五个文件是 Redux 的核心部分,我们可以通过阅读这些文件来深入理解 Redux 的设计和实现原理。

Redux 的核心概念

在深入阅读 Redux 的源码之前,我们需要了解 Redux 的核心概念。

  1. store:store 是 Redux 应用程序的核心对象,用于管理应用程序的状态,并提供了访问状态和更新状态的接口。
  2. action:action 是一个普通 JavaScript 对象,用于描述状态的变化,其中 type 属性用于表示 action 类型,payload 属性用于描述发生的事件对应的数据。
  3. reducer:reducer 是一个纯函数,用于处理状态的变化,Reducer 的参数是当前状态和一个 action 对象,返回值是新的状态对象。
  4. dispatch:用于向 store 发送 action 的方法,action 会传递给 reducer 进行状态的更新。
  5. subscribe:用于订阅 store 的变化,当 store 中的状态发生变化时,所有的订阅者都将得到通知,并重新渲染应用程序的界面。

createStore.js

createStore.js 是 Redux 库的核心文件,负责创建 store 对象。下面是 createStore.js 文件的代码:

-- -------------------- ---- -------
------ ------- -------- -------------------- --------------- --------- -
  --- ------------ - ---------------
  --- -------------- - --------
  --- ---------------- - ---
  --- ------------- - -----------------
  --- ------------- - ------

  -- ---

  ------ -
    ---------
    ----------
    ---------
    ---------------
  --
-

在 createStore.js 中,我们可以看到 createStore 函数的实现。createStore 的主要作用是创建一个 store 对象来管理状态,它接受的参数包括 reducer、preloadedState 和 enhancer 三个参数。

在 createStore 函数中,首先初始化了 currentState、currentReducer 和 currentListeners 等变量。其中 currentReducer 的值就是传入 createStore 函数的 reducer 参数。

接下来,从 createStore 函数返回的对象中,我们可以看到它包含了 dispatch、subscribe、getState 和 replaceReducer 四个方法。它们分别用于向 store 发送 action、订阅 store 的变化、获取当前的状态对象和替换 store 中的 reducer。

在 createStore 函数中,最重要的部分是 dispatch 函数的实现。dispatch 函数用于向 store 发送 action,它会传递给 store 中的 reducer 进行状态的更新。下面是 dispatch 函数的实现:

-- -------------------- ---- -------
-------- ---------------- -
  -- --- ------- --- -------- --
  -- --------------- -
    ----- --- --------------- --- --- -------- -----------
  -

  --- -
    ------------- - -----
    ------------ - ---------------------------- --------
  - ------- -
    ------------- - ------
  -

  ----- --------- - ----------------- - ---------------
  ---------------------------- -- ------------
  ------ -------
-

在 dispatch 函数中,首先检查当前是否正在执行 reducer,防止出现死循环。之后,使用传入的 action 和 currentState,调用 currentReducer 函数进行状态的更新。

接下来,dispatch 函数会将 currentListeners 赋给 listeners 变量,并调用 listeners 数组中所有的回调函数。这里需要注意的是,由于 listeners 变量和 nextListeners 变量是通过引用关系关联的,因此在 subscribe 函数中添加的回调函数会在下一次执行 dispatch 函数时生效。

最后,dispatch 函数返回 action 对象,方便在应用程序中对此对象进行处理。可以看出,在 Redux 中,每个 action 都对应一个状态的变化过程,包括 action 的创建、发送和处理等过程。

combineReducers.js

combineReducers.js 文件主要用于将多个 reducer 合并成一个 reducer。由于 Redux 应用程序的状态可能非常复杂,因此使用多个 reducer 对状态进行分层管理会更加方便。下面是 combineReducers.js 文件的代码:

-- -------------------- ---- -------
------ ------- -------- ------------------------- -
  ----- ----------- - ----------------------
  ----- ------------- - ---
  --- ---- - - -- - - ------------------- ---- -
    ----- --- - ---------------
    -- ------- ------------- --- ----------- -
      ------------------ - --------------
    -
  -

  -- ---

  ------ -------- ----------------- - --- ------- -
    --- ---------- - ------
    ----- --------- - ---
    --- ---- - - -- - - ------------------------ ---- -
      ----- --- - --------------------
      ----- ------- - -------------------
      ----- --------------- - -----------
      ----- --------------- - ------------------------ --------
      -------------- - ----------------
      ---------- - ---------- -- --------------- --- ----------------
    -
    ------ ---------- - --------- - ------
  --
-

在 combineReducers 函数中,首先从 reducers 对象中提取 reducerKeys,即所有 reducer 对象的 key 值。之后,使用 finalReducers 对象来存储所有有效的 reducer 对象。

接下来,combineReducers 函数会将多个 reducer 合并成一个 reducer,其中 finalReducers 对象包含了所有有效的 reducer 对象。这个合并过程的核心就在这里:

-- -------------------- ---- -------
------ -------- ----------------- - --- ------- -
  --- ---------- - ------
  ----- --------- - ---
  --- ---- - - -- - - ------------------------ ---- -
    ----- --- - --------------------
    ----- ------- - -------------------
    ----- --------------- - -----------
    ----- --------------- - ------------------------ --------
    -------------- - ----------------
    ---------- - ---------- -- --------------- --- ----------------
  -
  ------ ---------- - --------- - ------
--

通过 for 循环遍历所有的 reducer,对原有的 state 进行拆分、更新,并重新组合成一个新的 state 对象,最后返回这个新的 state 对象。在实际应用中,combineReducers 函数通常会与 createStore 函数一起使用,用于将多个 reducer 在全局状态树中进行合并,以便更好地管理全局状态。

bindActionCreators.js

bindActionCreators.js 文件主要用于包装 action creator 函数,使其能够更加方便地在应用程序中的各个组件中使用。下面是 bindActionCreators.js 模块的代码:

-- -------------------- ---- -------
------ ------- -------- ---------------------------------- --------- -
  -- ------- -------------- --- ----------- -
    ------ --------------------------------- ----------
  -

  -- ------- -------------- --- -------- -- -------------- --- ----- -
    ----- --- ------
      ------------------- -------- -- ------ -- - --------- ------- -------- ---------------- --- ---- - ------ - ------ ---------------- -
    --
  -

  ----- ------------------- - ---
  --- ------ --- -- --------------- -
    ----- ------------- - --------------------
    -- ------- ------------- --- ----------- -
      ------------------------ - -------------------------------- ----------
    -
  -
  ------ --------------------
-

在 bindActionCreators.js 文件中,首先判断 actionCreators 是不是一个函数,如果是,就调用 bindActionCreator 函数对它进行包装。如果不是函数,就遍历 actionCreators 对象,并对其中的函数进行包装,最后返回一个包含所有包装后函数的对象。

bindActionCreator 函数的实现也比较简单,它接收一个 actionCreator 函数和 dispatch 函数作为参数,在调用 bindActionCreator 函数时,会返回一个新的函数。这个新的函数会接收它自己的参数,并将参数传递给 actionCreator 函数,最后将 actionCreator 函数返回的 action 对象传递给 dispatch 函数,实现状态的更新。

使用 bindActionCreators 函数,我们可以更方便地将 action creator 函数用于 props 中:

-- -------------------- ---- -------
------ - ------------------ - ---- --------

----- ----------- ------- --------------- -
  -------- -
    ----- ---------- - -----------
    ----- ------- - ---------------------------------- ----------
    ------ -
      -----
        ------- ----------- -- ------------------------- ------------------
      ------
    --
  -
-

在这个例子中,我们通过 bindActionCreators 函数对 ActionCreators 对象中的 doSomething 函数进行了包装,并将包装后的函数传递给组件的 props 中,在组件中使用这个函数,可以方便地发送一个 action,更新应用程序的状态。

compose.js

compose.js 文件主要用于将多个函数组合成一个函数。在 Redux 应用程序中,有时需要执行多个中间件,或者按照一定的顺序执行多个函数。在这种情况下,compose.js 模块提供了一个方便的工具函数,用于将多个函数组合成一个函数。下面是 compose.js 模块的代码:

-- -------------------- ---- -------
------ ------- -------- ----------------- -
  -- ------------- --- -- -
    ------ ----- -- ----
  -
  -- ------------- --- -- -
    ------ ---------
  -
  ------ ---------------- -- -- --------- -- ---------------
-

在 compose.js 文件中,compose 函数接收多个函数作为参数,返回一个函数。这个函数会将多个函数进行嵌套调用,将最终的结果返回。在这个过程中,每个函数的返回值都是下一个函数的参数。

可以看出,compose 函数的实现比较简单,它通过函数的嵌套调用,将多个函数组合成一个函数,方便在 Redux 应用程序中的多个中间件和方法之间进行调用。

applyMiddleware.js

applyMiddleware.js 文件用于扩展 Redux 的功能,它实现了一个 applyMiddleware 函数,用于将多个中间件应用于 Redux 应用程序。下面是 applyMiddleware.js 文件的代码:

-- -------------------- ---- -------
------ ------- -------- ------------------------------- -
  ------ ------------- --
    --------- -- -

      -- ---

      ----- ----- - -------------------- --------------- ----------
      --- -------- - ---------------
      ----- ------------- - -
        --------- ---------------
        --------- --------- -- ------------------
      --
      ----- ----- - ---------------------------- -- ---------------------------
      -------- - ----------------------------------

      ------ -
        ---------
        ---------
      --
    --
-

在 applyMiddleware 函数内部,首先接收多个中间件函数作为参数,返回一个函数,这个函数接收 createStore 函数作为参数,并返回一个新的函数。在新返回的函数被调用时,会调用 createStore 函数,并将 createStore 函数的结果返回。

在 applyMiddleware 函数的实现中,首先执行 createStore 函数,获取一个 store 对象,之后使用 middlewareAPI 对象包装 store 对象,以便在后续的中间件函数中可以直接使用 dispatch 和 getState 方法。

接下来,使用 map 函数遍历所有的中间件函数,并将 middlewareAPI 作为参数,调用它们,获得一个中间件函数数组。通过调用 compose 函数,将中间件函数数组嵌套调用,形成一个新的 dispatch 函数,用于在 Redux 应用程序中发送 action,更新应用程序的状态。

最后,将 dispatch 函数替换 store 中原有的 dispatch 函数,返回包装后的 store 对象。

总结

在本文中,我们对 Redux 的核心源码进行了深度解析,介绍了 Redux 的核心概念,并对 createStore.js、combineReducers.js、bindActionCreators.js、compose.js 和 applyMiddleware.js 等文件进行了详细讲解。了解 Redux 的源码和实现原理,可以帮助我们更深入地理解 Redux 的功能和规范,以便更好地使用 Redux,并在实际应用中解决问题。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6455aa1b968c7c53b0917fbe

纠错
反馈