redux做为前端状体管理中最亮眼的那个仔,很是有必要弄清楚他的原理。本文将从源码结合实践一块儿来从新认识redux。纯干货分享!!!javascript
redux相对来说是相对比较复杂的状态管理工具。实现一个全局状态管理工具,经过一个全局变量和一些方法,便可实现的东西,那么为何redux须要提出action,store,dispatch,reducer等一系列概念?提出这些概念的做用或者说动机是什么?但愿读者能从这篇文章中深刻理解这些概念存在的价值和意义。前端
export const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
);
复制代码
咱们常常看到这段代码,本文将从以createStore做为入口顺藤摸瓜带你认识整个框架。下面源码是v3.7.2版本的代码。java
首先来看createStore函数源码, 为了方便理解和阅读省略了不少无关的代码,你们在阅读的时候能够折叠起来看。react
export default function createStore(reducer, preloadedState, enhancer) {
// 若是只有两个参数,而且第二个参数是函数的话,将会传递给enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
// 省略一堆判断逻辑
return enhancer(createStore)(reducer, preloadedState)
}
// 一堆方法定义
dispatch({ type: ActionTypes.INIT });
return {
dispatch, // 重点讲述
subscribe, // 重点讲述
getState, // 返回state的方法
replaceReducer, // 高级用法,目的在于分包时,动态更换reducer
[$$observable]: observable
}
}
复制代码
function myEnhancer(createStore){
return (reducer, preloadedState, enhancer) => {
//建立store以前, do someSting
const store = createStore(reducer, preloadedState, enhancer)
//store以后, do something
return store;
}
}
复制代码
dispatch是咱们的重头戏,后面仍是介绍他,咱们先看下,当咱们dispatch({ type: 'INCREACE', payload: 1})会发生些什么呢。redux
function dispatch(action) {
// 各类检查acton类型
try {
isDispatching = true
// currentState是原来的state
// currentReducer就是一开始createStore时传入的reducer
currentState = currentReducer(currentState, action)
// reducer以后返回的是更新后的新的state
} finally {
isDispatching = false
}
// 更新监听者函数
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
复制代码
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
复制代码
subscribe是一个简单的监听者模式,该函数主要是收集监听者。源码很简单以下bash
function subscribe(listener) {
// 检查listener类型
let isSubscribed = true
ensureCanMutateNextListeners()
// 该函数会复制一份currentListeners
// 保障更新期间其余listener不受影响
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
// 省略部分错误检查
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
// 下次运行时 currentListeners会从新从nextListeners中取,能够看dispatch的代码
// 做者这样作的目的主要是为了防止dispatch执行期间发生subscribe或者unsubscribe引起异常错误
}
}
复制代码
到这里,整个redux的核心功能就介绍的差很少了,可是redux的威力并无体现出来,接下来咱们将介绍redux的扩展功能中间件。闭包
该函数是一个enhancer函数,由redux实现提供, 用来嵌入中间件,也是咱们常用的一个工具函数。架构
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
// 特别注意这个dispatch是使用let赋值的
// 这个预约义是为了防止用户提早使用,此时没法触发其余中间件
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
// 这个dispatch方法不能在next函数前使用
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
function loggerMiddleware({getState, dispatch}){ // 这部分对应的是middleware(middlewareAPI)
// 这块区域不能使用dispatch函数,不然会抛出错误!!
return next => action => {
console.time(action.type);
const result = next(action);
// result 对象是一个action类型的对象,若是中间件未修改过该值,则全等,通常来说,action不该该被修改
console.timeEnd(action.type);
return result; // 将会传入下一个中间中
}
}
复制代码
在书写中间件的时候,咱们发现内部闭包了多个函数,若是部分函数采用async等方式的话,就能够实现异步操做,解决反作用的问题,redux-thunk正是借用这种方式实现,感兴趣的同窗能够学习下,代码只有14行,这里就不展开讨论了。app
compose是一个函数构造器,返回一个新的函数。相似数学中的函数f(x),g(x),h(x)复合为f(g(h(x)))。上一个函数的输出做为下一个函数的输入。框架
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码
function couponA(next) {
if(next >= 500){
return next - 50;
}
return x;
}
function couponB(next){
return next * 0.9;
}
const discount = compose(couponA, couponB);
discount(1000); // 850
复制代码
当参数是一个值的时候,没法实现回旋镖的形式。上述例子实际上是一个简单的职责链模式,感兴趣的能够深刻挖掘,在电商打折规则中特别实用
这是一个工具函数,能够将多个reducer聚合起来,返回值是一个reducer(这是一个函数)
// reducers是一个
export default function combineReducers(reducers) {
// 省略对reducers作了一堆检查
// 下面这句是为了好理解,我杜撰的,非真实源码
const finalReducers = {...reducers}
const finalReducerKeys = Object.keys(finalReducers);
function combination(state = {}, action) {
// 此处省略了一些检查
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
// 这里以key划分命名空间,previousStateForKey为指定key下的state
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
// 每一个reducer都应该有返回值
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
复制代码
该函数是redux提供的一个工具函数,首先要弄清楚action和actionCreator的关系。action是一个普通对象,actionCreator是一个构造action对象的函数
bindActionCreator的目的是将actionCreator与dispatch结合构造出一个可以直接触发一系列变化的Action方法 bindActionCreators就是将多个actionCreator转化为Action方法
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
export default function bindActionCreators(actionCreators, dispatch) {
// 省略一系列检查
const keys = Object.keys(actionCreators)
const 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
}
复制代码
在实践中,结合reat-redux的connect的第二个参数mapDispatchToProps为例,展现actionCreators转化为能够直接运行的方法。
const actionCreators = {
increase: (payload) => ({ type: 'INCREASE', payload }),
decrease: (payload) => ({ type: 'DECREASE', payload })
}
@connect(
state => state,
dispatch => ({
actions: boundActionCreators(actionCreators, dispatch)
})
)
class Counter {
render(){
<div>
<button onClick={() => this.props.actions.increase(1)}>increase</button>
<button onClick={() => this.props.actions.decrease(1)}>decrease</button>
</div>
}
}
复制代码