Skip to content

redux实现原理分析 #2

@liang520

Description

@liang520

自己的状态和reducer

let initState = {
    count: 0
  }

function reducer(state,action){
    //如果没有初始值,那么就初始化一次
    if(!state){
        state=initState;
    }
    switch(action.type){
        case 'INCREASE':
        return {
            ...state,
            count:state.count+1
        }
        case "DECREMENT":
        return {
            ...state,
            count:state.count-1
        }
        default:
        return state;
    }
}

自己创建createStore

const createStore=function(reducer,initState){
    let state=initState;
    let listeners=[];
    function subscribe(listener){
        listeners.push(listener);
        //退订
        return function unsubscribe(listener){
            const index=listeners.indexOf(listener);
            listeners.splice(index,1)
        }
    }

    function dispatch(action){
        state=reducer(state,action);
        for(let i=0;i<listeners.length;i++){
            const listener=listeners[i];
            listener();
        }
    }
    function getState(){
        return state;
    }

    /**
     * 替换reducer
     * @param {*} nextReDucer 要替换的reducer
     */
    function replaceReducer(nextReDucer){
        reducer=nextReDucer
        dispatch({type:Symbol()});//刷一遍state状态
    }
    /**
     * 所有reducer都不匹配,会初始化所有的state值
     */
    dispatch({type:Symbol()});
    return {
        subscribe,dispatch,getState
    }
}

合并reducer方法

 /**
 * 合并
 * @param {*} reducers 传入reducer数据结构类似于{
                                counter: counterReducer,
                                info: InfoReducer
                            }
 */
function combinReducers(reducers){
    const reducerKeys=Object.keys(reducers);
    return function combination(state={},action){//返回一个新的reducer ,对所有传入的state和action重新做处理
        const nextState={};//定义新的state
        //遍历所有reducer,每个reducer都会返回相关的数据,处理之后,返回新数据,没有相关联的,就返回之前的数据
        for(let i=0;i<reducerKeys.length;i++){
            const key=reducerKeys[i];//reducer name
            const reducer=reducers[key];//reducer
            const preiousStateForKey=state[key];//传入state的某个值
            const nextStateForKey=reducer(preiousStateForKey,action);
            nextState[key]=nextStateForKey;//新构造出来一个
        }
        return nextState;//返回新的状态
    }
}

重点内容:中间件

//中间件是对dispatch的扩展
/**
 * 比如记录日志
 * 记录修改前的state,为什么修改,修改后的state
 * 通过重写dispatch来实现
 * 
 * const store=createStore(reducer);
 * const next=store.dispatch;
 * 
 * store.dispatch=(action)=>{
 *  console.log('this state',store.getState())
 *  next(action)
 *  console.log('next state',store.getState())
 * }
 * 
 * store.dispatch({
 *  type:'INCREMENT'
 * })
 * 
 * 输出日志
 * 
 */


/**
 * 
 * 记录异常
 * 
 * const store = createStore(reducer);
 * const next = store.dispatch;
 * 
 * store.dispatch=(action)=>{
 * try{
 *      next(action)
 *  }catch(err){
 *      console.log(err)
 *  }
 * }
 * 
 */

 /**
  * 如果两个功能都需要处理呢
  */

 const store = createStore(reducer);
 const next = store.dispatch;
 
 //单独把日志提取出来
 const loggerMiddleware = (action) => {
   console.log('this state', store.getState());
   console.log('action', action);
   next(action);
   console.log('next state', store.getState());
 }
 
 store.dispatch = (action) => {
   try {
     loggerMiddleware(action);
   } catch (err) {
     console.error('错误报告: ', err)
   }
 }

//把错误处理提取出来
//  const exceptionMiddleware = (action) => {
//     try {
//       /*next(action)*/
//       loggerMiddleware(action);
//     } catch (err) {
//       console.error('错误报告: ', err)
//     } 
//   }
//   store.dispatch = exceptionMiddleware;

  /**
   * 错误处理里面写定了日志,不可配,提取出来
   */

   const exceptionMiddleware=(next)=>(action)=>{
        try{
            next(action);//任意dispatch
        }catch(err){
            console.log(err);
        }
   }

  store.dispatch=exceptionMiddleware(loggerMiddleware);

 /**
* 扩展loggerMiddleware
* 
* const loggerMiddleware=(next)=>(action)=>{
*   console.log('this state',store.getState())
*   console.log('action',action)
*   next(action)
*   console.log('next state',state.getState())
* }
*/






/**
 * 两个中间件合作模式
 * 
 * 
 *  const store = createStore(reducer);
    const next = store.dispatch;
    const loggerMiddleware = (next) => (action) => {
        console.log('this state', store.getState());
        console.log('action', action);
        next(action);
        console.log('next state', store.getState());
    }
    const exceptionMiddleware = (next) => (action) => {
        try {
            next(action);
        } catch (err) {
            console.error('错误报告: ', err)
        }
    }
    store.dispatch = exceptionMiddleware(loggerMiddleware(next));
 * 
 */

/**
 * 单独把中间件拆分出来 ,里面包含了store,需要提取出来
 * 
 * 
 * const store=creatStore(reducer)
 * const next=store.dispatch;
 * 
 * const loggerMiddleware=(store)=>(next)=>(action)=>{
 *     console.log('this state',store.getState());
 *     console.log('action',action)
 * 
 *     next(action)
 *     console.log('next state',store.getState)
 * }
 * 
 * const exceptMiddleware=(store)=>(next)=>(action)=>{
 *     try{
 *         next(action)
 *     }catch(err){
 *           console.log(err)
 *     }
 * }
 * 
 * const logger=loggerMiddleware(store);
 * const exception=exceptionMiddleware(store);
 * store.dispatch=exception(logger(next))
 */

