ASP JSP动态网站开发网站排名怎么做
一、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 = reducerlet currentState = preloadedStatelet currentListeners = []let nextListeners = currentListenerslet isDispatching = false// 获取最终的statefunction 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// 参数actionfunction 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()}// 返回actionreturn 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'// 创建一个reducer
function reducer(state={}, action) {switch(action.type) {case 'TEST':return {...state,test: 'test success'}}
}// 返回store
const store = createStore(reducer, initState={})// 执行dispatch
store.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,也就是我们传入的applyMiddleWareif (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {throw new Error('Expected the enhancer to be a function.')}// 在这里return了enhancer结果// 传入了createStore,reducer,preloadedState// 实际上是重写了createStorereturn enhancer(createStore)(reducer, preloadedState)}...
}
看完了enhancer的实际作用,我们可以弄清楚applyMiddleWare的实现原理,请看源码:
import compose from './compose'// 传入middlewares中间件
export default function applyMiddleware(...middlewares) {// 闭包嵌套返回2个方法return createStore => (...args) => {// 返回storeconst 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函数集合// 返回新的dispatchdispatch = 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把每个中间件都执行一遍,并且是通过管道式的传输,把每个中间件的返回结果当成参数传递给下一个中间件,实现了剥洋葱式的中间件模式。这里比较难理解,新手可以先写几个简单的中间件,然后再去慢慢理解为什么要这么处理,理解后就会知道这段代码有多简洁了。
四、手写一个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 = {} // 初始化statelet listeners = [] // 发布订阅函数队列// 定义getState 函数function getState() {// 直接返回statereturn state}// 定义dispatch 函数function dispatch(action) {try{// 执行reducer, 返回statestate = reducer(state, action)}catch(e) {console.log('dispatch error: 'e)} // 订阅listeners.forEach(listener => listener())// 返回actionreturn 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 => {// 先返回一个storeconst store = createStore(reducer)// 创建middleApiconst middleApi = {getState: store.getState,dispatch: (...args) => store.dispatch(...args) // 返回一个新的dispatch}// 注入middleApi// 并返回函数集合const chain = middlewares.map(middleWare => middleWare(middleApi))// 通过compose函数,执行所有中间件,并返回一个新的dispatchconst 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'// 创建reducerfunction 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的原理也就基本能够明白了,有问题欢迎在评论中指出。