React和redux,ES6等前端开发经验记录总结(都是干货,看到是你的运气)

React和redux,ES6等前端开发经验记录总结(都是干货,看到是你的运气)

前言

毫不夸张,你能看到这篇文章,是你的运气,里面都是干货,在理解js的道路上容易误解的和关键点的知识。牛逼先不吹….看内容

有趣的reduce函数

它是js里的一个方法,reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

形成函数调用链

var test1 = function(){
    console.log("方法一:test1");
}
var test2 = function(str){
    console.log("方法二:test2" + str);
}
var test3 = function(str){
    console.log("方法三:test3."+str);
    return "方法三被执行"
}
var arr = [test1,test2, test3]
var all = arr.reduce(function(a,b){
    return function(){
        return a(b.apply(undefined, arguments)) 
    }
})
all("ss");

结果

方法三:test3.ss
方法二:test2方法三被执行
方法一:test1

计算和

var numbers = [65, 44, 12, 4];
var total = numbers.reduce(function(a,b){
    return a+b;
})
console.log(total);

结果125

reduce的语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

redux里compose函数

function compose() {
  for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
    funcs[_key] = arguments[_key];
  }

  if (funcs.length === 0) {
    return function (arg) {
      return arg;
    };
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce(function (a, b) {
    return function () {
      return a(b.apply(undefined, arguments));
    };
  });
}

使用方法

compose.apply(undefined, 数组)(参数);

箭头函数的返回值

不使用大括号,则箭头后面的代码就是返回值

var sum = (num1, num2) => num1 + num2 

使用大括号,必需显示使用return,否则返回undefined

var sum = (num1, num2) => { return num1 + num2 }
var sum = (n1, n2) => {
  console.log(n1);
  return n1 + n2
}

使用箭头函数返回对象:必须在对象外面加上括号,否则就只是个代码块,返回undefined

var sum = () => ({name: 'a'})
等同于
var sum = function sum() {
  return { name: 'a' };
};

js里特殊的函数,行参和实参

我发现js里行参和实参完全可以理解为2条并行的线,比如

function t1(){
    console.log("打印参数: " + arguments.length);
    return "t1";
}
console.log(t1("1"));

t1函数在定义时没有指明可接收参数,但我们依然可以传入参数,这个传入的参数就是实参,可以通过arguments获取实参。
使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们,用 arguments[0],即第一个参数的值(第一个参数位于位置 0,第二个参数位于位置 1,依此类推)。

argumentsparameters经常被混为一谈,为了这个教程我们还是做一个2者的区分。在大多数标准中,parameters 是我们定义函数时设置的名字(形参),arguments (或者是实参)是我们传入函数的参数,看下如下的函数

function foo(param1, param2) {
    // do something
}
foo(10, 20);

这个函数里,param1 和 param2 是函数的形参,而我们传入函数的值10,20是实参。

怎么理解js预编译阶段和执行阶段(重要)

3个示例

var a = 2;
var b = 3;

//例子1
function test1(){
    if(b==3){
        //使用var声音变量
        var c = 3;
    }
    console.log("c=" + c);
}
test1()
console.log("分隔线")

//例子2
function test2(){
    console.log("b:" + b);
    var b = 3;
    console.log(b);
}
test2();

//例子3
function test3(){
    if(b==3){
    //注意使用的是es6里的let声明变量
        let c = 3;
    }
    console.log("c=" + c);
}
test3()
console.log("分隔线")

结果如下

c=3
分隔线
b:undefined
3
D:\gitSource\dy\dongli\testScope.js:24
        console.log("c=" + c);
                           ^

ReferenceError: c is not defined
    at test2 (D:\gitSource\dy\dongli\testScope.js:24:21)
    at Object.<anonymous> (D:\gitSource\dy\dongli\testScope.js:26:1)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:612:3

为什么会有这个结果呢?
js解析代码按如下阶段执行的
1. 语法分析(分析语法)
2. 预编译(预处理js文件)
3. 解释执行(边解释边执行)3

js是单线程执行,es5之前(注意es6之后加入了let和const声明变量,但javascript是不认的,预编译时会把它当成普通代码)javascript中的两种声明方式,var和function,前者声明的是变量,后者声明的是方法。对应的预编译阶段也有两种处理方案
使用var定义的变量在内存中开辟一块内存空间并指向变量名,且赋值为undefined,使用function声明的函数,则同样会进行开辟内存空间,但赋值的对象会将声明的函数赋值给函数名。
预编译阶段会先声明变量名,再声明函数名,不管代码中声明变量和声明函数的顺序如何。
以上面例子为便解释过来
js引擎扫描js代码块(js里以<script>或文件区别代码块),先扫描var声明的变量,结果有a,b两个变量,同时赋值为underfined,即a=underfined,b=underfined。然后扫描functioni声明的函数,结果有test1,test2,test3三个函数,同时赋值为声明时的函数体,即test1=function(){...},test2=function(){...},test3=function(){...},然后预编译就结束了,注意我说的是脚本的预编译就结束了,后面调用函数时,会对函数体内的代码执行预编译,然后就是执行阶段。

注意区别声明式函数与赋值式函数,凡是使用var的都设为underfined

function Fn(){
    //声明式函数
}
var Fn = function(){
    //赋值式函数
}

执行阶段按代码顺序执行,a,b赋值为对应的值。a=2;b=3
- 然后调用 test1()函数,在调用test1()函数之前也有一次对函数的预编译。执行函数体里所以var和function声明的变量,扫描到var c=3,然后将它提升到函数体顶部,同时赋值c=underfined。然后执行函数体内代码,直到执行到var c=3定义的地方才会给c赋值,否则c就是一直是underfined,故,当b=3时,打印日志c=3,当b=2时,由于c的赋值代码得不到执行,打印日志c=underfined
-调用test2()函数,预编译函数体内代码块,扫描到var b = 3;,赋值为b=underfined,然后执行代码,第一个log为underfined,之后赋值b=3,这时候b已经不是underfined了,执行了赋值语句,所以第二个log中b为3。这里又声明一个与全局变量同名的变量b,在函数体内,会覆盖b的值
-调用test3()函数,预编译函数体内代码块,由于let是es6声明变量语法,js引擎目前还识别不了,预编译时不会把它当成var的处理方案,所以c的作用域在if条件语句里,所以会报错误。

有2次预编译,一次是代码块的预编译,它对整个代码块里用var和function声名的变量处理,但它不会对函数体内的代码做处理。
第二次是函数体块的预编译,它对函数体内用var和function声名的变量处理。

总结
js的预编译有2条,整个js代码块和函数体内代码块预编译。第一次只处理整个js代码块,直接执行阶段调用函数才会预编译函数体内代码块。
变量var声明的变量预处理赋值为underfined,function声明的函数赋值为对应的函数体。在执行阶段从上往下执行,遇到赋值语句就赋值,赋值之后才是真正的初始化值。

js的闭包,scope(域)

scope(域)

参考深入理解JS之Scope链拓展阅读
这两个概念必需一起理解,闭包是讲编译方式,scope是讲闭包的原理 ,相辅相成的。
由于js有预编译的阶段,然后再是执行阶段。在预编译阶段,会根据var和function关键字找到声明的变量和函数,然后抽出来。在执行阶段,根据代码定义赋值的地方初始化var声明的变量,在调用函数时,会创建新的scope(域),scope存在的内容就是预编译阶段抽出的var声明和变量和function声明的函数,注意保存的是引用,里面的内容可能会被代码修改。比如

//例子1
function test1(){
    console.log("name:" + name);
    return function(){
        console.log("name:" + name);
    }
}
//执行这个函数时,创建scope,scope里维护的变量name还是underfined,返回一个函数
var t=test1()
console.log("分隔线")
var name="cy"
//但在执行t函数时,由于name值已经发生改变,scope维护的name值也已经发生了变化
t()

函数在定义的时候就会创建一个scope,保存当前环境下的数据,在执行函数时,内部会创建一个新的scope,这个scope内的数据是根据代码的执行不断变化的。这个新的scope当前执行的函数可以有一份,如果函数返回另一个函数,那另一个函数在定义的会创建一个scope。
总结一句话,函数定义时,会根据当前环境创建一个scope并保持关系,在函数执行时创建一个新的scope,2个scope是父子依赖关系,在JavaScript中所有的域都是并且只能是被函数域(function scope)所创建,它们不能被for/while循环或者if/switch表达式创建

