Redux 源码解析

2021-04-15  本文已影响0人  bowen_wu

概述

Redux 是 JavaScript 状态容器,提供可预测化的状态管理方案。其三大原则为:

纯函数: 即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用

Redux 的核心:

自我实现

代码,这部分主要参照了这个系列的相关文章。相关的逻辑可以参照 commit message

官方文档

  1. 变化 vs 异步
  2. React 把处理 state 中数据的问题留给了开发者,Redux -> 处理 state 中的数据
  3. Redux 试图让 state 的变化变得可预测
  4. reducer 合成 -> 每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。开发一个函数作为主 reducer,它调用多个子 reducer 分别处理 state 中的一部分数据,然后再把这些数据合成一个大的单一对象。
  5. combineReducers API
  6. store 职责:
    • 维持应用的 state
    • 提供 getState() 方法获取 state
    • 提供 dispatch(action) 方法更新 state
    • 通过 subscribe(listener) 注册监视器
    • 通过 subscribe(listener) 返回的函数注销监听器

源码

类型

createStore

  1. 类型:Function

  2. 参数:

    • reducer<Reducer>
    • 【可选】preloadedState<PreloadedState<S>> => 初始化 state
    • 【可选】enhancer<StoreEnhancer<Ext, StateExt>> => 增强器,用来扩展store的功能
  3. 返回值:store<Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext>

源码分析

源码 源码
// TODO: 使用一个 enhancer 作为例子
源码 源码

定义 ensureCanMutateNextListenersgetStatesubscribedispatchreplaceReducerobservable 函数

源码
dispatch
dispatch
getState
getState

getState 方法比较简单,就是 return currentState

subscribe
subscribe
ensureCanMutateNextListeners
ensureCanMutateNextListeners

currentListeners 做了浅拷贝以便于可以在 dispatch 的时候使用 nextListeners 作为临时的 listener。这样可以防止使用者在 dispatch 的时候调用 subscribe/unsubscribe 出现 bug。避免相互影响

combineReducers

  1. 类型:Function
  2. 参数:reducers
  3. 返回值:Function
  4. 使用 combineReducers 例子

源码分析

源码
assertReducerShape
assertReducerShape

遍历给到的 reducer

combination
combination

compose

  1. 类型:Function
  2. 参数:Function[]
  3. 返回值:Function
  4. 使用 compose 例子

源码分析

compose

注意:Array.reduce() 的使用 => 回调函数第一次执行时,accumulatorcurrentValue 取值有两种情况:如果调用 reduce() 时提供了 initialValueaccumulator 取值 initialValuecurrentValue 取数组中的第一个值;如果没有提供 initialValue,那么 accumulator 取数组中第一个值,currentValue 取数组中的第二个值

Lodash compose -> flow

Lodash compose

applyMiddleware

  1. 类型:Function
  2. 参数:Middleware[]
  3. 返回值:Function
  4. Middleware:({getState}) => next => dispatch => ({getState}) => dispatch => action => any

applyMiddleware的功能:改造dispatch函数,产生真假dispatch,而中间件就是运行在假真(dispatchAndLog假和next真)之间的代码。

源码分析

applyMiddleware

applyMiddleware 函数,直接返回一个函数,我们直接看这个函数即可。这个函数的结构是 (createStore) => (reducer, preloadedState?) => store

之后看一下 Middleware 使用场所。是作为 applyMiddleware 的参数使用。applyMiddleware 做为 createStore 的第三个参数传入 createStore

createStore enhancer

此时的 enhancer === applyMiddleware,之后在 createStore 里面直接执行
applyMiddleware,将 createStorereducer + preloadedState 传入 applyMiddleware,即在 applyMiddleware 代码中的第 21 行 - 第 24 行的返回值即为 createStore 的返回值

applyMiddleware 做了什么

  1. 执行传入的 Middleware,并将 state 作为参数传入 Middleware
  2. 更新 dispatch,这个更新后的 dispatch 是用户传入的(强化的 dispatch),例如上述的 logger 例子
    action => {
        console.log('will dispatch', action)
    
        // 此处的 next === dispatch === store.dispatch === createStore(reducer, preloadedState).dispatch
        const returnValue = next(action)
    
        console.log('state after dispatch', getState())
    
        return returnValue
    }
    

注意点

  1. enhancer store 的增强器。enhancer 是一个高阶函数,返回值是一个经过包装的强化的 storeapplyMiddleware 就是一个 enhancer

疑问

Redux 做了什么?

  1. 存储 state
  2. 更删改查 state => 可以理解为 state 的变化
  3. 触发更新

middleware 的执行顺序

Redux 的中间件模型类似于 koa。在 next 前面以及 next,由外向内依次执行。当最里面的 next 执行完成之后,next 后面的代码会由内向外执行。非常类似于 Koa 的洋葱中间件模型。

dispatch 之后,Redux 是如何去处理的?

  1. 执行 reducer 更新 currentState
  2. 依次执行 listener 函数

state 中的数据被修改之后,订阅者们如何去收到更新后的数据?

subscribe 中通过 store.getState() 获取数据

applyMiddlewaredispatch 为何赋值两次

第一次赋值表示 => 在 Middleware 创建时,不能进行 dispatch
第二次赋值表示 => 此 dispatch 是一个强化后的 dispatch

middlewareAPI 中的 dispatch 什么时候会被调用

注意使用场景

function logger({ getState, dispatch }) {
  // 此时的 dispatch 为抛错函数,即第一次赋值的函数,即第 7 - 12 行
  return next => {
    // 此处的 next === store.dispatch
    // 此时的 dispatch === 下面 return 的函数
    return action => {
        console.log('will dispatch', action)

        const returnValue = next(action)

        console.log('state after dispatch', getState())

        return returnValue
      }
  }
}

middlewareAPI 中的 dispatch 为啥要用匿名函数包裹

let dispatch = () => console.log('Error!');
const obj = {
  name: 'dispatch',
  dispatch,
}
obj.dispatch();

dispatch = () => console.log('Ready!');
obj.dispatch();

上述的 log 是什么结果呢?


result
let dispatch = () => console.log('Error!');
const obj = {
  name: 'dispatch',
  dispatch: (...args) => dispatch(...args),
}
obj.dispatch();

dispatch = () => console.log('Ready!');
obj.dispatch();

上述的 log 是什么结果呢?


result

store.subscribe 为啥要有🔐 isDispatching

dispatch 执行时候会循环执行更新函数,要保证 listeners 数组在这时候不能被改变

以下代码作用

createStore

答:有了这一层判断,我们就可以这样传:createStore(reducer, initialState, enhancer) 或者这样:createStore(reducer, enhancer),其中 enhancer 还会是 enhancer

Redux 优势

  1. 纯函数,做测试的时候 easy
上一篇 下一篇

猜你喜欢

热点阅读