redux 思考以及源码解析

1. 基本概念

redux有以下几个基本概念:

1.1. action

action: 是一个对象,对一个行为的基本描述

{
    type:'add',
    todo 
}

1.2 action creator

一个函数,返回结果是一个action

function add (todo) {
        return {
            type: 'add',
            todo
        }
    }

1.3 reducer

真正更新数据操作的函数

let todoReducer = function (state = todoList, action) {
    switch (action.type) {
        case 'add':
            return [...state, action.todo]
        case 'delete':
            return state.filter(todo => todo.id !== action.id)
        default:
            return state
    }
}

store: 只有一个
dispatch:触发一个action
subscribe : 订阅store
getState :获得当前的state
replaceReducer :更换reducer
observable :

2. createStore源码解析

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }
  

如果 preloadState是函数,并且enhancer为空, enhancer =preloadState
接着,如果有enhancer ,那么 return enhancer(createStore)(reducer, preloadedState)
结合我们的调用分析一下我们通常调用的代码:

let store = createStore(todoReducer, applyMiddleware(thunk,logger))

是怎么来的。

preloadState是函数,并且enhancer为空, enhancer  =  applyMiddleware(thunk,logger)

return enhancer(createStore)(reducer, preloadedState) =  return applyMiddleware(thunk,logger)(createStore)(reducer, preloadedState) 

3. applymiddleware 源码解析

中间件是通过next来进入下一个中间件的,执行完毕后,会调用最原始的store.disptach,reducer执行完毕后,该次操作并没有完毕, 还会依次返回到中间件。
任何一个中间件不next ,其后面的中间件都不会执行

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

所有中间件的格式都是第一层参数都是 {disptach,getState}这个样子

 ({dispatch, getState} ) => next => action => {......}
 

第二句就是把每个middleware传入参数,初始化一下,这里的最大作用就是利用闭包,让每个middleware拥有同一份disptach和getState的引用。

核心的一句:

dispatch = compose(...chain)(store.dispatch)

这样的函数是整个作为前面一个函数的next参数存在的,所以你每次next(action)实际上就是进入下一个中间件的执行体,

3.1 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;

4. compose源码解析

其作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。
如:

compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 
 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)))
}

把b(...args)作为参数传入a a(b(...args))再执行

我们知道reduce函数返回是一个值。上面函数传入的回调函数是(a, b) => (...args) => a(b(...args))其中a是当前的累积值,b是数组中当前遍历的值。假设调用函数的方式是compose(f,g,h),首先第一次执行回调函数时,a的实参是函数f,b的实参是g,第二次调用的是,a的实参是(...args) => f(g(...args)),b的实参是h,最后函数返回的是(...args) =>x(h(...args)),其中x为(...args) => f(g(...args)),所以我们最后可以推导出运行compose(f,g,h)的结果是(...args) => f(g(h(...args)))。发现了没有,这里其实通过reduce实现了reduceRight的从右到左遍历的功能,但是却使得代码相对较难理解。在Redux 1.0.1版本中compose的实现如下:

export default function compose(...funcs) {
     return funcs.reduceRight((composed, f) => f(composed));
}

5. bindActionCreator源码解析

bindActionCreators:对disptach的一种封装,可以直接执行或者通过属性方法的调用隐式的调用dispatch,而不用显式调用dispacth

例子:
// action creater

let actionCreaters = {
    add: function (todo) { //添加
        return {
            type: 'add',
            todo
        }
    }, delete: function (id) {
        return {
            type: 'delete',
            id
        }
    }
}

let boundActions = bindActionCreators(actionCreaters, store.dispatch)
boundActions.add({
    id: 12,
    content: '睡觉觉'
})

let boundAdd = bindActionCreators(actionCreaters.add, store.dispatch)
boundAdd({
    id: 13,
    content: '陪媳妇'
})

源码:

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  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 = {}
  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
}

bindActionCreators.js 里面有一个 bindActionCreator,bindActionCreators 方法,

bindActionCreators会根据传入的是函数还是对象,采取不同的处理方式,
入参是函数,返回函数; 传入对象,返回对象。

6. combineReducers源码解析

combineReducers:把recuder函数们,合并成一个新的reducer函数,dispatch的时候,挨个执行每个reducer

在 reducer 层级的任何一级都可以调用 combineReducers。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer,甚至更多层。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值