第一个scope内部数据为{name:underfined, test1:function(){...}}由于执行第一个test1()时,name还没初始化,所以为underfined),第二个scope是函数体内部scope为{c=3},2个scope构建scope链,查找变量,先从内部开始找,找不到向上游找,直到找到全局scope。

理解scope要点
-函数在定义时,会生成一个scope
-函数在执行时,会创建一个新的scope,也叫做本地scope,同时创建一个链接向上父scope,scope域内维护的数据是预编译时筛选的var和function声明的东西。不是环境执行时上下文环境变量
- scope可以用于创建函数时保存函数定义时一些数据

this变量

对于this,我们可以理解为:特殊的Scope引用变量,其指向当前函数的执行环境Scope(并不是定义时的Scope)
比如

var a = 1;
function doit(){
    var b = 2;

    return function(){
        var c = 3;
        console.log(this);
    }
}

doit()();

可以使用apply改变执行上下文时环境,比如doit.apply(funderfined, "666")

闭包

参考一分钟理解js闭包拓展阅读
我觉得闭包就出现就是为了保存执行环境时一些变量数据。。
有权访问另一个函数作用域内变量的函数都是闭包,所以如下2种都是闭包

function test(){
    var name=cy;
    return function(){
        console.log(name);
    }
}

function test(){
    var name=cy;
    function testInner(){
        console.log(name);
    }
}

我们一般喜欢通过一个函数返回另一个函数的闭包形势,将外部数据变成函数的局部变量,然后返回的函数在定义时创建一个scope,scope保存有这个局部变量值,这样一来,这个返回的函数就会一直保存传入的数据。

上面的内容是基础,必需理解和掌握,如果不理解那么下面内容也一知半解。

redux里combineReducers函数

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。说明了2点.
- combineReducers合并多个Reducer函数后,返回的是一个函数。
- combineReducers返回的函数传入createStore函数里,比如const store: Store = createStore(reducers);

比如有如下2个reducer

//reducer  todos.js
//state初始化值是数组
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, action.data.name];
    case 'TOGGLE_TODO':
      return [...state, action.data.age];
    default:
      return state;
  }
};

//reducer  visibilityFilter.js
//state初始化值是字符串
const visibilityFilter = (state: VisibilityFilter = 'SHOW_ALL',action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default:
      return state;
  }
};

上面2个reducer里的state默认值类型不同,赋予不同类型的值是为了说明 reducer 是可以处理任何类型的数据结构的。你完全可以选择那些符合你的需求的数据结构作为 state 的值。(例如,字面量对象、数组、布尔值、字符串或其它不可变结构)。

完整的使用combineReducers函数的例子如下

import todos from './todos';
import visibilityFilter from './visibilityFilter';

const reducers = combineReducers({
  todos,
  visibilityFilter
});

const store=createStore(reducers);

备注,得益于ES6的语法糖,上述写法有前提是State 的属性名必须与子 Reducer 同名,换句话说,combineReducers接收到对象里,key是state里的key,value是reducers函数名。
那么combineReducers到底怎么执行的呢?
函数源码如下

export default function combineReducers(reducers) {
  //传入的对象里,value值的reducers函数必需存在,如果为underfined则给错误提示。
  const reducerKeys = Object.keys(reducers)
  //finalReducers对象保存的就是key-value键值对,key是state状态数据的key(下面会有体现),value是reducers函数
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  //得到keys,就是state里keys
  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }

  let shapeAssertionError
  try {
      //安全检查,combineReducers 实施的一次安全检查,用以确保 reducer 永远不会返回undefined。
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  //返回一个函数
  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    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]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

从源码可以看到combineReducers函数就是把传入的reducers对象存入返回的函数作用域内,同时返回一个函数,每次调用redux的dispatch函数时,这个函数就会得到执行。
我们设断点调试一下看看
从调用const store=createStore(reducers);开始,执行createStore时,函数内部执行dispatch({ type: ActionTypes.INIT });代码,用于初始化store维持的state状态数据。当调用dispatch函数时,combineReducers返回的函数就会得到执行。
这里写图片描述
从图中可以看到执行这个大函数。第一次执行时,state={},arguments是函数调用时实际传入的函数值。
var action = arguments[1];这行代码得到的就是{ type: ActionTypes.INIT }
这里写图片描述
从图中我们可得知如下内容

  • 开头的时假使用combineReducers函数绑定reduces时
