redux—中间件原理

中间件之前我在写koa还有express的时候用过不少,主要是将逻辑分开,然后去一层一层的去判断执行是否符合要求比如权限之类的,就相当于一个洋葱一样,一层一层的往里深入。 除了koa还有express之外,redux的中间件也感觉比较牛,redux可以说是百行代码千行文档。在redux中我们常用的中间件有日志记录(logger),还有处理异步请求(thunk) 这次博客主要用来记录一下我在看redux中间件源码过程中的体会,还有他中间件源码的执行流程,理解大佬们的思维

起始

我们在创建store会调用createStore方法,并且我们还会传入中间件如下

typescript 复制代码
const store = createStore(reducer,initState,applyMiddleware(thunk))

这个时候我们可以看到,他主要是两个方法,一个是createStore,一个是applyMiddleware,我们就从createStore入手

typescript 复制代码
function createStore<
  S,
  A extends Action,
  Ext extends {} = {},
  StateExt extends {} = {},
  PreloadedState = S
>(
  reducer: Reducer<S, A, PreloadedState>,
  preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
  enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext {
  // 省略边界判断代码(主要是用于判断参数的类型)
    return enhancer(createStore)(
      reducer,
      preloadedState as PreloadedState | undefined
    )
  }

我们可以看到它首先是将applyMiddleware函数的执行结果(enhancer)执行并传入createStore(用于初始化和获取state),并把返回enhancer的返回结果的执行结果,执行时传入reducer和preloaderState(我们传入的initState)

applyMiddleware

看完createStore,我们并不能得到中间件的原理,所以我们看一下applyMiddleware

typescript 复制代码
function applyMiddleware(
  ...middlewares: Middleware[]
): StoreEnhancer<any> {
  return createStore => (reducer, preloadedState) => {
    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
    }
  }
}

首先该方法,直接返回一个函数,该函数接受createStore,然后又直接返回一个函数,接受reducer和preloadedState,这个函数中初始化store,并且定义了dispatch,然后把所有的中间件都执行一遍,传入middlewareAPI,得到一个chain,然后dispatch等于compose的返回值

compose

我们看完applyMiddleware并不能得到中间件的具体处理,不过我们可以看到,中间件的主要原理就是劫持dispacth,这样我们修改数据提交dispatch时,它背后并没有直接就触发修改数据的dispatch,而是触发compose处理过的dispatch,那么我们就来看一下compost代码

typescript 复制代码
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))
  )
}

可以看出来,就是返回一个函数,如果是多个中间件这个函数是层层嵌套的函数,然后接受一些参数(这里的参数主要是真实的dispatch)这里需要注意的一点是,只有最后一个中间件才能拿到真实的dispatch,而其他中间件拿到的是下一个中间件,就这样一层层传递,最后到达真实的dispatch 为了方便理解,我这里模仿了一下redux的执行流程

typescript 复制代码
function a(next) {
  return (action) => {
    console.log('a-----------');
    console.log(next, 'next');
    console.log(action, 'action');
    console.log('-----------a');
    next(action)
  }
}
function b(next) {
  return (action) => {
    console.log('b-----------');
    console.log(next, 'next');
    console.log(action, 'action');
    console.log('-----------b');
    next(action)
  }
}
function c(next) {
  return (action) => {
    console.log('c-----------');
    console.log(next, 'next');
    console.log(action, 'action');
    console.log('-----------c');
    next(action)
  }
}
const chain = [a, b, c]
const compose = (...funs) => {
  return funs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  )
}
const dispatch = () => {
  console.log('我是原始的dispatch');
}
const m = compose(...chain)(dispatch)
m({ state: '触发' })

上边的m就是我们平常触发的action,然后它会一层一层的传递,从a执行到b然后再从b执行到c,最后c触发真实的action,这里的a,b,c就相当于中间件(不过仍需要注意一点就是,真实的中间件是三层函数,因为我们这里并没有const chain = middlewares.map(middleware => middleware(middlewareAPI))这个操作,所以就直接返回了)传递的参数next是中间件返回的函数 由此我们就知道了中间件的原理,不得不说,大佬写的代码还是挺牛的

redux-thunk中间件原理

这个中间件主要是为了异步操作的,虽然我们可以再触发action时异步触发,但是这个逻辑并不能复用,由此就有了该中间件,其实我感觉这个中间件并非是支持异步,而是支持action为函数,我们平常写的action都是一个对象的形式,有了该中间件,我们可以让action是一个对象 看了上边的原理之后,我们可以发现中间件写的思路其实挺固定的,就是写一个函数,里边返回了三层函数,去调传递的next参数,然后一步一步的像一个洋葱一样最终触发到真正的修改数据的action 其实thunk的源代码非常短我们,主要还是感受大佬的思维

typescript 复制代码
const thunk=()=>{
  const middleware: ThunkMiddleware<State, BasicAction, ExtraThunkArg> =
    ({ dispatch, getState }) =>
    next =>
    action => {
      if (typeof action === 'function') {
        // 执行后,在action函数中触发dispatch
        return action(dispatch, getState, extraArgument)
      }
      // 走其他中间件
      return next(action)
    }
  return middleware
}

简单来说就是判断action传递的是否是函数,然后去走不同的逻辑

相关推荐
一棵白菜几秒前
Iterm2 终端安装(带语法提示)
前端
不想说话的麋鹿几秒前
《NestJS 实战:RBAC 系统管理模块开发 (三)》:角色权限分配与数据一致性
前端·后端·node.js
满分观察网友z几秒前
Vue Router 导航解密:replace 还是 push?
前端
三年三月几秒前
019-各种灯光和常用helper(一)
前端·three.js
恋猫de小郭19 分钟前
Flutter 小技巧之:实现 iOS 26 的 “液态玻璃”
android·前端·flutter
糖墨夕23 分钟前
Trae还能将Figma 设计稿转化为前端代码
前端·trae
程序猿小D24 分钟前
第26节 Node.js 事件
服务器·前端·javascript·node.js·编辑器·ecmascript·vim
天天打码25 分钟前
Bootstrap Table开源的企业级数据表格集成
前端·开源·bootstrap
Allen Bright27 分钟前
【CSS-8】深入理解CSS选择器权重:掌握样式优先级的关键
前端·css
hnlucky29 分钟前
安装vue的教程——Windows Node.js Vue项目搭建
前端·javascript·vue.js·windows·node.js