深度剖析github star数15.1k的开源项目redux-thunk

徐小夕 等级 459 0 0

日益忙碌的一周又过去了,是时候开始每周一次的总结复盘了,今天笔者就来剖析一下githubstar数15.1k的开源项目redux-thunk

作为一名React方向的前端工程师,不管是被面试还是面试别人,大部分都会说起redux-thunk的实现原理,因为它非常经典且有用,而且代码量少的感人,只有短短12行代码,却能解决React开发中同一个函数支持多dispatch异步action的问题(虽然这完全依赖于redux的中间件机制(Middleware))。

接下来笔者将从:

  • Redux的工作机制
  • 中间件实现原理
  • redux-thunk源码实现

这三个方面来带大家彻底掌握redux-thunk源码,从而对redux有更深入的了解和应用。如果大家对react-redux-redux-thunk实战感兴趣的,读完之后可以移步笔者的《彻底掌握redux》之开发一个任务管理平台

正文

在解读Redux-thunk源码之前我们需要先掌握redux的基本工作机制和中间件实现原理,这样才能更好的理解源码背后的奥义。长话短说我们先来看看redux的几个核心api及其作用: 深度剖析github star数15.1k的开源项目redux-thunk redux解决的真正问题是React组件间的状态共享状态管理问题,通过以上的6个核心api我们便能管理复杂的状态,并能监听和追溯状态的改动。机制笔者总结如下: 深度剖析github star数15.1k的开源项目redux-thunk redux工作机理基本了解之后,我们先看看一个实际的例子:

import actionType from './actionType'

class Actions {
    static start() {
        return {
            type: actionType.CREATE_TODO_DOING
        }
    }

    static ok(data, cb) {
        cb && 'function' === typeof cb && cb(data);
        return {
            type: actionType.CREATE_TODO_SUCCESS,
            payload: data
        }
    }

    static fail(data, cb) {
        cb && 'function' === typeof cb && cb(data);
        return {
            type: actionType.CREATE_TODO_FAILURE,
            payload: data
        }
    }
}

以上代码我们可以发现我们用了一个统一的createAction来创建action,在调用时只需要执行Actions.start()即可,我们也知道action返回的是一个标准的对象,但我们可以在return之前做一些side effect。这里我们并不能在action中处理异步逻辑,这也是redux-thunk的价值之一,即解决异步调用action。

到这一步我们仍然不能直接进入redux-thunk的源码分析,因为我们还是不清楚如何解决上述步骤,因为我们还没有了解redux的中间件机制。

redux中间件机制

说到中间件(middleware),使用过nodejs的人可能会很熟悉,比如说知名的koa中间件,express中间件等,其实中间件笔者的理解是在某个执行流中的某个环节做一些额外的处理的模块。实现中间件的机制也很简单, 就是在框架核心执行流中去遍历外部传入的中间件,并依次执行即可,我们先来看看redux中如何使用中间件的:

import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';

const middlewares = applyMiddleware(middleware1, middleware2);
const store = createStore(reducers, middlewares);

所以说redux-thunk是被传入applyMiddleware方法中作为参数使用的,不难猜到applyMiddleware方法中一定有遍历执行参数的逻辑,我们来看看applyMiddleware的核心源码:

export default function applyMiddleware(...middlewares) {
    return function enhancer(createStore) {
        return function enhancedCreateStore(...args) {
        const store = createStore(...args)
        let dispatch = () => {
            thrownewError('此处省略n个字...')
        }

        const middlewareAPI = {
            getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }

        const chain = middlewares.map(function(middleware) {
            return middleware(middlewareAPI)
        })

        dispatch = compose(...chain)(store.dispatch)
        return {
            ...store,
          dispatch,
        }
     }
  }
}

由上面的源码可知,在chain这段代码里我们发现其存储的是applyMiddleware方法参数传入getState,dispatch后的调用结果。接下来在dispatch这段代码中出现了compose函数, 熟悉函数式编程的朋友不难猜到其内部肯定是实现批处理chain的函数,并将store.dispatch泵送至其内部。上面源码分析后我们知道每一次执行dispatch时,都会先经过middleware的“洗礼”。

我们再来看看compose函数的内部实现:

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

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

  return funcs.reduce(function(a, b) {
    return function (...args) {
      return a(b(...args))
    }
  })
}

由上面代码可以看出compose最终返回的是一个函数,如果参数大于一时,我们采用reduce将上一个函数返回的结果传给下一个函数参数,以此来实现之间的参数共享和传递,非常经典的设计。

在掌握了redux中间件实现原理之后, 我们再来看redux-thunk源码就非常容易理解了。

redux-thunk源码分析

我们先看看这个github中star数15.1k的源码长啥子:

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

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

没错, 这就是redux-thunk的全部源码了,是不是很nice~。在上面的介绍中我们了解到redux中间件机制使得我们可以在中间件中拿到必备的dispatch, getState,并且在执行之前已经调用了两层middleware,此时我们可以解剖一下createThunkMiddleware,在第一次调用createThunkMiddleware是在chain阶段,即上面源码分析的: 深度剖析github star数15.1k的开源项目redux-thunk 所以这里的next也就是第二次调用时的store.dispatch, 为了实现同一函数内能执行多次dispatch,我们会判断如果action为函数,则执行action本身并把必要参数传递给它,否则则直接触发dispatch,这样我们就实现了支持action为函数并且支持异步多dispatch的功能了,读到这还是非常感叹其设计的优雅和简洁,不经让笔者感叹:学好函数式,走遍天下都不怕!

