redux深入进阶

上一篇文章讲解了redux如何使用,本篇文章将进一步深入,从redux的源码入手,深入学习redux的中间件机制。
在这里我们会以一个redux-thunk中间件为例,逐步分解redux的中间机制如何操作,如何执行。

闲话不多说,上代码

如何加载中间件

import { createStore,applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

// create a store that has redux-thunk middleware enabled
const createStoreWithMiddleware = applyMiddleware(
  thunk
)(createStore);

const store = createStoreWithMiddleware(rootReducer);

这里需要用到redux中提供的一个工具方法,叫做applyMiddleware,向该方法传入你想要使用的中间件,完了之后再传入createStore方法
最终形成新的创建store的方法

这显然是一个装饰器模式,通过不同的中间件对createStore方法进行修饰,最后形成新的createStore方法,那么创建的store就具有这些中间件的特性,
非常出色的设计,惊喜不仅在这,看了之后的代码你就更不得不佩服作者的代码设计能力。

瞬间觉得别人都是码神,而我就是码农有木有/(ㄒoㄒ)/~~

中间件加载机制的实现

先来看applyMiddleware方法的实现

import compose from './compose';

/** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks,such as expressing * asynchronous actions in a concise manner,or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous,this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */
export default function applyMiddleware(...middlewares) {
  return (next) => (reducer,initialState) => {
    var store = next(reducer,initialState);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,dispatch
    };
  };
}

这就是redux里面这个方法的源码,其中还一半是注释有木有。。。本来以为肯定有百来行代码
当然这里不得不说es6的特性提供了非常多的帮助,所以为了省力吧es6玩透还是灰常有必要的(更别说为了装X了(^__^) )

从这里开始代码就有点绕了,我们逐行分析

return(next) => (reducer,initialState) => {...}

整个applyMiddleware方法就是返回了一个方法,根据applyMiddleware方法的使用,我们可以知道next就是createStore方法
因为最终我们要返回的是一个装饰过的createStore方法,那么接收的参数肯定是不会变,所以最终我们调用createStoreWithMiddleware方法其实就是调用

function (reducer,initialState) {
    Box-sizing: border-Box; color: rgb(133,initialState); // next即为最初的createStore方法
    // ...以下省略
}
Box-sizing: border-Box; overflow: auto; font-family: 'Source Code Pro',initialState);
var dispatch = store.dispatch;
var chain = [];

这里没什么好讲的,首先创建了一个store,这个store就是最原始的通过createStore创建的store,后两行只是变量赋值

var middlewareAPI = {
  getState: store.getState,dispatch: (action) => dispatch(action)
};
chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);

这里是关键,必须详细进行讲解。

首先,这边声明了一个middlewareAPI对象,这个对象包含两个方法

  1. getState:store中的getState方法的引用

  2. dispatch:对本身的dispatch方法进行一次封装

然后

chain = middlewares.map(middleware => middleware(middlewareAPI));

我们来仔细看看这行代码,首先我们对所有的中间件进行一个map,map结果就是调用中间件方法,将middlewareAPI作为参数传入,
这里我们拿redux-thunk中间件举例,来看看一个中间件是长什么样子的,传入的参数又是用来干嘛的。

export thunkMiddleware({ dispatch,getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch,getState) :
      next(action);
}

redux-thunk功能是让action支持异步,让我们可以在action中跟服务器进行交互等操作,而他的实现。。。(⊙﹏⊙)b是的,又是这么几行代码

我们回顾之前的代码,在map所有中间件的时候我们调用thunkMiddleware方法,传入两个方法dispatchgetState,然后返回了一个方法
我们大致抽象一下,应该如下:

function (next) {
  
  return function (action) {
    Box-sizing: border-Box; color: rgb(133,getState) :
      next(action)
  }
  
}

于是我们接下去分析applyMiddleware里面的代码

现在我们知道chain是一个数组,每一项是调用每个中间件之后的返回函数

dispatch = compose(...chain)(store.dispatch);

compose是redux里面的一个帮助函数代码如下:

compose(...funcs) {
  return arg => funcs.reduceRight((composed,f) => f(composed),arg);
}

~~(>_<)~~我已经不想再吐槽什么了,

我们看到这边先调用compose函数,传入了结构后的chain数组,然后compose函数返回的也是一个函数

function (arg) {
  return funcs.reduceRight((composed,arg);
  // funcs就是中间件数组
}

然后我们把store.dispatch函数作为arg传入这个结果,这里reduceRight可以参考这里
。那么这边得到的结果是什么呢?

// 假设中间件数组是[A,B,C]
// 那么结果就是A(B(C(store.dispatch)))

再次结合redux-thunk来看,我们假设只有一个中间件,那么最终的dispatch方法就是

function (action) {
  'function' ?
    action(dispatch,getState) :
    next(action)
}
// 这里的next方法,就是真正的store.dispatch方法
// 这里的dispatch是(action) => store.dispatch(action)

我们再结合redux-thunk的使用方法来分析一下,

incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    },1000);
  };
}

这是使用redux-thunk时可以定义的异步action,我们触发action的时候调用的是

dispatch(incrementAsync())

incrementAsync返回的是

function (dispatch) {
  setTimeout(() => {
    // Yay! Can invoke sync or async actions with `dispatch`
    dispatch(increment());
  },152);">1000);
}

这个时候我们回想经过中间件加工的dispatch方法

action是一个函数,所以action === 'function' ?成立,那么就执行action, 并把中间件接收到的dispatch方法(action) => store.dispatch(action)方法作为参数传入,在异步方法执行完之后再次触发真正的action。如果action不是异步的,那么久直接返回一个对象,这个时候action === 'function' ?不成立,就直接调用next,也就是原始的store.dispatch方法

我们再接着想,如果我们有许多个中间件,那么没一个中间件的next就是下一个中间件直到最后一个中间件调用store.dispatch为止。

以上的代码非常绕,建议去专研一下源码。这么精简的代码包含了非常多的函数式编程的思想,也用到了装饰器模式的原理,不得不说:

太烧脑啦/(ㄒoㄒ)/~~


原文https://segmentfault.com/a/1190000004612224

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom