一 目标
- 实现 createStore 函数
- 讲清楚中间件原理(本文重点)
二 实现 createStore 函数
用法 createStore(countReducer, applyMiddleware())
function createStore(reducer, enhancer) {
if (enhancer) {
return enhancer(createStore)(reducer)
}
let currentState
let currentListener = []
function getState() {
return currentState
}
function dispatch(action) {
currentState = reducer(currentState, action)
currentListener.forEach(listener => listener())
return action
}
function subscribe(listener) {
currentListener.push(listener)
return () => {
const index = currentListener.indexOf(listener)
currentListener.splice(index, 1)
}
}
// 初始化数据
dispatch({ type: ''})
return {
getState,
dispatch,
subscribe
}
}
- createStore函数的接收两个参数,第一个参数是reducer, 第二参数是 applyMiddleware()函数执行后的增强器,对 dispatch 增强
- 执行 dispatch 会依次执行所有的中间件函数,最后执行createStore里的dispatch
三 中间件原理
中间件函数的格式
function middleware({ getState, dispatch }) {
return function(next) {
return function(action) {
// todo stuff
return next(action)
}
}
}
- 中间件位于 store.dispatch 的顶层
- 每个中间件都接收一个对象包含 getState, dispatch 属性
- 传给下一个中间件之前做自己的事情,最终执行 createStore 的 dispatch
下面实现两个简单的中间件
function thunk({ getState, dispatch }) {
return (next1) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState)
}
next1(action)
}
}
function logger({ getState, dispatch }) {
return (next2) => (action) => {
const prevState = getState()
console.log('prev state:', prevState)
const value = next2(action)
const nextState = getState()
console.log('next state: ', nextState)
return value
}
}
- thunk 就是dispatch中可以执行异步
- logger 打印state的值
实现 applyMiddleware 函数
function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
const store = createStore(reducer)
let dispatch = store.dispatch
const midApi = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
const chain = middlewares.map(middleware => middleware(midApi))
dispatch = compose(...chain)(store.dispatch)
return { ...store, dispatch}
}
}
function compose(...funs) {
if (funs.length === 0) {
return (arg) => arg;
}
if (funs.length === 1) {
return funs[0];
}
return funs.reduce((a, b) => (...args) => a(b(...args)));
}
上面说了 applyMiddleware 函数是增强 dispatch
调用中间件函数
applyMiddleware(logger, thunk)
传参的顺序会决定中间件的调用顺序,下面重点讲解一下中间件函数到底是怎么执行的,对 compose 函数不是太了解的,请先阅读另一个篇文章 函数组合
最难懂就是下面两行代码,把这两行代码搞懂了就明白了中间件到底是怎么调用的
const chain = middlewares.map(middleware => middleware(midApi))
dispatch = compose(...chain)(store.dispatch)
chain 中保存就是每个中间件执行后返回的内层函数
由于compose函数是从右向左依次执行函数的,所以执行这行代码的时候 compose(...chain)(store.dispatch)
- 先执行chain[1],参数next1就是store.dispatch
- 再执行chain[0],参数next2就是chain[1]的返回值
- 最后的返回值就是chain[0]中的 (action) = {}, 赋值给createStore中的dispatch,得到增强后的dispatch
调用store.dispatch时的执行顺行时和上面的执行顺序相反
- 先调用logger 中的 (action)=> {} 函数
- 再执行 thunk 中的 (action)=> {} 函数
- 最后执行createStore里的dispatch
四 总结
- 实现了 createStore() 函数
- 熟悉 redux 中间件的写法
- 分析了中间件函数的执行流程