美文网首页Web前端之路
根据Redux文档学源码(一)

根据Redux文档学源码(一)

作者: 9540cfc28488 | 来源:发表于2017-12-12 22:37 被阅读30次
    image.png

    redux应该是react学习中绕不过去的一个环节了,而且redux的源码也比较少,是学习的比较好的一个库,今天我们就结合着redux的手册来看看redux的工作原理到底是什么样子的。

    首先我们看看http://cn.redux.js.org/官网的例子中的第一句import { createStore } from 'redux';引入的createStore在redux中是如何实现的以及功能是什么。我们都知道createStore最常见的用法就是传入一个reducer返回一个store。在和react的配合使用中我们经常将reducer的入口文件传给createStore来得到一个store。

    Ok回到源码我们来看redux怎么实现的,首先从参数和返回值来看,接受一个reducer和一个初始的state,并且返回一个Store,

     * @param {Function} reducer 
     * @param {any} [initialState]
     * @returns {Store}
     
    export default function createStore(reducer, initialState) {
      invariant(
        typeof reducer === 'function',
        'Expected the reducer to be a function.'
      );
    
      var currentReducer = reducer;
      var currentState = initialState;
      var listeners = [];
      var isDispatching = false;
    
      function getState() {
        return currentState;
      }
      
      ....
    
      }
      
      
    

    下面来看看具体实现部分,首先invariant应该是一个错误提示的工具函数,不深究其内部实现逻辑,继续往下看到了四个赋值声明,每一个的意思是什么呢,从命名上来简单的猜测一下,应该分别是当前reducer,当前state,监听器,是否dispatch的falg。然后又定义了一个getState的函数,这个函数我们应该非常熟悉了,就是返回一个当前的state。

    继续往下看看下面的解释就直接在代码中写,感觉这样可能更加直观一点

      /*
      * @returns {Function} The reducer used by the current store.
      */
      function getReducer() {
        return currentReducer;
      }
      /* 
      从这个函数的返回值定义上应该就能一眼看出来,主要的作用就是返回当前store的reducer,
      但是我们平时开发时直接用到这个函数的机会应该几乎没有,而且现在这个api也已经被redux移除了。
      */
    
     /*
     * @param {Function} listener A callback to be invoked on every dispatch.
     * @returns {Function} A function to remove this change listener.
     */
      function subscribe(listener) {
        listeners.push(listener);
    
        return function unsubscribe() {
          var index = listeners.indexOf(listener);
          listeners.splice(index, 1);
        };
      }
      /*
      * 这里的subscript接受一个listener回调函数返回一个可以取消这个监听函数的函数(比较绕)可
      * 以理解成setTimeOut和clearTimeout这种关系。
      * 来看看返回的这个函数unsubscribe,他在内部主要做了两件事,一是找到当前这个listener监听
      * 函数在listeners数组中的index位置,然后将其从数组中移除
      */
    

    下面来看看比较关键的一个函数dispatch

    /*
    * @param {Object} action
    * @returns {Object}
    */
    function dispatch(action) {
        invariant(
          isPlainObject(action),
          'Actions must be plain objects. Use custom middleware for async actions.'
        );
        
        // 判断action是不是一个普通对象,如果不是的话返回一个错误提示
    
        invariant(
          isDispatching === false,
          'Reducers may not dispatch actions.'
        );
        
        // 判断当前Reducer有没有正在dispatch
    
        try {
          isDispatching = true;
          currentState = currentReducer(currentState, action);
        } finally {
          isDispatching = false;
        }
        // 执行当前的dispatch
    
        listeners.slice().forEach(listener => listener());
        // 执行绑定在此action上的监听函数
        
        return action;
      }
    /* 
    * 在这里我们可以看到dispatch函数接受了一个action对象,返回了一个对象,
    
    /**
     * Replaces the reducer currently used by the store to calculate the state.
     *
     * You might need this if your app implements code splitting and you want to
     * load some of the reducers dynamically. You might also need this if you
     * implement a hot reloading mechanism for Redux.
     *
     * @param {Function} nextReducer The reducer for the store to use instead.
     * @returns {void}
     */
    function replaceReducer(nextReducer) {
      currentReducer = nextReducer;
      dispatch({ type: ActionTypes.INIT });
    }
    

    从他的注释上我们可以了解到这个函数的作用就是替换当前store的reducer并计算出替换后的state,常用于你的应用需要代码分割以及你想要动态载入reducer的时候用。

    👌我觉得现在我们看的这些代码已经足够我们来解释下面的代码的内部运行原理了,一起来看看

    import { createStore } from 'redux';
    
    function counter(state = 0, action) {
      switch (action.type) {
      case 'INCREMENT':
        return state + 1;
      case 'DECREMENT':
        return state - 1;
      default:
        return state;
      }
    }
    
    let store = createStore(counter);
    
    
    store.subscribe(() =>
      console.log(store.getState())
    );
    
    
    store.dispatch({ type: 'INCREMENT' });
    // 1
    store.dispatch({ type: 'INCREMENT' });
    // 2
    store.dispatch({ type: 'DECREMENT' });
    // 1
    

    首先我们定义了一个counter函数,也就是一个reducer,然后把这个reducer传给了createStore,我们来看看发生了什么,在这只是简单的将传入的reducer赋值给了currentReducer。然后生成了一个store,之后再用这个store来注册一个监听函数,在注册监听函数这一步,也就是往listener这个数组push了一个监听函数,并返回了一个可注销监听的函数。接下来就是触发dispatch了,我们看看dispatch的时候都干了啥,首先判断传入的action是否为普通对象,这里就是一个对象,继续往下看看当前是否正在进行dispatch,没有,继续往下,然后执行currentReducer,也就是我们的counter函数,且传入的action就是dispatch所传入的对象,得到一个currentState然后循环执行listener里面的监听函数,我们这只有一个,就是打印出当前的state最后返回这个action,我猜想这里返回我们传入的action的作用应该是可以再进行下一次dispatch不知道理解的对不对。

    至此我们的根据redux文档看源码的第一部分也就结束了,下一篇文章我将继续跟随redux文档看看redux的其他API实现原理。

    相关文章

      网友评论

        本文标题: 根据Redux文档学源码(一)

        本文链接:https://www.haomeiwen.com/subject/ywjoixtx.html