/**
 * 我们只需要构建出相关的中间件,其他的内部细节,通过creatStore扩展开来
 * 
 * 期望实现 
 * const newCreatStore=applyMiddleware(exceptionMiddleware,timeMiddleware,loggerMiddleware)(creatStore)
 * const store=newCreatStore(reducer);//return 新的store
 * 
 * 实现 applyMiddleware
 * 
 * const applyMiddleware=function(...middelwares){
 *    return function rewriteCreateStore(oldCreateStore){
 *        return function newCreatStore(reducer,initState){
 *            const store=oldCreateStore(reducer,initState);
 * 
 *            const chain=middelwares.map((middleware)=>middleware(store))
 * 
 *            let dispatch=store.dispatch;
 *            
 *            //实现 exception(time(logger(dispatch)))
 *            chian.reverse().map((middleware)=>{
 *                //更改dispatch
 *                dispatch=middleware(dispatch)
 *            })
 *            //重写
 *            store.dispatch=dispatch;
 * 
 *            return store
 *        }
 *    }
 * }
 */


/**
* 现在会有两种createStore
* 
* 没有中间件的creatStore
* import { createStore } from './redux';
* const store = createStore(reducer, initState);
* 
* 
* 有中间件的createStore
* const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware);
* const newCreateStore = rewriteCreateStoreFunc(createStore);
* const store = newCreateStore(reducer, initState);
* 
* 
* 为了统一起来,修改createStore方法
* 
* const createStore=(reducer,initState,rewriteCreateStoreFunc)=>{
*   if(rewriteCreateStoreFunc){
*       const newCreateStore=rewriteCreateStoreFunc(createStore);
*       return newCreateStore(reducer, initState)
*   }
*   否则按照正常流程走
* }
* 
* 
*/

/**
 * 最终用法
 * 
 * const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware);
 * const store = createStore(reducer, initState, rewriteCreateStoreFunc);
 */

一些优化的地方

 /**
  * 目前中间件能够拿到store,通过store修改我们的东西,保持最小开放策略
  * 传递给中间件的变成当前一个快照state
  * 之前const chain = middlewares.map(middleware => middleware(store));
  * 
  * 修改之后
  * const simpleStore = { getState: store.getState };
  * const chain = middlewares.map(middleware => middleware(simpleStore));
  * 
  */


  /**
   * compose
   * applyMiddleware中,把 [A, B, C] 转换成 A(B(C(next))),是这样实现的
   * const chain = [A, B, C];
   * let dispatch = store.dispatch;
   * chain.reverse().map(middleware => {
   *    dispatch = middleware(dispatch);
   * });
   * 
   * redux提供了一个方法compose
   * const chain = [A, B, C];
   * dispatch = compose(...chain)(store.dispatch)
   * 
   * 内部实现
   * export default function compose(...funcs) {
   *    if (funcs.length === 1) {
   *        return funcs[0]
   *    }
   *    return funcs.reduce((a, b) => (...args) => a(b(...args)))
   * }
   */

 /**
    * 省略initState
    * 有时候没有传initState,redux允许我们这么写
    * const store = createStore(reducer, rewriteCreateStoreFunc);
    * 内部实现
    * const createStore=(reducer,initState,rewriteCreateStoreFunc)=>{
    *   if(typeof initState ==="function"){
    *       rewriteCreateStoreFunc=initState;
    *       initState=undefined;
    *   }
    *   ...
    * }
    */


 /**
     * bindActionCreators
     * 
     * 这只有在react-redux的connect实现中用到,
     * 他通过闭包,把dispatch和actionCreator封装起来,让其他地方感觉不到redux的存在
     * 
     * 通过普通方式来隐藏dispatch和actionCreator,注意最后两行代码
     * 
     * const reducer=combineReducers({
     *      counter:counterRedcuer,
     *      info:infoReducer
     * });
     * const store=createStore(reducer);
     * 
     * 
     * //返回action的函数叫做actionCreator
     * function increment(){
     *  return {
     *      type:'INCREMENT'
     *  }
     * }
     * 
     * function setName(name){
     *      return {
     *          type:"SET_NAME",
     *          name:name
     *      }
     * }
     * 
     * const acrions={
     *      increment:function(){
     *             return store.dispatch(increment.apply(this,arguments))
     *      },
     *      setName:function(){
     *              return store.dispatch(setName.apply(this,arguments))
     *      }
     * }
     * 
     * ⚠️注意:我们可以把actions传到任何地方去,
     * 其他地方实现加减的时候,根本不需要知道其他细节
     * 
     * actions.increment();
     * actions.setName('123')
     * 发现有好多公共代码,在actions生成的过程中
     * 希望可以直接调用:const actions=bindActionCreators({increment,setName},store.dispatch);
     * 
     * 实现,通过闭包隐藏actionCreator和dispatch
     * 
     * fucntion bindActionCreator(actionCreator,dispatch){
     *      return function(){
     *          return dispatch(actionCreator.apply(this,argument))
     *      }
     * }
     * 
     * export default function buindActionCreators(actionCreators,dispatch){
     *      if (typeof actionCreators === 'function') {
     *          return bindActionCreator(actionCreators, dispatch)
     *      }
     *      if (typeof actionCreators !== 'object' || actionCreators === null) {
     *          throw new Error()
     *      }
     *      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 
     * }
     */

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions