Redux周边库源码解读之Redux

529 阅读4分钟

前言

​ Redux 可以说是前端数据流控制的一大 Boss,根据 Redux 衍生的类库数不胜数,这就说明仅仅掌握 Redux 的使用是不够的,需要深入源码,了解细节为佳,本文将尝试阅读 Redux 的源码,源码本身也不长,更多的是体会这种流管理的思想.

​ Redux 源码是用 Ts 写的,不熟悉 Ts 的朋友可以先去TypeScript 官网熟悉下


使用

​ 老规矩,看源码之前先熟悉下 Redux 是怎么用的,这当然要去Redux 官网或者Github 仓库去看文档了.(为了避免中文文档没有及时更新和翻译带来的难理解,建议去外文官网)

​ 首先映入眼帘的都是同一句话

Redux is a predictable state container for JavaScript apps.

​ predictable,可预测的,是redux很关键的概念

数据流

总的来说,Redux 数据流很简洁,一个state一个action一个reducers,使用上就是编写上述三者,再通过一些内置 API 联系在一起,生成一个store,通过官网 🧅 说明

1. action

action 就是一个对象,描述一个可以改变state的事件,那怎么描述呢?约定是在该对象上定义一个type属性

const THREEPM = {
  type: "DrinkTea",
  payload: "xxxxx",
};

就这么简单,至于写成函数返回对象还是异步 action,我不管,whatever

2. state

state 也只是一个对象,保存程序状态,无任何约束

const state = {
    todo:[];
}

就这么简单,至于是写在 reducers 的参数列表了还是配套 immuableJS,我不管,whatever

3. reducers

这名字就起的诡异,还不如叫 actionToState,这家伙的主要作用就是描述 action 触发之后要干嘛,也就是根据 action 更新 state,约束就是每次必须返回新的state,不能修改原来的state

即 (state, action) => newState;

function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case "counter/incremented":
      return { value: state.value + 1 };
    case "counter/decremented":
      return { value: state.value - 1 };
    default:
      return state;
  }
}

关联

就这么简单,大道至简,那怎么关联他们呢?更简单,将 reducers 传入 createStore,生成 store 管理他们三个就行了

import { createStore } from "redux";
const store = createStore(reducers);

生成的 store 就是老大,负责三个小弟的通信和管理

根据开头的那张图,具体流程必须是以下这样

store.dispatch(action) -> reducers handle -> update state

至于怎么监听更的 state,怎么进行异步操作,怎么分散 state 到几个 reducers,怎么合并几个 reducers 的 state 成总体 state,这里不再赘述.


createStore

使用部分为我们打开了一个切入点,因为只用到了一个 API,createStore,所谓的黑魔法就发生在这里.

源码 index.ts

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes,
};

我们先看下,整个库导出的接口型 API,什么叫接口型 API 呢,就是给开发人员用的,一共就 6 个,可以看到,混进了个奇怪的东西 🧞‍♀️,这个__DO__NOT__USE__ActionTypes是个什么东西呢?定义如下

源码 utils/actionTypes

const randomString = () =>
  Math.random().toString(36).substring(7).split("").join(".");

