redux深入理解之中间件(middleware)
本文代码请看本人github,https://github.com/Rynxiao/redux-middleware
关于redux运用,请看之前一篇文章http://blog.csdn.net/yuzhongzi81/article/details/51880577
理解reduce函数
reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。
arr.reduce([callback, initialValue])
关于reduce的用法,这里不再做多述,可以去这里查看
看如下例子:
let arr = [1, 2, 3, 4, 5];
// 10代表初始值,p代表每一次的累加值,在第一次为10
// 如果不存在初始值,那么p第一次值为1
// 此时累加的结果为15
let sum = arr.reduce((p, c) => p + c, 10); // 25
// 转成es5的写法即为:
var sum = arr.reduce(function(p, c) {
console.log(p);
return p + c;
}, 10);
下面我们再来看一个reduce的高级扩展。现在有这么一个数据结构,如下:
let school = {
name: 'Hope middle school',
created: '2001',
classes: [
{
name: '三年二班',
teachers: [
{ name: '张二蛋', age: 26, sex: '男', actor: '班主任' },
{ name: '王小妞', age: 23, sex: '女', actor: '英语老师' }
]
},
{
name: '明星班',
teachers: [
{ name: '欧阳娜娜', age: 29, sex: '女', actor: '班主任' },
{ name: '李易峰', age: 28, sex: '男', actor: '体育老师' },
{ name: '杨幂', age: 111, sex: '女', actor: '艺术老师' }
]
}
]
};
比如我想取到这个学校的第一个班级的第一个老师的名字,可能你会这样写:
school.classes[0].teachers[0].name
这样不就行了么?so easy!是哦,这样写"毫无问题",这个毫无问题的前提是你已经知道了这个值确实存在,那么如果你不知道呢?或许你要这么写:
school.classes &&
school.classes[0] &&
school.classes[0].teachers &&
school.classes[0].teachers[0] &&
school.classes[0].teachers[0].name
我去,好大一坨,不过要在深层的对象中取值的场景在工作中真真实实存在呀?怎么办?逛知乎逛到一个大神的解决方案,如下:
const get = (p, o) => p.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), o);
// call
get('classes', 0, 'teachers', 0, 'name', school); // 张二蛋
是不是很简单,用reduce这个方法优雅地解决了这个问题。
理解redux的compose函数
讲了这么久的reduce,这不是讲redux么?这就尴尬了,下面我们就来看看为什么要讲这个reduce函数。去github上找到redux源码,会看到一个compose.js文件,带上注释共22行,其中就用到了reduce这个函数,那么这个函数是用来做啥的?可以看一看:
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)))
}
初步看上去貌似就是函数的嵌套调用。我们去搜一下,看哪个地方会用到这个函数,在源码中找一下,发现在applyMiddleware.js中发现了这样的调用:
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
}
}
}
看到熟悉的东西了么?applyMiddleware哟,我们在写中间件必须要用的函数。我们来看一下一个简单的middleware是怎样写的?比如我要写一个loggerMiddleware,那么就像这样:
const logger = store => next => action => {
console.log('action', action);
let result = next(action);
console.log('logger after atate', store.getState());
return result;
}
当我们创建了一个store的时候,我们是这样调用的:
let middlewares = [loggerMiddleware, thunkMiddleware, ...others];
let store = applyMiddleware(middlewares)(createStore)(reducer, initialState);
那么传给compose的funcs实际上就是包含这样的函数的一个数组:
function(next) {
return function(action) {
return next(action);
}
}
当把这样的一个数组传给compose会发生什么样的化学反应呢?稍微看一下应该不难看出,最终会返回一个函数,这个函数是通过了层层middleware的加工,最终的形态仍如上面的这个样子。注意,此时的next(action)并未执行,当执行了
compose(...chain)(store.dispatch)
之后,返回的样子是这样的:
function(action) {
return next(action);
}
各位看官们,看出了一点点什么东西了么?好像createStore中的dispatch呀,没错,这其实也是一个dispatch,只是这个dispatch正一触即发,再等待一个机会。我们有这么一个数量加1的action,类似这样的:
export function addCount() {
return {
type : ADD_COUNT
}
}
// 下面我们来触发一下
dispatch(addCount());
没错,此时的dispatch执行啦,最外层的dispatch执行了会发生什么样的反应呢?看下面:
return next(action);
// 这个next就是dispatch函数,只不过这个dispatch函数在每次执行的时候,会保留
// 上一个middleware传递的dispatch函数的引用,因此会一直的传递下去,
// 直到最终的store.dispatch执行
那么我们去createStore中去看看dispatch函数的定义:
function dispatch(action) {
// ...
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// ...
return action
}
找到这一句
currentState = currentReducer(currentState, action);
当执行了这一步的时候,这一刻,原本传递过来的initialState值已经改变了,那么就会层层执行middleware之后的操作,还记得我们在middleware中这样写了么:
const logger = store => next => action => {
console.log('action', action);
let result = next(action);
console.log('logger after atate', store.getState());
return result;
}
这就是为什么我们会在next执行之后,会取到store中的state的原因。
异步的middlewares
异步的action写法上可能会和立即执行的action不一样,例如是这样的:
// 定义的非纯函数,提供异步请求支持
// 需要在sotre中使用thunkMiddleware
export function refresh() {
return dispatch => {
dispatch(refreshStart());
return fetch(`src/mock/fetch-data-mock.json`)
.then(response => response.json())
.then(json => {
setTimeout(() => {
dispatch(refreshSuccess(json && json.data.list));
}, 3000);
});
}
}
为什么要使用thunkMiddleware呢,我们去找一找thunkMiddleware中到底写了什么?
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;
短短14行代码,看这一句:
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
如果action的类型为function的话,那么就直接执行啦,实际上就是将一个异步的操作转化成了两个立即执行的action,只是需要在异步前和异步后分别发送状态。为什么要分解呢?如果不分解会是什么样的情况?还记得这一行代码吗?
currentReducer(currentState, action);
这里的reducer只接受纯函数,只接受纯函数,只接受纯函数,重要的事情说三遍。所以你传个非纯函数是个什么鬼?那不是直接走switch的default了么?所以得到的state依旧是之前的state,没有任何改变。
redux深入理解之中间件(middleware)的更多相关文章
- redux 中的 redux-thunk(中间件)
前言 空闲时间把redux中的redux-thunk中间件回顾下,因为以前没有写博客的习惯,都怪自己太年轻,好了 其实: redux的核心概念其实很简单:将需要修改的state都存入到sto ...
- 初探中间件(middleware)
初探中间件(middleware) 因为考虑到文章的长度, 所以 BaseHandler 的展开被推迟了. 在 BaseHandler 中隐藏着中间件的信息, 较常见的 SessionMiddlewa ...
- C# Owin 创建与测试自己的中间件Middleware(二)
本文纯属介绍怎么简单地创建自己的Owin.还没有理解owin概念的请看上一篇文章:http://www.cnblogs.com/alunchen/p/7049307.html 目录 1.创建项目 2. ...
- 跟我一起学.NetCore之中间件(Middleware)简介和解析请求管道构建
前言 中间件(Middleware)对于Asp.NetCore项目来说,不能说重要,而是不能缺少,因为Asp.NetCore的请求管道就是通过一系列的中间件组成的:在服务器接收到请求之后,请求会经过请 ...
- ASP.NET Core 开发-中间件(Middleware)
ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下一个组件的请求,并能之前和下一组 ...
- ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析
ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...
- laravel中间件-----------middleware
middleware中间件 是访问到达服务器后在被对应的路由处理之前所经过的一层过滤层,故称中间件. 中间件是存放在app\http\middleware中,需要定一个 handle 处理方法,在ha ...
- 二、中间件(middleware)
1. 中间件(middleware) Django中的中间件主要实现一些附加功能,在request被用户handler处理前,以及用户handler处理后生存的response进行处理.因此 ...
- 中间件(Middleware)
中间件(Middleware) ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下 ...
随机推荐
- hasOwnProperty的用法
判断一个属性倒底是在原型中,还是在实例中 hasOwnProperty() 来个栗子 function Person(){ }; Person.prototype.name = "hezhi ...
- Vue--props
组件实例的作用域是孤立的.这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据.要让子组件使用父组件的数据,我们需要通过子组件的 props 选项. 字面量语法 vs 动态语法 初学者常犯 ...
- spring基础系列--JavaConfig配置
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7171011.html 早以前,Spring推荐使用XML的方式来定义Bean及Bean之间 ...
- IDEA使用01 创建java项目、创建web项目
注意:本教程使用的开发环境是:(专业版) 1 创建javaSE项目 1.1 file -> new -> project 注意:如果是第一次使用,就需要配置 project SDK , ...
- linux DNS 问题
今天准备爬虫51job时候,发现ping不通外网了,ping 了一下IP,都是OK的,只是host不通. 呵呵,就一DNS问题,好的.第一步,开始检查配置文件 cat /etc/sysconfig/n ...
- 浅谈JVM与内存分配
一.程序内存分配 初始内存分配 当一个程序准备运行时,它首先向java虚拟机要内存,但是java虚拟机本身没有权限,它只能向操作系统申请内存,此时java虚拟机会拥有一个初始内存, 此处额外说明一下e ...
- http调用端HttpClient、DefaultHttpClient、CloseableHttpClient
1:说下httpClient接口和4.2.6版本后过时实例DefaultHttpClient,以及新的实例应用. 说到HTTP,脑子就冒出它的特性,基于TCP协议,简短点:说明是交互性的. 2:下面 ...
- PM2源码浅析
PM2工作原理 最近在玩一个游戏,<地平线:黎明时分>,最终Boss是一名叫黑底斯的人,所谓为人,也许不对,黑底斯是一段强大的毁灭进程,破坏了盖娅主进程,从而引发的整个大陆机械兽劣化故事. ...
- css 的包含块 、负外边距,字体,文本行高
一.包含块 目的:确定元素的位置和相对大小(%) 1.正常文档流元素和浮动元素 ---- 父元素的 content-box 2.绝对定位元素 ---- 父元素的 padding-box 3.固定定位元 ...
- 连续子序列最大和的O(NlogN)算法
对于一个数组,例如:int[] a = {4,-3,5,-2,-1,2,6,-2}找出一个连续子序列,对于任意的i和j,使得a[i]+a[i+1]+a[i+2]+.......+a[j]他的和是所有子 ...