const reducers = combineReducers({
  todos,
  visibilityFilter
});

传入的参数对象的key其实用于state的key的,value是reducers函数。
代码state[_key]从state里拿到对应key的值,由于key对应的value可以为合意值,所以reducers函数里接收的state可以为任意类型数据,其中reducer接收的state数据是对应这个模块的数据,不是整个store里的state。
- 执行了reducer函数。var nextStateForKey = reducer(previousStateForKey, action);,看到这里你应该明白reducer是怎么执行的了吧?
- 初始化时state是空的,第一次dispatch({ type: ActionTypes.INIT });时才会得到最初的数据
- 每dispatch一个action,这个大函数都会执行,for循环遍历finalReducerKeys(可以理解为遍历state里所有keys),然后根据key从store.state里获得对应模块的数据。同时将dispatch派发的action和取得的模块数据state一同传reducer函数,得到一个初始化值。根据这次得到的数据和上一次数据比较得到是否数据有变量,只要一次有变化,整个store.state就用重新计算后的nextState

总结
combineReducers执行后返回一个函数,这个函数的作用域内保留有传入的reducers对象,当调用store.dispatch函数时,这个返回的函数就会得到执行。执行方式就是循环每一个“模块”,根据key得对应的state模块数据和reducer函数,最后执行reducer(state,action)函数计算对应模块数据有没有变化。有变化则store.state就使用计算后的nextState

bindActionCreators函数

这里Redux里的一个函数,bindActionCreators(actionCreators, dispatch)由于绑定dispatchactionCreators,从结果论上来讲两者关系就是先执行creator函数,将返回的action传给dispatch派发。那绑定关系到底怎样的呢?从源码来说

function bindActionCreators(actionCreators, dispatch) {
  //如果是函数,那么直接调用bindActionCreator,返回一个闭包函数,闭包函数里的作用域scope保留有定义环境下的变量数据。
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch);
  }

  if ((typeof actionCreators === 'undefined' ? 'undefined' : _typeof(actionCreators)) !== 'object' || actionCreators === null) {
    throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators === 'undefined' ? 'undefined' : _typeof(actionCreators)) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');
  }
  //Object.keys用于获取对象的所有key值,返回数组
  var keys = Object.keys(actionCreators);
  var boundActionCreators = {};
  //如果是对象,那么遍历每一个对象,都调用一次bindActionCreator函数
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var actionCreator = actionCreators[key];
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
  }
  return boundActionCreators;
}

//使用闭包保存数据的功能,返回一个函数,先执行actionCreator返回一个action,然后使用store.dispatch(actioni)派发消息。
function bindActionCreator(actionCreator, dispatch) {
  return function () {
    return dispatch(actionCreator.apply(this, arguments));
  };
}

官文给bindActionCreators(actionCreators, dispatch)解释是:把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象。同时使用 dispatch 对每个 action creator 进行包装,以便可以直接调用它们。
每个action里都有自己的动作(函数),函数的key是不一样的。创建一个boundActionCreators对象key就是传入的creatorAction,value就是封装的函数。也就是把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象,key还是原来的key,key对应的value变成了封装的函数对象function () { return dispatch(actionCreator.apply(this, arguments)); };

bindActionCreators(actionCreators, dispatch)具体怎么用要api讲完才能明白,现在只要知道bindActionCreators函数接收actionCreatorsstore.dispatch2个参数。返回一个对象,包含同名的key-value,value是一个关联dispatchactionCreators两个参数的函数。通过key直接调用的就是这个返回的函数。比如react-redux结合使用的,actionCreator被映射到组件的props,在组件内部this.props.testActioinCreator(参数)调用的函数就是返回的函数。

applyMiddleware(…middlewares)函数

其实applyMiddleware是无形参的函数,但由于js的特殊性,可以使用arguments获取实参的数据。神奇不神奇,这块内容上面有讲到。