const ActionTypes = {
  INIT: `@@redux/INIT${/* #__PURE__ */ randomString()}`,
  REPLACE: `@@redux/REPLACE${/* #__PURE__ */ randomString()}`,
  PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`,
};

export default ActionTypes;

是几个奇奇怪怪的 action type,这些action type主要用于内部事件标识,让我们别用,那不用就是了.

说回createStore

export default function createStore<
  S,
  A extends Action,
  Ext = {},
  StateExt = never
>(
  reducer: Reducer<S, A>,
  enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
export default function createStore<
  S,
  A extends Action,
  Ext = {},
  StateExt = never
>(
  reducer: Reducer<S, A>,
  preloadedState?: PreloadedState<S>,
  enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;
export default function createStore<
  S,
  A extends Action,
  Ext = {},
  StateExt = never
>(
  reducer: Reducer<S, A>,
  preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
  enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;

createStore有三个重载,返回值都是泛型 Store 和 Ext 的交叉类型,但三个重载的参数并不相同,其中reducer都是必须的,可选的有enhancerpreloadedState

enhancer可选增强器,属于第三方插件性接口,比如说中间件增强等等

preloadedState可选初始化的state

createStore 内部流程

进入到函数内部,首先 javascript 特色,参数判断,根据不同的参数情况会执行不同的步骤

【有增强器】:

return enhancer(createStore)(
  reducer,
  preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;

【无增强器】:

//1. 内部属性初始化
let currentReducer = reducer
let currentState = preloadedState as S
let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
//2. 内部方法定义
//略
//3. dispatch 一个 action ,就是上面不让我们用的那个action type
dispatch({ type: ActionTypes.INIT } as A)
//4. 生成store
 const store = {
    dispatch: dispatch as Dispatch<A>,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  } as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
  return store
}

dispatch、subscribe

完完全全的观察者模式,subscribe 进行监听,dispatch 去 notify listener,只是在 dispatch 去 notify 之前,把 currentState 传进 reducer 获得最新的 state 而已

dispatch 节选

try {
  isDispatching = true;
  currentState = currentReducer(currentState, action);
} finally {
  isDispatching = false;
}

const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
  const listener = listeners[i];
  listener();
}

subscribe 节选

nextListeners.push(listener);

真的没有黑魔法 🙉

当然,redux 的核心就是一开始提到的可预测,因为无论是 state 还是 reducer,都存在两个版本,一个 pre,一个 cur,并且每次的 reducer 改变 state,都是立刻改变,并通知更新,并不会累积一下再去统一做改变,这样性能上可能不是最佳的,但是一切都是实打实的预料之中的,设计思想很坚定.


增强器与中间件

增强器

​ 很多朋友知道 redux 有个叫中间件的东西,却不知道其实中间件只是增强器的一种,所谓增强器,就是可以增强 store 功能的高阶函数,在前面 createStore 的参数里作为可选项,下面来看下他的定义

export type StoreEnhancer<Ext = {}, StateExt = never> = (
  next: StoreEnhancerStoreCreator<Ext, StateExt>
) => StoreEnhancerStoreCreator<Ext, StateExt>;

可以看到,增强器是接收storeEnhancerStoreCreator类型返回值也是storeEnhancerStoreCreator类型,storeEnhancerStoreCreator类型定义如下

export type StoreEnhancerStoreCreator<Ext = {}, StateExt = never> = <
  S = any,
  A extends Action = AnyAction
>(
  reducer: Reducer<S, A>,
  preloadedState?: PreloadedState<S>
) => Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;

storeEnhancerStoreCreator则是接收reducerpreloadedState作为参数返回store,因为如果调用createStore时有传入enhancer,执行enhancer并将createStore作为参数传入.

return enhancer(createStore)(
  reducer,
  preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;

中间件增强器

光说不练假把式,Redux 就内置了一个我们熟悉的增强器applyMiddleware,先看其使用

import { applyMiddleware } from "redux";
const middleware = [thunk];
const store = createStore(reducer, applyMiddleware(...middleware));

applyMiddleware有很多个类型重载,都是接收若干个中间件作为参数,返回一个storeEnhancer,这里只截取其中一个重载

export default function applyMiddleware(
  ...middlewares: Middleware[]
): StoreEnhancer<any>;

因为他是先执行再作为增强器传入createStore,所以返回值自然是个增强器咯

核心代码如下:

const store = createStore(reducer, preloadedState);
let dispatch: Dispatch = () => {
  throw new Error(
    "Dispatching while constructing your middleware is not allowed. " +
      "Other middleware would not be applied to this dispatch."
  );
};
const middlewareAPI: MiddlewareAPI = {
  getState: store.getState,
  dispatch: (action, ...args) => dispatch(action, ...args),
};
const chain = middlewares.map((middleware) => middleware(middlewareAPI));
dispatch = compose<typeof dispatch>(...chain)(store.dispatch);

return {
  ...store,
  dispatch,
};
  1. 创建出 store
  2. 把暴露给中间件的 API 写在 middlewareAPI 对象里,传给每个中间件
  3. 调用compose组合全部中间件

compose的作用是将compose(f, g, h)转换成(...args) => f(g(h(...args))),更多信息在官网中间件章节

尾声

​ 以上就是 Redux 核心代码的解读,本人能力有限,难免出错,望请包涵!

​ 总的来说,Redux 内部实现足够简洁且拓展性很好,这也可能是为什么 base 在其上有这么多库的原因,能读懂才有 PR,接下来本人还会分析几个 Redux 相关库的源码,比如 redux-thunk,redux-saga 等

​ 感谢阅读 🌈