一、中间件(midleware)是做什么的?
Redux 不能自动处理 store.dispatch 的返回的函数(异步函数),但在 dispatch和 reducer 之间增加一个中间件就可以将这个函数转换成适合redux处理的内容。
action ---> dispatch ---> middleware 1 ---> middleware 2 ---> reducers
中间件就是一个函数对 store.dispatch() 方法的重新包装,也就是在 action 和 reducer 这两步之间添加了其他功能生成一种洋葱模型
store.dispatch会被层层包装成一个洋葱模型,这样就可以在里面执行一些副作用和异步操作。
二、为什么中间件去改造 dispatch 而不是在其他的位置?
1) 先来看一下Redux的一个同步的例子。
//reducer
const Redux = (state, action) => state
//store
const store = createStore(Redux)
//action
{ type: 'NAME', value: 'value' }
//store.dispatch
store.dispatch({ type: 'NAME', value: 'value' })
由于:
Reducer是纯函数只承担对 State 的计算。
view 和state 一一对应也不能作为承担其他的功能。
action 存放数据的对象,只能被别人操作,自己不能执行任何操作。
**所以: **就选定在store.dispatc() 方法中添加功能既可以在数据 action 发出时做出相应的动作也可以更容易的操作 action 。
那就先做一个简易的中间件看看
let next = store.dispatch;
store.dispatch = function dispatch_Log(action) {
console.log('action:', action);
next(action);
console.log('next state:', store.getState())
}
可以看到将原来的方法进行保留然后重新对 dispatch 重新定义在调用 next(action) 时候开始打印功能。让我们在回顾一下对中间件的定义:中间件就是一个函数对 store.dispatch() 方法的重新包装,也就是在 action 和 reducer 这两步之间添加了其他功能生成一种洋葱模型
三、使用示例
import { createStore, applyMiddleware } from 'redux';
//state
const state = {
score: 2
}
//Reducer
const reducer = (state, action) => {
switch(action.type) {
case: 'SCORE_VALUE'
return { ...state, score: action.score}
}
}
//中间件 (看不懂没关系往下看)
cosnt logger = ({ getState, dispatch } => next => action =>{
console.log('log开始:',action)
let returnValue = next(action)
console.log('log结束:', action)
return returnValue
})
//store
let store = createStore(reducer, state, applyMiddleware(logger))
//store.dispatch
store.dispatch({
type: 'SCORE_VALUE',
score: 5
})
首先先看 createStore 这个函数接收三个参数 createStore(reducer, [preloadedState], enhancer)
reducer(Function): 接收两个参数,分别是当前的 state 树和要处理的 [action](www.redux.org.cn/docs/Glossa… [state 树](www.redux.org.cn/docs/Glossa…- [
preloadedState] (any): 初始时的 state。enhancer(Function):enhancer是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。(请牢牢记住这一点)
为了深入了解 enhancer 来先看以下 它接收那些参数 。由于 enhancer 是返回一个新的强化后的 store creator 可以先看到 StoreCreator 它的函数签名:
StoreCreator 的函数签名为
type StoreCreator = (reducer: Reducer, State: ?State) => Store
enhancer 的签名为
type enhancer = (next: StoreCreator) => StoreCreator
现在就明白了 enhancer 作为一个类似于 react 高阶组件的去接收 StoreCreator 然后在返回一个加强版的 StoreCreator 需要注意:enhancer 就是指代的 applyMiddleware(...midleware)
//Reducer
const reducer = (state, action) => {
switch(action.type) {
case: 'SCORE_VALUE'
return { ...state, score: action.score}
}
}
//store
let store = createStore(reducer, applyMiddleware(thunk))
注明:高阶组件是 react 的一种设计模式 它是一个函数接收一个组件为参数然会在返回新的组件
为了直观的感受 enhancer 的魅力 所以...
export default function applyMiddleware(...middlewares) {
// 接收 createStore 参数
return (createStore) => (reducer, preloadedState, enhancer) => {
// 保存 store disptch chain
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
// 传递给中间件的参数
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 注册中间件调用链,并由此可知,所有的中间件最外层函数接收的参数都是{getState,dispatch}
// 这里就可以明确为什么编写中间件(logger)需要接收一个{ getState, dispatch }
chain = middlewares.map(middleware => middleware(middlewareAPI))
//compose 函数起到代码组合的作用:compose(f, g, h)(...args) 效果等同于 f(g(h(...args)))
//从此也可见:所有的中间件最二层函数接收的参数为 dispatch,一般我们在定义中间件时这个形参不叫dispatch 而叫 next,
//是由于此时的 dispatch 不一定是原始 store.dispatch,有可能是被包装过的新的 dispatch。
dispatch = compose(...chain)(store.dispatch)
//被包装后的dispatch 也被称为 next
// 返回经 middlewares 增强后的 createStore
return {
...store,
dispatch
}
}
}
//中间件 (现在看懂了吗?)
cosnt logger = ({ getState, dispatch } => next => action =>{
console.log('log开始:',action)
let returnValue = next(action)
console.log('log结束:', action)
return returnValue
})
这里就能感受到 action --中间件--> reducer 中间件的拦截作用
异步中间件 redux-thunk 简化版
const thunk = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
//如果传入的 action 是个函数那么他会将参数传入里面
//直到 action 不是参数的情况下开始执行 next(action)
//这里没有完全明白 看看阮一峰博客
}
return next(action);
};
function thunk () {
return function ({dispatch, getState}) {
return function (next) {
return function (action) {
if(typeof action === 'function'){
return action(dispatch, getState)
}
return next(action)
}
}
}
}
compose 函数实现
// ...funcs === [f,g,h], store.dispatch
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg //透传
}
if (funcs.length === 1) {
return funcs[0]
}
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
// composed 是上次计算的返回值 f 是当前元素 last(...args)是初值第一运行就会赋值给 composed
// f(g(h(store.dispatch)))
}
array.reduceRight(function(total, currentValue, currentIndex, arr), initialValue) **作用:**从数组的末尾向前将数组中的数组项执行 所定义的函数 操作