最后笔者准备了一个基于React+redux+redux-thunk的实战项目,github地址:

https://github.com/MrXujiang/redux_OA

感兴趣的可以学习参考一下。

最后

如果想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。 深度剖析github star数15.1k的开源项目redux-thunk

更多推荐

收藏
评论区

相关推荐

《彻底掌握redux》之开发一个任务管理平台(上)
前言 redux是上手react开发的必经之路,也是目前react项目中使用的最流行状态管理库。虽然我们不使用redux也可以通过react的state和父子props进行基本的数据通信和项目开发,但是对于一个大型项目而言,往往考虑的更多的是代码结构和组件之间的通信,我们需要一种很优雅且有利于扩展的方式去开发我们的复杂系统,所以这种情况下使用redux是最佳
10分钟教你手写8个常用的自定义hooks
前言 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。本文是一篇以实战为主的文章,主要讲解实际项目中如何使用hooks以及一些最佳实践,不会一步步再介绍一遍react hooks的由来和基本使用,因为写hooks的文章很多,而且官网对于react hooks的介绍也很详细
每天学点 JS 编码规范(2):Objects
(给前端大全加星标,提升前端技能) 英文:AirBnB,翻译:Kakaka Hou 为什么要在前端大全推送这个系列? 前段时间有读者通过我的个人微信,提议能不能推送一些编程规范的文章。我们以前关注过 Airbnb 在 GitHub 上开源的 JS 编程规范(现在有10万 star,可见受欢迎程度),但因为篇幅太长,一直没有推送过。现在化整为零,设立这
深度剖析github star数15.1k的开源项目redux-thunk
日益忙碌的一周又过去了,是时候开始每周一次的总结复盘了,今天笔者就来剖析一下github中star数15.1k的开源项目reduxthunk。 作为一名React方向的前端工程师,不管是被面试还是面试别人,大部分都会说起reduxthunk的实现原理,因为它非常经典且有用,而且代码量少的感人,只有短短12行代码,却能解决React开发中同一个函数支持多
做了N+1个企业项目之后, 我总结了这些React必备插件
为了提高大家开发 React 项目的效率, 笔者结合自己的实际工作经验, 汇总如下React项目常用插件. 1. 状态管理 Redux JavaScript 状
做了N+1个企业项目之后, 我总结了这些React必备插件
为了提高大家开发 React 项目的效率, 笔者结合自己的实际工作经验, 汇总如下React项目常用插件. 1. 状态管理 Redux JavaScript 状
JavaScript设计模式之英雄联盟
作者:黄梵高 原文: https://juejin.cn/post/6844904165982879758 构造函数模式 简介 在Jav
这些 JavaScript函数让你的工作更加 So Easy!
作者: YoussefZidan 译者:前端小智 来源:dev 点赞再看,养成习惯 本文 GitHub https://github.com/qq44924588...(https://github.com/qq449245884/xiaozhi) 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和
MobX 上手指南
之前用 Redux 比较多,一直听说 Mobx 能让你体验到在 React 里面写 Vue 的感觉,今天打算尝试下 Mobx 是不是真的有写 Vue 的感觉。 题外话 在介绍 MobX 的用法之前,先说点题外话,我们可以看一下 MobX 的中文简介。在 MobX 的中文网站上写着: MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程
问题 first path segment in URL cannot contain colon 的解决方案
目录问题解决 问题使用Golang开发流媒体服务器处理Post请求时,遇到了这个报错信息:2020/12/14 07:21:01 callback post failed2020/12/14 07:21:01 error::8080/api/callback: first path segment in URL cannot contain col
Vue UI组件库对比与选择
主流组件库对比PC端| UI组件库名 | github star | 是否可自定义主题 | 是否支持vue3 | 发布时间 | 稳定性 | 贡献者 || | | | | | | || | | 可自定义 | 支持,beta版本,, | 201608,4年半 | 长期支持,最新两个
Golang如何解析post请求中的json字符串
目录问题解决 问题使用Golang开发服务器,最常用的使用场景之一就是处理各种http请求。那么我们如何使用Golang解析Post请求中的Json字符串呢?今天我们就来通过一个实例了解一下。 解决首先,我们需要定义好对应的消息结构,也就是前端请求服务器的API接口。定义接口的话推荐使用工具YAPI编写,支持预
非常实用的GitHub项目
CoilCoil是Android上的一个全新的图片加载框架,它的全名叫做 coroutine image loader,即协程图片加载库。与传统的图片加载库Glide,Picasso或Fresco等相比。该具有轻量(只有大约1500个方法)、快、易于使用、更现代的API等优势。它支持GIF和SVG,并且可以执行四个默认转换:模糊,圆形裁剪,灰度和圆角。示例如
你要的几个JS实用工具函数(持续更新)
今天,我们来总结下我们平常使用的工具函数,希望对大家有用。1、封装fetch「源码:」/   封装fetch函数,用Promise做回调   @type get: (function()), post: (function(, ))  / const fetchUtil       get: (url)           return new 
cpu分析利器 — async-profiler
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。 简介asyncprofiler是一款采集分析java性能的工具,翻译一下github上的项目介绍:asyncprofiler是一款没有Safepoint bias problem的低开销java采集分析器,它利用HotSpot特殊的api来收集栈信息以及