乐趣区

关于前端:miniredux-实现原理讲解-第三讲

文章首发于我的博客 https://github.com/mcuking/bl…

相干代码请查阅 https://github.com/mcuking/bl…

上一讲实现了 react-redux,从而能够更加优雅地在 react 中应用 redux。

然而,一个关键问题没有解决:异步操作怎么办?Action 收回当前,Reducer 立刻算出 State,这叫做同步;Action 收回当前,过一段时间再执行 Reducer,这就是异步。

怎么能力 Reducer 在异步操作完结后主动执行呢?这就要用到新的工具:中间件(middleware)。

中间件原理

为了了解中间件,让咱们站在框架作者的角度思考问题:如果要增加性能,你会在哪个环节增加?

  • Reducer:纯函数,只承当计算 State 的性能,不适合承当其余性能,也承当不了,因为实践上,纯函数不能进行读写操作。
  • View:与 State 一一对应,能够看作 State 的视觉层,也不适合承当其余性能。
  • Action:存放数据的对象,即音讯的载体,只能被他人操作,本人不能进行任何操作。

只有发送 Action 的这个步骤,即 store.dispatch()办法,能够增加性能。举例来说,要增加日志性能,把 Action 和 State 打印进去,能够对 store.dispatch 进行如下革新。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

store.dispatch“ 进行了重定义,在发送 Action 前后增加了打印性能。这就是中间件的雏形。
中间件就是一个函数,对store.dispatch“ 办法进行了革新,在收回 Action 和执行 Reducer 这两步之间,增加了其余性能。

中间件编写

在编写中间件之前,咱们先看下在真正的 redux 外面是如何应用中间件的,当应用单个中间件时代码如下,其中 thunk 就是一个中间件:

const store = createStore(counter, applyMiddleware(thunk))

有下面的代码能够看出,咱们须要做三件事件

  • 第一步 对 createStore 函数进行扩大,使其可能接管第二个参数——中间件
  • 第二步 定义 applyMiddleware 函数,使其可能将一个中间件退出到 redux 中
  • 第三步 实现一个中间件——redux-thunk

对 createStore 函数进行扩大

代码如下,检测是否有增强器,若存在则先用增强器对 createStore 进行扩大加强

export function createStore(reducer, enhancer) {
    // 如果存在增强器,则先用增强器对 createStore 进行扩大加强
    if (enhancer) {return enhancer(createStore)(reducer)
    }
    ...
}

定义 applyMiddleware 函数

依据下面的代码,咱们能够晓得,applyMiddleware(中间件)返回的是一个高阶函数,接管参数 createStore 后,返回一个函数,而后再接管参数 reducer。
因而对应代码如下:

export function applyMiddleware(...middlewares) {return createStore => (...args) => {
        // 第一步 取得原生 store 以及原生 dispatch
        const store = createStore(...args)
        let dispatch = store.dispatch
        
        const midApi = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        // 第二步 将原生 dipatch 传入中间件进行扩大加强,生成新的 dispatch
        dispatch = middleware(midApi)(dispatch)

        return {
            ...store, // 原生 store
            dispatch, // 加强扩大后的 dispatch
        }
    }
}

在上述代码中,咱们先是取得原生 store 以及原生 dispatch,组成 midApi,即中间件 API,而后将其传入中间件,执行中间件内定义的操作,返回一个函数,再传入原生 dispatch,再返回一个加强后的 dispatch,最初传入 action。加强后的 dispatch 如下:

dispatch(action) = middleware(midApi)(store.dispatch)(action)

实现中间件 redux-thunk

异步操作至多要送出两个 Action:用户触发第一个 Action,这个跟同步操作一样,没有问题;如何能力在操作完结时,零碎主动送出第二个 Action 呢?

奥秘就在 Action Creator 之中。

// action creator
export function addGun() {return {type: ADD_GUN}
}

export function addGunAsync() {
    return dispatch => {setTimeout(() => {dispatch(addGun())
        }, 2000)
    }
}

上文中有两个需要,第一个需要是store.dispatch “ 一个 action 对象(即{type: ADD_GUN}), 而后立刻加机枪,即:

addGun = () => store.dispatch(addGun())
addGun()

第二个需要是 store.dispatch 一个函数,这个函数外部执行异步操作,在 2000ms 之后再执行 store.dispatch(addGun()),加机枪,然而store.dispatch 参数只能是 action 这样的对象,而不能是函数。store.dispatch的无关源码如下:

function dispatch(action) {
    // reducer 依据老的 state 和 action 计算新的 state
    currentState = reducer(currentState, action)
    // 当全局状态变动时,执行传入的监听函数
    currentListeners.forEach(v => v())
    return action
}

为了可能让让 store.dispatch 可能接管函数,咱们能够应用 redux-thunk,革新store.dispatch“,使得后者能够承受函数作为参数。

因而,异步操作的一种解决方案就是,写出一个返回函数的 Action Creator,而后应用 redux-thunk 中间件革新 store.dispatch。

革新后的 dispatch 解决 addGunAsync 函数生成的 action(一个函数):

// action creator
export function buyHouse() {return {type: BUY_HOUSE}
}

function buyHouseAsync() {
    return dispatch => {setTimeout(() => {dispatch(buyHouse())
        }, 2000)
    }
}

dispatch(buyHouseAsync()) = middleware(midApi)(store.dispatch)(buyHouseAsync())

因而 redux-thunk 对应代码如下:

const thunk = ({dispatch, getState}) => next => action => {
    // next 为原生的 dispatch
    // 如果 action 是函数,执行一下, 参数是 dispatch 和 getState
    if (typeof action === 'function') {return action(dispatch, getState)
    }
    // 默认间接用原生 dispatch 收回 action,什么都不做
    return next(action)
}

即判断 action 如果是一个函数,则执行这个函数。否则间接用原生 dispatch 收回 action,什么都不做

这样咱们就能够通过 redux-thunk 中间件,实现了增强版的 dispatch 能够接管函数作为参数,而咱们在函数外面进行异步操作,异步操作实现后用原生 dispatch 收回 action,从而实现了 redux 的异步操作全局状态的性能。

相干文章如下:

  • mini-redux 实现原理解说 第一讲
  • mini-redux 实现原理解说 第二讲
  • mini-redux 实现原理解说 第三讲
  • mini-redux 实现原理解说 第四讲
退出移动版