redux源码阅读

本文详细解读了Redux源码,包括createStore、applyMiddleware、compose等核心概念,探讨了reducer、中间件、状态管理等方面,旨在帮助读者深入理解Redux的工作原理和设计思想。同时,文章提及了react-redux的结合使用,强调了Redux在前端数据管理中的作用。
摘要由CSDN通过智能技术生成

最近在写【重拾前端】系列,下面有几个快速通道,大家自取

【重识前端】原型/原型链和继承

【重识前端】闭包与模块

【重识前端】全面攻破this

【重识前端】一次搞定JavaScript的执行机制

【重识前端】什么是BFC、IFC、GFC 和 FFC

【重识前端】深入内存世界

【重识前端】暴走的异步编程

【重识前端】redux源码阅读

前言

之前一直在使用redux,后来出了hooks,用的比较少了,但是还是会使用,他们不冲突,各自对应的场景不同。听说redux源码比较少,也比较好懂(不是贬低,越强的代码其实越简洁明了)

所以准备阅读一下,也算是重识前端系列的一员吧。

注意一下,其实我们写react的时候用到的其实是redux-react。后面也会一起介绍。

更好的阅读体验其实是在项目里面打断点,这里是我的GitHub地址,喜欢的同学可以fork或者star一下哦。里面我把源码都拉出来放到文件夹里面然后由create-react-app来调用,而不是直接用npm装。这有助于理解与调试,里面也有我的注释,其实可以直接阅读源码的注释会更快。

redux和rdux-react的代码也是目前最新(2020-09-13)的master分支上面的。

开始

redux是用的rollup打包的。

打开根目录下面的rollup.config.js可以看到入口是src目录下的index文件。

可以看到他导出的一些东西

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

我们一个个开始。首先映入眼帘的是createStore

createStore

开始之前,我们先看看是怎么用。以下采用我的源码阅读里面的代码:

// middleware
const logger = (store:any) => (next:any) => (action:any) => {
   
  // debugger
  console.info('dispatching', action)
  let result = next(action);
  console.log('next state', store.getState())
  return result
}

let store = createStore(rootReducer, applyMiddleware(logger));

再看看源码的createStore。

参数

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 {
   
  //.. 一些校验的源码就不介绍了,想了解的可以看我的GitHub,上面有注释
  let currentReducer = reducer // 临时存放 reducer 的地方
  let currentState = preloadedState as S // 临时存放 state 的地方
  let currentListeners: (() => void)[] | null = [] // 监听队列
  let nextListeners = currentListeners // 引用赋值, 和正式的队列进行区分, 别有他用
  let isDispatching = false // 是不是正在dispatch
  // ... 
}

createStore接收3个参数reducerpreloadedStateenhancer

我们一一剖析他们分别是做什么的。

reducer

这个大家太熟悉了。就是一个函数,然后我们在里面写switchswitch dispatch过来 actiontype。然后做对应的操作。最后返回一个新的对象,也就是全新的store的数据。为什么是返回一个全新的store,是为了防止js的引用。

忘记的,或者不太懂的同学可以看一下下面的demo:

const initState = {
   
  todos: []
}

// 这里面我们一般会赋值上默认的state,因为后续可能会用到默认的情况,像数组之类的就非常常见。
const todos = (state = initState, action) => {
   
  switch (action.type) {
   
    case 'ADD_TODO':
      return {
   
        ...state
      }
    case 'TOGGLE_TODO':
      return state.map(
        todo =>
          todo.id === action.id ? {
    ...todo, completed: !todo.completed } : todo
      )
    default: {
   
      return state
    }
      
  }
}

export default todos
preloadedState

这个其实我们用的不是很多,因为我们一般预存的都直接卸载reducer里面了。像👆🌰里面的state = initState就省去了我们的传递这个参数的过程。而且数据放在reducer里面也更加清晰。

哪怕是在阮一峰老师的日志里面也是这样教的。

