Redux学习总结
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
使用Redux应该遵循一下原则:
- 整个应用共享的state应该存储在store的状态树中,store是唯一的;
- state不能直接修改,只能通过action表达修改的意图,调用dispatch()修改state;
- state的修改规则reducers必须是一个纯函数,不能有副作用。
Redux提供的API
createStore
createStore方法的作用就是创建一个 Redux store 来以存放应用中所有的 state。
createStore(reducer, [preloadedState], [enhancer])
createStore方法接受三个参数 ,后面两个是可选参数:
reducer :参数的类型必须是 function。
preloadedState : 这个参数代表初始化的state(initState),可以是任意类型的参数。
enhancer : 这个参数代表添加的各种中间件,参数的类型必须是function。
中间的preloadedState参数可以直接省略不写,redux已经对这种情况做了处理:
/*
*如果第二参数preloadedState 是function,且第三个参数enhancer 省略,
*把第二个参数赋值给enhancer
*/
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
createStore提供的方法:
getState() :
返回当前的state,利用闭包获得currentState。
subscribe(listener) :
订阅事件,传入的参数listener必须是一个函数。主要作用是当state发生改变的时候,能够得到通知,通过subscribe,传入的监听函数在dispatch函数中会全 部执行一次。
返回值是一个函数,执行返回的函数会从listeners中移除当前的listener。这里也是利用了闭包。
function subscribe(listener) {
//判断listenner是不是一个函数
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
}
//定义这个变量是防止已经从listeners中移除的listener再次取消订阅
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
//利用闭包,取消订阅
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See http://redux.js.org/docs/api/Store.html#subscribe for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
dispatch(action) :
参数action必须是一个对象,且必须含有type字段;
作用:执行reducer,修改store里面的数据;执行订阅函数。
function dispatch(action) {
//检查传入的action是不是一个对象
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
//检查传入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.')
}
//执行reducer
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
//执行订阅函数
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
replaceReducer(nextReducer) :
动态的替换当前的reducer
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}
observable():
这个方法查阅其他资料解释的是配合其他特点的框架或编程思想来使用的,如rx.js,一般用不到
这个方法我也不太懂 - -#,官方推荐的学习渠道 https://github.com/tc39/proposal-observable
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer) {
if (typeof observer !== 'object') {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
if (observer.next) {
observer.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
createStore总共暴露了五个API:
- dispatch
- subscribe
- getState
- replaceReducer
- [$$observable]: observable
前三个是常用的API
combineReducers
redux总共暴露5个的API,除了createStore之外,combineReducers是另外一个比较重要的API。
combineReducers主要是把多个reducer合并成一个,并且返回一个新的reducer函数,该函数接收的参数也是两个state和action。
//这是combineReducers.js导出的方法
//这个方法接收reducersexport default function combineReducers(reducers) {
//reducer是一个对象,reducerKeys:存储reducers key的数组 const reducerKeys = Object.keys(reducers) const finalReducers = {} //这个循环是为了去除reducers中类型不是function的值 for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] //不是生产环境的话,只有key没有value会给出警告 if (process.env.NODE_ENV !== 'production') { //如果reducer中有一个只有key没有value,value是undefined给出警告 if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } //如果reducers[key]是一个函数,添加到finalReducers中,基本上都是function if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] //把reducers中的函数加进finalReducers } } //获取finalReducer中的key值 const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { //检查每一个reducer是否有错误
//初始化之后state==='undefined'抛出错误
//执行了一个位置的action.type返回 undefined 抛出错误 assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } //返回值,返回一个函数,接收state和action为参数 return function combination(state = {}, action) { //有错误抛出异常 if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { /**
* 给出警告信息
* 没有传入reducers给出警告
* 如果传入的state不是对象给出警告
* 还有其他警告信息是针对unexpectedKeyCache这个参数进行判断的
*/ const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false const nextState = {} //把传入进来的reducer循环执行 for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] //reducer是一个函数 const reducer = finalReducers[key] /** * 如果 combineReducers({reducer1, reducer2, reducer3}) 类似这样 * 生成的state {reducer1, reducer2, reducer3} 就会类似这样,和传入的参数 */ //获取当前reducer对应的state部分 const previousStateForKey = state[key] //执行当前的reducer,获取执行之后的state const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey
//如果nextStateForKey和previousStateForKey不一致说明reducer执行之后改变了nextStateForKey的值
//两个的值不相等,hasChanged为true,返回nextStete hasChanged = hasChanged || nextStateForKey !== previousStateForKey } //如果state在reducer执行之后发生了变化,返回nextState,否则返回传入的state return hasChanged ? nextState : state } }
compose
先看这个函数是因为在applyMiddleware这个函数中用到了这个函数。
export default function compose(...funcs) {
//如果没有传入funcs返回一个函数
if (funcs.length === 0) {
return arg => arg
}
//如果funcs的长度为1,返回这个函数
if (funcs.length === 1) {
return funcs[0]
}
/**
* funcs长度大于1,把funcs中的函数从右至左依次组合起来
* compose(fn1,fn2,fn3,fn4) 返回 (...args) => fn1(fn2(fn3(fn4(...args))))
*/
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
示例:当创建store时同时用到中间件和redux-devtools会用到compose:
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
window.devToolsExtension
? window.devToolsExtension()
: undefined
)
)
applyMiddleware
applyMiddleware是用来扩展自定义功能的middleware(中间件),包装store的方法实现我们需要达到的目的。applyMiddleware可以接收多个middleware。
export default function applyMiddleware(...middlewares) {
//返回createStore
return createStore => (...args) => {
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.`
)
}
let chain = []
//暴露两个参数给middleware
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
//传入middlewareAPI执行middleware函数,返回执行后的结果
chain = middlewares.map(middleware => middleware(middlewareAPI))
//这里用到的是compose方法,得到一个嵌套执行的函数
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
这一段代码参考一下一个已经实现的中间件更好理解,下面是官方文档中使用的redux-thunk的源码
function createThunkMiddleware(extraArgument) {
//返回一个函数, 接收middlewareAPI{dispatch, getState}参数
//这里连续的箭头函数,是柯里化的写法
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
//export的就是createThunkMiddleware的返回值
export default thunk;
从这段代码中可以看出来 export 的是({ dispatch, getState })这个匿名函数,接收的参数就是 applyMiddleware 暴露的 middlewareAPI 参数。
bindActionCreator
bindActionCreator的主要作用就是将action与dispatch函数绑定,生成直接可以触发action的函数。
function bindActionCreator(actionCreator, dispatch) { //执行返回的function可以触发action return function() { return dispatch(actionCreator.apply(this, arguments)) } } export default function bindActionCreators(actionCreators, dispatch) { //如果actionCreators是一个函数,返回一个执行之后触发action的函数 if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } //如果传入的参数不是一个object或者是null抛出错误(typeof null === 'object' 为true) if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error( `bindActionCreators expected an object or a function, instead received ${ actionCreators === null ? 'null' : typeof actionCreators }. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ) } const keys = Object.keys(actionCreators) const boundActionCreators = {} //循环执行bindActionCreator,把返回值放进boundActionCreators对象中 for (let i = 0; i < keys.length; i++) { const key = keys[i] const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
结尾
看完redux的源码之后,只能说这代码真的太漂亮了。