一、Redux是什么?
家喻户晓,Redux最早使用于React框架中,是一个全局状态管理器。Redux解决了在开发过程中数据有限层层传递而引发的一系列问题,因而咱们有必要来理解一下Redux到底是如何实现的?
二、Redux的核心思想?
Redux次要分为几个局部:dispatch、reducer、state。
咱们着重看下dispatch,该办法是Redux流程的第一步,在用户界面中通过执行dispatch,传入绝对应的action对象参数,action是一个形容类型的对象,紧接着执行reducer,最初整体返回一个store对象,咱们来看下这部分的源码:
// 主函数createStore// 返回一个store对象export default function createStore(reducer, preloadedState, enhancer) { // 增强器 if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners let isDispatching = false // 获取最终的state function getState() { if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return currentState } // dispatch // 参数action function dispatch(action) { // 校验传入的action // action必须是个对象,否则抛出错误信息 if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // 测验action对象的必要属性 // type属性是action对象必要的属性 // 如果传入的action没有type属性,则抛出错误信息 if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true // 执行传入的reducer函数 // 返回state,给currentState赋值 currentState = currentReducer(currentState, action) } finally { // 一个dispatch执行完,还原状态 isDispatching = false } // 执行订阅函数队列 // dispatch执行的同时会一并执行订阅队列 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } // 返回action return action } // When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. // 默认执行一次dispatch,做初始化 dispatch({ type: ActionTypes.INIT }) // 返回一个store对象 return { dispatch, subscribe, getState, ... }}复制代码
通过源码咱们能够根本分明,通过执行createStore办法,最终会返回一个store对象,该对象次要裸露几个属性,咱们次要关注比拟罕用的:dispatch、getState、getState,看下理论用例:
import createStore from 'redux'// 创立一个reducerfunction reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } }}// 返回storeconst store = createStore(reducer, initState={})// 执行dispatchstore.dispatch({ type: 'TEST'})const state = store.getState() // 返回 {test: 'TEST'}复制代码
三、Redux中间件原理
接下来咱们来探讨Redux的另一个重要组成部分---中间件。什么是Redux的中间件?Redux中间件其实是通过重写createStore来加强和扩大原来的dispatch办法,使其可能在执行dispatch的同时能够同步执行其它办法,比方redux-thunk就是一个解决异步的中间件:
function createThunkMiddleware(extraArgument) { // 中间件规定格局 // 闭包返回三层嵌套 return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };}const thunk = createThunkMiddleware();thunk.withExtraArgument = createThunkMiddleware;export default thunk;复制代码
下载了中间件,那么咱们来看下如何应用中间件:
import createStore, {applyMiddleWare} from 'reduximport reduxThunk from 'redux-thunk'// 创立一个reducerfunction reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } }}// 返回store// 中间件作为applyMiddleWare的参数传入createStoreconst store = createStore(reducer, initState={},applyMiddleWare(reduxThunk))复制代码
咱们会发现,中间件的应用形式是用applyMiddleWare把中间件作为参数传入createStore中,那么applyMiddleWare是如何实现的?在这之前咱们先看下createStore办法的第三个参数是什么,咱们回看下createStore源码:
export default function createStore(reducer, preloadedState, enhancer) { ... // 增强器 // 第三个参数是enhancer,也就是咱们传入的applyMiddleWare if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // 在这里return了enhancer后果 // 传入了createStore,reducer,preloadedState // 实际上是重写了createStore return enhancer(createStore)(reducer, preloadedState) } ...}复制代码
看完了enhancer的理论作用,咱们能够弄清楚applyMiddleWare的实现原理,请看源码:
import compose from './compose'// 传入middlewares中间件export default function applyMiddleware(...middlewares) { // 闭包嵌套返回2个办法 return createStore => (...args) => { // 返回store const store = createStore(...args) let dispatch = () => { throw new Error( 'Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.' ) } // 返回一个对象 // 蕴含getState办法和dispatch办法 const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) // 返回一个全新的dispatch办法,不净化原来的dispatch } // 执行中间件第一层办法 // 回顾下两头的格局:({getState, dispatch}) => next => action => next(action) // 这里会比拟绕 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 返回一个中间件的函数汇合[next => action => next(action), next => action => next(action)] // 应用compose聚合chain函数汇合 // 返回新的dispatch dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } }}复制代码
这里可能会让人很纳闷,不大清楚的童鞋能够先看下中间件的标准写法,这里还有一个重要的函数compose,咱们来看下compose怎么解决chain函数汇合的,请看源码:
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */// 传入聚合函数汇合// 汇合为:[next => action => next(action), next => action => next(action)]// 返回一个新的函数: (arg) => arg export default function compose(...funcs) { // 判断如果没有则返回一个新函数 // 能够联想一下dispatch的定义 // function dispatch(action) { ... return action } if (funcs.length === 0) { return arg => arg } // 判断如果只有一个中间件,则间接返回第一个 if (funcs.length === 1) { return funcs[0] } // 这里用了reduce函数 // 把后一个的中间件的后果当成参数传递给下一个中间件 // 函数列表的每个函数执行后返回的还是一个函数:action => next(action) // 这个函数就是新的dispatch // 最初返回函数:(...args) => action => args(action) return funcs.reduce((a, b) => (...args) => a(b(...args)))}复制代码
compose的源码及其简洁,然而很精华,简直是整个中间件最出彩的中央。通过reduce把每个中间件都执行一遍,并且是通过管道式的传输,把每个中间件的返回后果当成参数传递给下一个中间件,实现了剥洋葱式的中间件模式。这里比拟难了解,老手能够先写几个简略的中间件,而后再去缓缓了解为什么要这么解决,了解后就会晓得这段代码有多简洁了。
参考React实战视频解说:进入学习
四、手写一个Redux
源码解析完了,咱们来简略实现一个redux。
createStore
// 判断值是否是对象类型function isPlainObject(obj) { if(!obj) { reutrn false } return Object.prototype.toString.call(obj) === '[object, Object]'}export default createStore(reducer, enhancer) { // 先判断有没有传入中间件 // 有则之间返回 if(typeof enhancer !== 'undefined') { // 必须是个函数 // 因为须要传参 if(typeof enhancer !== 'function') { return } return enhancer(createStore)(reducer) } let state = {} // 初始化state let listeners = [] // 公布订阅函数队列 // 定义getState 函数 function getState() { // 间接返回state return state } // 定义dispatch 函数 function dispatch(action) { try{ // 执行reducer, 返回state state = reducer(state, action) }catch(e) { console.log('dispatch error: 'e) } // 订阅 listeners.forEach(listener => listener()) // 返回action return action } // 定义subscribe 函数 function subscribe(listener) { if(!listener) { return } // 必须是回掉函数 // 因为须要在dispatch里执行 if(typeof listener !== 'function') { return } Listeners.push(listener) } // 返回对象:蕴含getState, dispatch, subscribe 三个办法 return { getState, dispatch, subscribe }}复制代码
compose
function compose(...funs) { if(!funs) { return arg => arg } if(funs.length === 1) { return funs[0] } // 遍历传入函数,返回一个新函数 return funs.reduce((a,b) => (...args) => a(b(...args))) }复制代码
applyMiddleWare
import compose from './compose'function applyMiddleWare(...middlewares) { return createStore => reducer => { // 先返回一个store const store = createStore(reducer) // 创立middleApi const middleApi = { getState: store.getState, dispatch: (...args) => store.dispatch(...args) // 返回一个新的dispatch } // 注入middleApi // 并返回函数汇合 const chain = middlewares.map(middleWare => middleWare(middleApi)) // 通过compose函数,执行所有中间件,并返回一个新的dispatch const dispatch = compose(...chain)(store.dispatch) // 返回store对象 return { getState: store.getState, dispatch } }}复制代码
logger中间件
function logger({getState, dispatch}) { return function(next) { return function(action) { console.log('prev') next(action) console.log('done') } } }复制代码
测试
import createStore from './myCreateStore' import applyMiddleWare from './myApplyMiddleWare' import logger from './logger' // 创立reducer function reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } } } // 引入中间件 const middleware = applyMiddleWare(logger) const store = createStore(reducer, middleware) // 返回{getState, dispatch}复制代码
总结
至此一个残缺的redux咱们就曾经剖析完了,集体认为中间件的compose这里是比拟不好了解的点,然而只有明确中间件次要要解决的是加强dispatch函数,就能够顺着这个思路去了解。接着再试着写几个中间件,进一步了解为什么中间件的格局须要返回嵌套的三层函数,明确了这两个点,redux的原理也就根本可能明确了,有问题欢送在评论中指出。