redux源码以及中间件剖析

729 阅读3分钟

redux相信使用react进行开发项目的过程中是必然会使用到的第三方库,并且他相比于vuex来说有很大的独立性,他并不依赖于react语言包。你对于redux的源码以及中间件有深入理解吗?

redux工作流

redux的工作流程:派发一个dispatch事件会经过store里面的reducer函数在改变初始的state状态,当state发生变化又会通过store的forceUpdate进行对于组件的强制更新。

redux使用以及中间件的应用

redux只是能支持同步内容的执行,并且只能使用对象的形式作为action传到reducer中作为参数,如果要用函数作为参数传递的时候,redux就会报错,因此需要借助于一些中间件进行支持!

## 创建store,src/store/index.js
import {createStore} from "redux";
function countReducer(state = 0, action) {
   switch (action.type) {
   case "ADD":
   return state + 1;
   case "MINUS":
   return state - 1;
   default:
   return state;
    }
}
const store = createStore(countReducer);
export default store;

## 应⽤中间件
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import counterReducer from './counterReducer'
const store = createStore(counterReducer, applyMiddleware(thunk, logger));

import { createStore, applyMiddleware } from "redux";
代码我们可以看出redux会抛出两个方法:一个是createStore,另一个是应用中间件:applyMiddleware
const store = createStore(counterReducer, applyMiddleware(thunk, logger));
从代码可以看出createStore是一个函数,并且reducer以及应用中间件函数式作为参数进行执行

redux在组件中的使用

import React, {Component} from "react";
import store from "../store/";

export default class ReduxPage extends Component {
componentDidMount() {
 this.unsubscribe = store.subscribe(() => {
   this.forceUpdate();
 });
}

componentWillUnmount() {
 if (this.unsubscribe) {
   this.unsubscribe();
 }
}

add = () => {
 store.dispatch({type: "ADD"});
};

asyAdd = () => {
 store.dispatch((dispatch, getState) => {
   setTimeout(() => {
     dispatch({type: "ADD"});
   }, 1000);
 });
};
promiseMinus = () => {
 store.dispatch(
   Promise.resolve({
     type: "MINUS",
     payload: 100
   })
 );
};
render() {
 return (
   <div>
     <h3>ReduxPage</h3>
     <p>{store.getState()}</p>
     <button onClick={this.add}>add</button>
     <button onClick={this.asyAdd}>asyAdd</button>
     <button onClick={this.promiseMinus}>promiseMinus</button>
   </div>
 );
}
}

从在组件的使用中可以看出redux是有dispatch方法用来派发执行时间,getState是用来获取最新的state状态,subscribe是用来进行强制更新的回调。

综上所述,我们对于源码有以下几点的实现:

1、createStore函数的实现,并且抛出了getState、dispatch、subscribe 函数
2、applyMiddleware函数的实现,核⼼任务是实现函数序列执⾏。

源码

1、createStore函数 主要原理是使用了发布订阅模式

export default function createStore(reducer, enhancer) {
  // enhancer是应用中间件函数
  if (enhancer) {
  	// enhancer需要对于dispatch进行加强
    return enhancer(createStore)(reducer);
  }
  // 存储当前状态
  let currentState;
  // 存储订阅时间的强制更新函数
  let currentListeners = [];
  // 获取store state获取
  function getState() {
    return currentState;
  }

  // 修改状态
  function dispatch(action) {
  	// 更新state状态并进行组件的更新
    currentState = reducer(currentState, action);
    currentListeners.forEach(listener => listener());
  }
  function subscribe(listener) {
   //收集组件更新方法
    currentListeners.push(listener);
    return () => {
      // 简单置空,大家可以自己实现过滤
      currentListeners = [];
    };
  }
  // 派发一个唯一的函数名,以获取最初始化的state状态
  dispatch({type: "KKKKREDUX/OOOOOO"});

  return {
    getState,
    dispatch,
    subscribe
  };
}

2、applaymiddlware的实现

export default function applyMiddleware(...middlewares) {
  return createStore => reducer => {
    let store = createStore(reducer);
    // 这是原版的dispatch,这个dispatch只能接受plain object,不能处理异步、promise
    let dispatch = store.dispatch;
    const midApi = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    };

    const middlewaresChain = middlewares.map(middleware => middleware(midApi));

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

    return {
      ...store,
      // 加强版的dispatch
      dispatch
    };
  };
}
// 对于中间件的处理,使用的是组合函数即高阶函数
function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}