Redux-实现applyMiddleware

最近在读Redux源码,现将自己实现的代码和理解贴上,后期再补充详细~

中间件:类似于插件,可以在不影响原本功能、并且不改动原本代码的基础上,对其功能进行增强。在Redux中,中间件主要用于增强dispatch函数。

实现Redux中间件的基本原理,是更改仓库中的dispatch函数。

中间件本身是一个函数,该函数接收一个store参数,表示创建的仓库,该仓库并非一个完整的仓库对象,仅包含getState,dispatch。该函数运行的时间,是在仓库创建之后运行。

由于创建仓库后需要自动运行设置的中间件函数,因此,需要在创建仓库时,告诉仓库有哪些中间件。需要调用applyMiddleware函数,将函数的返回结果作为createStore的第二或第三个参数。

中间件函数必须返回一个dispatch创建函数,applyMiddleware函数,用于记录有哪些中间件,它会返回一个函数,该函数用于记录创建仓库的方法,然后又返回一个函数。

手写applyMiddleware

middleware的本质,是一个调用后可以得到dispatch创建函数的函数。

compose:函数组合,将一个数组中的函数进行组合,形成一个新的函数,该函数调用时,实际上是反向调用之前组合的函数。

主代码:

import {compose} from "./compose";
/**
 * 注册中间件
 * @param  {...any} middlewares 
 */
export function applyMiddleware(...middlewares) {
    // 接收creatStore方法
    return function (createStore) {
        // 接收reducer和默认状态 ,用于创建仓库
        return function (reducer, defaultState) {
            //创建仓库
            const store = createStore(reducer, defaultState);
            let dispatch = () => { throw new Error("目前还不能使用dispatch") };

            const simpleStore = {
                getState: store.getState,
                // 这里不能写成dispatch: dispatch,否则一直是上面那个报错的dispatch
                // 也不能写成store.dispatch,否则一直是最原始的dispatch
                // 写成函数形式是为了保证引用地址一致
                dispatch: (...args) => dispatch(...args)
            }
            
            //给dispatch赋值
            //根据中间件数组,得到一个dispatch创建函数的数组
            const dispatchProducers = middlewares.map(mid => mid(simpleStore));
            // 在完成dispatch前,是调用不了simpleStore中的dispatch的,只有等包装完
            dispatch = compose(...dispatchProducers)(store.dispatch)
            return {
                ...store,
                dispatch,
            }
        }
    }
}

compose代码:

export function compose(...funcs) {
    if (funcs.length === 0) {
        return args => args; //如果没有要组合的函数,则返回的函数原封不动的返回参数
    } else if (funcs.length === 1) {
        //要组合的函数只有一个
        return funcs[0];
    }

    // redux官方写法,在下面附有步骤分析,非常巧妙
    // return funcs.reduce((a, b) => (...args) => a(b(...args)));

    // 这是可读性好一些的写法,和上面代码功能一样
    return function (...args) {
        let lastReturn = null; // 记录上一个函数返回的值
        for (let i = funcs.length - 1; i >= 0; i--) {
            const func = funcs[i];
            if (i === funcs.length - 1) {
                lastReturn = func(...args);
            } else {
                lastReturn = func(lastReturn);
            }
        }
        return lastReturn;
    }

}

function func1(n) {
    return n * 2;
}

function func2(n) {
    return n + n
}

function func3(n) {
    return n - 5
}

[func1, func2, func3].reduce((a,b)=>(...args) => a(b(...args)));

/**
 * (...args) => func1(func2(...args)), func3
 * (...args) => ((...args) => func1(func2(...args)))(func3(...args)) 
 * 函数执行变成如下代码
 * (...args) => func1(func2(func3(...args)))
 */

上面的实例代码传入一个数,会发现是从func3至func1运行的,参照redux中间件写法才是从func1至func3运行,代码如下所示:

function func1 (next) {
    return function (action) {
        action = action * 2
        next(action);
    }
}
 
function func2 (next) {
    return function (action) {
        action = action + 20
        next(action);
    }
}
 
function func3 (next) {
    return function (action) {
        action = action / 4
        next(action);
    }
}


let comp = [func1, func2, func3].reduce((a,b)=>(...args) => a(b(...args)))((action)=>console.log(action))

comp(6)

// 输出 8

上述代码是从func3开始,至func1将代码一层层包裹起来,这也就是洋葱模型,当运行时就会从func1开始运行了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值