const defaultState = 0;
const reducer = (state = defaultState, action) => {
   
  switch (action.type) {
   
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
};

const state = reducer(1, {
   
  type: 'ADD',
  payload: 2
});

中文翻译就是:预存的state。也可以理解为初始的,默认的。因为我们一般在也页面里面直接拿store里面的值,或者map或者什么。很有可能就会报错,因为如果没有默认值我们直接用map就会报错…相信刚刚开始用的小伙伴应该有这样的困惑。

但是,我们通常都是传两个参数,那咋办?我看他好像是顺序读取的诶…

redux兼容我们啦,我们可以看到前面有一段代码是为了做这个的

// preloadedState为function enhancer为undefined的时候说明initState没有初始化, 但是有middleware
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
   
  enhancer = preloadedState // 把 preloadedState 赋值给 enhancer
  preloadedState = undefined // preloadedState赋值undeifined
}
enhancer

中文翻译是:增强。其实就是我们常用的中间件,用于增强我们的redux。常见有的有logredux-thunkredux-saga等等。

我们发现代码里面有一段是这样的:

// debugger
// 如果参数enhancer存在
  if (typeof enhancer !== 'undefined') {
   
    // 如果enhancer存在,那他必须是个function, 否则throw Error哈
    if (typeof enhancer !== 'function') {
   
      throw new Error('Expected the enhancer to be a function.')
    }
    /**
     * 传入符合参数类型的参数,就可以执行 enhancer,
     * 但是这个return深深的吸引了我, 因为说明有applyMiddleware的时候后面的都不用看了 ??? 当然不可能
     * 可是applyMiddleware其实是必用项,所以猜想一下applyMiddleware强化store之后会enhancer赋值undefined,再次调用createStore
     * 上下打个debugger看一下执行顺序(debugger位置以注释),果然不出所料
     * 好了, 假设我们还不知道applyMiddleware()这个funcrion具体干了什么,
     * 只知道他做了一些处理然后重新调用了createStore并且enhancer参数为undefined
     * 先记下,后续在看applyMiddleware, 因为我们现在要看的是createStore
     * * */
    // debugger
    return enhancer(createStore)(reducer, preloadedState)
  }

这个重点其实在于applyMiddleware,后续我们会介绍到的。

内置变量

这个比较简单,我就都写在注释里面了

let currentReducer = reducer // 临时存放 reducer 的地方
let currentState = preloadedState as S // 临时存放 state 的地方
let currentListeners: (() => void)[] | null = [] // 监听队列
let nextListeners = currentListeners // 引用赋值, 和正式的队列进行区分, 别有他用
let isDispatching = false // 是不是正在dispatch

内置方法

ensureCanMutateNextListeners

这里其实是为了弄两个数组,一个操作一个原数组而抽象的一个方法。

有一个小的知识点,===对于数组的判断只能用于判断是否是同一个内存地址,不信的话大家可以试试。

const a = [1, 2];
const b = [1, 2];
const c = a;
console.log(a === b);
console.log(a === c);
console.log(b === c);

所以下面的操作的就变得很好理解了。在我们操作listener之前会先存储一下快照。大家要多理解一下,因为后面很多地方会用到这个函数。

// Google翻译: 确保可以使下一个侦听器突变
  // 我的理解是存储一下快照, 以为接下来可能会进行操作.
  function ensureCanMutateNextListeners() {
   
    // 是否相同, 是不是简单的引用赋值, 是的话就浅拷贝一份
    if (nextListeners === currentListeners) {
   
      nextListeners = currentListeners.slice()
    }
  }
getState

这个函数很简单,首先判断一下是否是在dispatch,然后返回当前的state。

// 获取当前的state
  function getState(): S {
   
    // 如果正在dispatch 就报错,因为要获取最新的state, dispatch很有可能会改变state
    if (isDispatching) {
   
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState as S
  }

有没有感觉redux好像也不过如此~~接着看哦。

subscribe

中文翻译:订阅者。

其实也很好理解。可能有用过redux的同学都有用过这个。

我举一个怎么用的🌰:

const unSubscribe = store.subscribe(() => {
   
  console.log('store.subscribe', store.getState())
})
// 取消订阅就直接调用这个函数就好了
// unSubscribe();

一旦触发了dispatch就会触发这个函数,也就是我们写上去的console.log()

然后我们再来看这个函数。

function subscribe(listener: () => void) {
   
    // listener必须为函数,因为要以回调函数的方式来触发。
    if (typeof listener !== 'function') {
   
      throw new Error('Expected the listener to be a function.'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值