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,依此类推)。
arguments
和 parameters
经常被混为一谈,为了这个教程我们还是做一个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)
由于绑定dispatch
与actionCreators
,从结果论上来讲两者关系就是先执行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
函数接收actionCreators
和store.dispatch
2个参数。返回一个对象,包含同名的key-value,value是一个关联dispatch
和actionCreators
两个参数的函数。通过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,然后执行reducers
和listeners
函数集,返回最新的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);
};
}
}
createStore
里enhancer(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结合起来的流程
- 首先关注redux的
combineReducers(reducers)
,它返回一个函数,函数体内保留有我们传入的所有在reducers
函数,当有dispatch(action)
时,就会执行这个函数,根据key(我说的key不仅仅是对象key,同时它还表示模块的key
)遍历每个reducer
函数。计算数据并返回一个新的state
,保存在store里的currentState
变量里。 - 创建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函数当成同一个函数。
- 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))形势的函数,它封装了我们传入的dispatch
和actionCreator
形成一个新函数。每次我们调用action生产函数this.props.actionCreator,其实执行的就是这个新函数。函数先将参数传入actionCreator函数并执行,返回的结果再作为入参数执行dispatch函数。所以我们的actionCreator函数有2种写法
1.第一种,接收dispatch函数,使用dispatch(actioin对象)的形势
testActionCreator:(参数1,参数2...)=>dispatch=>{
dispatch(actioin对象)
}
- 第二种,不接收dispatch,返回一个action,这个action会最张调用store.dispatch函数
testActionCreator:(参数1,参数2...)=>{
return actioin对象;
}
- 如果理解了上面的内容。我们只需要记住actionCreator里接收的dispatch函数是被中间件修改过之后的函数或者没使用中间件原始的store.dispatch函数,内部怎么调用我们不管。actionCreator接收参数,我们可以按上面第一种/第二种的写法,派发action。
项目太忙,暂时不更新,以后会继续更新此贴