redux 源码分析

122 阅读7分钟

Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。

单向数据流

image.png

 "单向数据流(one-way data flow)":

  • 用 state 来描述应用程序在特定时间点的状况
  • 基于 state 来渲染出 View
  • 当发生某些事情时(例如用户单击按钮),state 会根据发生的事情进行更新,生成新的 state
  • 基于新的 state 重新渲染 View

 

三大原则

单一数据源

整个应用的 全局 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

使用纯函数来执行修改

为了描述 action 如何改变 state tree,你需要编写纯的 reducers。Reducer 就是纯函数,它接受当前的 state 和 action。然后返回一个新的 state。所以这里,state不会更新,只会替换。

目录结构

image.png

 

原生中的调试

  • 先将github.com/reduxjs/red…源码下载下来,将依赖安装完毕,然后打包一下
  • 然后在项目中新建一个文件夹,将打包过后的文件放进去即如图所示。接下来就可以调试啦 。

image.png  

react 框架中 直接在create-react-app中安装redux 即可使用

api

React.createStore

创建一个包含程序完整 state 树的 Redux store 。 应用中应有且仅有一个 store。

参数

  1. reducer  (Function) : 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树
  2. [preloadedState(any) : 初始时的 state。你可以决定是否把服务端传来的 state 水合(hydrate)后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 combineReducers 创建 reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。
  3. enhancer  (Function) : Store enhancer。你可以选择指定它以使用第三方功能,如middleware、时间旅行、持久化来增强 store。Redux 中唯一内置的 store enhander 是 applyMiddleware()

返回值

(Store): 保存了应用程序所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe state 的变化,然后更新 UI

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 {
  let currentReducer = reducer // 当前的reducer函数
  let currentState = preloadedState as S  // 当前的 state
  let currentListeners: (() => void)[] | null = []  // 当前的监控数组函数
  let nextListeners = currentListeners // 监听数组函数副本
  let isDispatching = false // 是否在dispatch中
  
  function ensureCanMutateNextListeners() {
  }
  function getState() { return currentState }
  function subscribe(listener) {} 
  function dispatch(action) {}
  function replaceReducer(nextReducer) {}
  function observable() {} 
  // ActionTypes.INIT @@redux/INITu.v.d.u.6.r
   dispatch({ type: ActionTypes.INIT } as A)
   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
   
}

 

image.png

这个方法是Redux保留用的,用来初始化state,其实就是dispatch 走到我们默认的分支里面获取到默认的 state值

dispatch

这是触发状态更改的唯一方法。

function dispatch(action: A) {
    if (!isPlainObject(action)) {}


    if (isDispatching) {}


    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()
    }
   // 最终返回 action
    return action
  }

在执行完后reducer会将监听数据中的函数都执行一遍。这样页面才会有所更新

 

包含中间工作步骤工作图

image.png  

中间件applyMiddleware(...middlewares)

使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。

参数

  • ...middleware (arguments): 遵循 Redux middleware API 的函数。每个 middleware 接受 Store 的 dispatch 和 getState 函数作为命名参数,并返回一个函数。该函数会被传入被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是 ({ getState, dispatch }) => next => action

返回值

(Function) 一个应用了 middleware 后的 store enhancer。这个 store enhancer 的签名是 createStore => createStore,但是最简单的使用方法就是直接作为最后一个 enhancer 参数传递给 createStore() 函数。

 

export default function applyMiddleware(
  ...middlewares: Middleware[]
): StoreEnhancer<any> {
  return (createStore: StoreEnhancerStoreCreator) =>
    <S, A extends AnyAction>(
      reducer: Reducer<S, A>,
      preloadedState?: PreloadedState<S>
    ) => {
      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
      }
    }
}

 

中间件

function logger({ getState }) {
  return next => action => {
    console.log('will dispatch', action)
    // Call the next dispatch method in the middleware chain.
    // 调用 middleware 链中下一个 middleware 的 dispatch。
    const returnValue = next(action)
    console.log('state after dispatch', getState())
    // This will likely be the action itself, unless
    // a middleware further in chain changed it.
    return returnValue
  }
}

 

function thunk({ dispatch, getState }) {
 return (next) => (action) => {
        if (typeof action === 'function') {
            return action(dispatch, getState);
        }
        return next(action);
    };
}

 

 

compose(...functions)

从右到左来组合多个函数。这是函数式编程中的方法,为了方便,被放到了 Redux 里。当需要把多个 store enhancers 依次执行的时候,需要用到它。

参数

  1. (arguments): 需要合成的多个函数。预计每个函数都接收一个参数。它的返回值将作为一个参数提供给它左边的函数,以此类推。例外是最右边的参数可以接受多个参数,因为它将为由此产生的函数提供签名。(compose(funcA, funcB, funcC) 形象为 compose(funcA(funcB(funcC())))

返回值

(Function): 从右到左把接收到的函数合成后的最终函数。

 

export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg
  }


  if (funcs.length === 1) {
    return funcs[0]
  }


  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  )
}

 

先看一个简单的例子

function f1(args){
    console.log('f1',args)
    return args;
}
function f2(args){
    console.log('f2',args)
    return args;
}
function f3(args){
    console.log('f3',args)
    return args;
}


// 让这三个函数依次执行
// step 1
// f1('omg')
// f2('omg')
// f3('omg')


// // step 2
// f1(f2(f3('omg')))
// step 3
const res = compose(f1,f2,f3)('omg')
console.log('res',res)


function compose(...fun){
    if(fun.length ===0){
        return arg=>arg
    }
    if(fun.length === 1){
        return fun[0]
    }
    return fun.reduce((a,b)=>{
        return (...args)=>{
        return a(b(...args))
    }})
}

 

 

这个时候再看一下 创建store的方法传参

const store = Redux.createStore(counter, Redux.applyMiddleware(logger1, logger2,logger3))

 

image.png  

这个时候 再看一下 中间件的触发过程

image.png

combineReducers(reducers)

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。

参数

  1. reducers (Object): 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数需要满足的规则。

返回值

(Function):一个调用 reducers 对象里所有 reducer 的 reducer,并且构造一个与 reducers 对象结构相同的 state 对象。

export default function combineReducers(reducers: ReducersMapObject) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers: ReducersMapObject = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]


    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)


  // This is used to make sure we don't warn about the same
  // keys multiple times.
  let unexpectedKeyCache: { [key: string]: true }
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }


  let shapeAssertionError: unknown
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }


  return function combination(
    state: StateFromReducersMapObject<typeof reducers> = {},
    action: AnyAction
  ) {
  


    let hasChanged = false
    const nextState: StateFromReducersMapObject<typeof reducers> = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        const actionType = action && action.type
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}

3. 资料