function applyMiddleware() {
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  //返回一个函数,其实我们要知道为什么他要通过一个函数返回另一个函数,是为了使用闭包的特性(保存函数定义时当前环境数据),而闭包的功能又依赖scope作用域链
  return function (createStore) {
    return function () {
      for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
        args[_key2] = arguments[_key2];
      }

      var store = createStore.apply(undefined, args);
      var _dispatch = function dispatch() {
        throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
      };

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch() {
          return _dispatch.apply(undefined, arguments);
        }
      };
      var chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

applyMiddleware是应用中间件函数,接收可变参数,将可变参数传入(简单的理解就是传入,标准就是函数当前环境变量)返回的函数中。而返回的函数又是另一个闭包,接收createStore(它就是redux里创建store的createStore函数)函数作为参数,其实applyMiddleware应用中间件函数最终目的就是拿到中间件变量middlewares和createStore函数,然后执行这个函数。
具体这个功能要放在createStore里讲。

dispatch(action)函数

功能是派发action,把action当作数据传给combineReducers包装的函数。我们知道,combineReducers返回的函数中会遍历所有的reducer,然后把action都传进去,直到遇到匹配的处理,然后继续for循环。

function dispatch(action) {
   //判断action是否是纯对象
    if (!isPlainObject(action)) {
      throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
    }
    //判断action非空
    if (typeof action.type === 'undefined') {
      throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
    }

    //store类内部变量,isDispatching是否正处理派发数据
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.');
    }

     //try catch  finally确保处理完数据后isDispatching=false.
    try {
      isDispatching = true;
      //执行currentReducer函数,currentReducer是combineReducers处理之后返回的函数,返回store里整个currentState状态数据。
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    //for循环监听函数,监听函数是通过subscribe添加的函数
    var listeners = currentListeners = nextListeners;
    for (var i = 0; i < listeners.length; i++) {
      var listener = listeners[i];
      listener();
    }

    return action;
  }

所以store.dispatch的函数目的就是接收action,然后执行reducerslisteners函数集,返回最新的state,保存在store里。

store.dispatch如果应用了中间件拿到的函数就不是上面源码贴出的函数了,中单件会将上面函数封装一遍,store.dispatch拿到的就是封装之后的函数。

createStore

创建redux的store
源码

//reducer是combineReduces返回的函数,preloadedState是state初始化状态数据,enhancer是增加函数,一般使用中间件函数传入preloadedState变量
function createStore(reducer, preloadedState, enhancer) {
  var _ref2;
  //如果enhancer为undefined,preloadedState是函数,那么将preloadedState函数变成增强函数
  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.');
    }
    //将createStore传入增加函数,同时把reducer和preloadedState传入(当然此时preloadedState为undefined)
    return enhancer(createStore)(reducer, preloadedState);
  }
...
}

一般store.dispatch接收的是action对象,而redux-thunk是让store.dispatch能接收函数的中间件。redux-thunk改造了store.dispatch函数,然后重新赋值为store.dispatch,所以store.dispatch获取的函数已经不是以前的dispatch了,是改造之后的dispatch,然后dispatch最终会调用store里源码里的dispatch。

redux-thunk完整源码如下

function createThunkMiddleware(extraArgument) {
  return function (_ref) {
    var dispatch = _ref.dispatch,
        getState = _ref.getState;
    return function (next) {
      //这里的next才是原始的redux里dispatch函数,而dispatch其实是中间件改造之后的dispatch函数。
      return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
        return next(action);
      };
    };
  };
}

var thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

其实上面的dispatch函数就是return里的返回函数,接收

return function (action) {
  if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
  }
  return next(action);
};

这个函数即可以接收纯的action对象,又可以接收如下的函数(dispatch, getState, extraArgument)=>{/**逻辑代码**/.... dispatch(action)}做为action.

一般中间件定义函数如下
function customMiddle(ref){
var dispatch = _ref.dispatch;
var getState = _ref.getState;
return function (next) {
return function (action) {
if (typeof action === ‘function’) {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
}

createStoreenhancer(createStore)(reducer, preloadedState)执行
这里写图片描述
执行 var store = createStore.apply(undefined, args);代码,创建store,store里有5个关键变量

  var currentReducer = reducer;
  var currentState = preloadedState;
  var currentListeners = [];
  var nextListeners = currentListeners;
  var isDispatching = false;

执行dispatch({ type: ActionTypes.INIT });初始化state

最终createStore返回包含4个函数

dispatch: dispatch,
subscribe: subscribe,
getState: getState,
replaceReducer: replaceReducer

下面代码很关键

function () {
   for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
      args[_key2] = arguments[_key2];
    }
    //创建store
    var store = createStore.apply(undefined, args);
    /*
    创建一个临时函数,而这个临时函数就是redux-thunk中间件最终返回的函数
    return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }

        return next(action);
      };

    dispatch:function dispatch() {
        return _dispatch.apply(undefined, arguments);
      }
   */
    var _dispatch = function dispatch() {
      throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
    };

    //是中间件函数接收的对象
    var middlewareAPI = {
      getState: store.getState,
      dispatch: function dispatch() {
        return _dispatch.apply(undefined, arguments);
      }
    };
    var chain = middlewares.map(function (middleware) {
      return middleware(middlewareAPI);
    });
    _dispatch = compose.apply(undefined, chain)(store.dispatch);

    return _extends({}, store, {
      dispatch: _dispatch
    });
  };
};

react、redux、react-redux结合起来的流程

  1. 首先关注redux的combineReducers(reducers),它返回一个函数,函数体内保留有我们传入的所有在reducers函数,当有dispatch(action)时,就会执行这个函数,根据key(我说的key不仅仅是对象key,同时它还表示模块的key)遍历每个reducer函数。计算数据并返回一个新的state,保存在store里的currentState变量里。
  2. 创建store,执行createStore函数,函数里应用了中间件(如果没有使用中间件,这部分内容可以pass掉),创建store,以redux-thunk为例,它会改造store.dispatch()函数。
    改造之后的dispatch函数是这样的
function (action) {
     if (typeof action === 'function') {
       return action(dispatch, getState, extraArgument);
     }

     return next(action);
};

其实代码里上面的dispatch函数是如下的,_dispatch就是上面的函数

function dispatch() {
 return _dispatch.apply(undefined, arguments);
}

由于使用了fun.apply(undefined, arguments)调用方式,我们可以把dispatch函数和_dispatch函数当成同一个函数。

  1. react-redux框架用于连接react组件和redux数据处理,比较重要的函数是connect
connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {})

单从名字上我们便可以理解出来意思
mapStateToProps:用于当state数据映射成组件的props上,组件内部可以直接使用this.props.key的方式取数据。
mapDispatchToProps:当dispatch映射到props上,可以使用this.props.dispatch方式使到dispatch函数,比较经常出现的函数形势如下

dispatch=>bindActionCreators(userAction,dispatch)

dispatch函数就是中间件改造之后的store.dispatch函数。
4. 上面的bindActionCreators(userAction,dispatch)最终返回的是同名的key-()=>dispatch(actionCreator.apply(this, arguments))形势的函数,它封装了我们传入的dispatchactionCreator形成一个新函数。每次我们调用action生产函数this.props.actionCreator,其实执行的就是这个新函数。函数先将参数传入actionCreator函数并执行,返回的结果再作为入参数执行dispatch函数。所以我们的actionCreator函数有2种写法
1.第一种,接收dispatch函数,使用dispatch(actioin对象)的形势

 testActionCreator:(参数1,参数2...)=>dispatch=>{
 dispatch(actioin对象)
 }
  1. 第二种,不接收dispatch,返回一个action,这个action会最张调用store.dispatch函数
 testActionCreator:(参数1,参数2...)=>{
     return actioin对象;
 }
  1. 如果理解了上面的内容。我们只需要记住actionCreator里接收的dispatch函数是被中间件修改过之后的函数或者没使用中间件原始的store.dispatch函数,内部怎么调用我们不管。actionCreator接收参数,我们可以按上面第一种/第二种的写法,派发action。

项目太忙,暂时不更新,以后会继续更新此贴

猜你喜欢

转载自blog.csdn.net/achenyuan/article/details/81435843