Web前端最全Redux源码解析系列 (四)-- 揭秘applyMiddleware工作原理(1),腾讯前端hr面

Vue 编码基础

2.1.1. 组件规范

2.1.2. 模板中使用简单的表达式

2.1.3 指令都使用缩写形式

2.1.4 标签顺序保持一致

2.1.5 必须为 v-for 设置键值 key

2.1.6 v-show 与 v-if 选择

2.1.7 script 标签内部结构顺序

2.1.8 Vue Router 规范

Vue 项目目录规范

2.2.1 基础

2.2.2 使用 Vue-cli 脚手架

2.2.3 目录说明

2.2.4注释说明

2.2.5 其他

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

就相当于

store.dispatch = function Logger1(action) {

console.log(‘Logger1 dispatching’, action)

let result = next(action)

console.log(‘Logger1 next state’, store.getState())

return result

}

注意,这里的next是store之前的dispatch。

也就是说

这个store现在的dispatch方法为

store.dispatch = function Logger1(action) {

console.log(‘Logger1 dispatching’, action)

let result = next(action)

console.log(‘Logger1 next state’, store.getState())

return result

}

然后再遍历到第二个logger

store.dispatch = function Logger2(action) {

console.log(‘Logger2 dispatching’, action)

let result = next(action)

console.log(‘Logger2 next state’, store.getState())

return result

}

注意,到这里的时候,next是等于之前的那个store.dispatch,也就是说这里的next是上面的Logger2

next = function Logger1(action) {

console.log(‘Logger1 dispatching’, action)

let result = next(action)

console.log(‘Logger1 next state’, store.getState())

return result

}

所以现在这个箭头指向的store的dispatch是什么呢?

没错,就是现在这个样子

store.dispatch = function Logger2(action) {

console.log(‘Logger2 dispatching’, action)

let result = function Logger1(action) {

console.log(‘Logger1 dispatching’, action)

let result = next(action)

console.log(‘Logger1 next state’, store.getState())

return result

}

console.log(‘Logger2 next state’, store.getState())

return result

}

看,实际上,现在的store的dispatch是被logger1包装起来,然后再被logger2包装起来。像极了一个礼物呗几个盒子包起来了。

原理实际上就是不断地更新store.dispatch

接下来,我们就可以这样用

applyMiddleware(store, [logger, crashReporter])

刚刚说过,在applyMiddle里必须要给store.dispatch赋值,否则下一个middleware就拿不到最新的dispatch。

但是有别的方式,那就是在middleware里不直接从store.dipatch里读取next函数,而是将next作为一个参数传入,在applyMiddleware里用的时候把这个参数传下去。

step 5


function logger(store) {

return function wrapDispatchToAddLogging(next) {

return function dispatchAndLog(action) {

console.log(‘dispatching’, action)

let result = next(action)

console.log(‘next state’, store.getState())

return result

}

}

}

接着,在applyMiddleware里有可以不用立刻对store.dispatch赋值啦,可以直接赋值给一个变量dispatch,作为middleware的参数传递下去,这样就能链式的增强dispatch的功能啦~

function applyMiddleware(store, middlewares) {

middlewares = middlewares.slice();

middlewares.reverse();

let dispatch = store.dispatch;

middlewares.forEach(middleware => {

dispatch = middleware(store)(dispatch)

})

return Object.assign({}, store, {dispatch})

}

用es6的柯西化写法,可以写成下面的形式,其实这个next我个人觉得叫previous更为合适,因为它指代的是上一个store.dispatch函数。

const logger = store => next => action => {

console.log(‘dispatching’, action)

let result = next(action)

console.log(‘next state’, store.getState())

return result

}

提供报错信息的中间件

const crashReporter = store => next => action => {

try {

return next(action)

} catch (err) {

console.error(‘Caught an exception!’, err)

Raven.captureException(err, {

extra: {

action,

state: store.getState()

}

})

throw err

}

}

由此我们可以看到所以middleware要传入的参数就是三个,store,next,action。

接下来,看一个实例,redux-thunk 的源码,我们知道,它用于异步API,因为异步 API action creator返回的是一个funciton,而不是一个对象,所以redux-thunk做的事情 其实很简单,就是看第三个参数action是否是function,是的话,就执行它,如果不是, 就按照原来那样执行next(action)

function createThunkMiddleware(extraArgument) {

return (store) => next => action => {

if(typeof action === ‘function’) {

return action(dispatch, getState, extraArgument)

}

return next(action)

}

}

const thunk = createThunkMiddleware();

thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

step 6


下面终于可以看看applyMiddleware的样子啦

/**

* Composes single-argument functions from right to left. The rightmost

* function can take multiple arguments as it provides the signature for

* the resulting composite function.

* @param {…Function} funcs The functions to compose.

* @returns {Function} A function obtained by composing the argument functions

* from right to left. For example, compose(f, g, h) is identical to doing

* (…args) => f(g(h(…args))).

*/

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)))

}

//可以看出compose做的事情就是上一个函数的返回结果作为下一个函数的参数传入。

/**

* Creates a store enhancer that applies middleware to the dispatch method

* of the Redux store. This is handy for a variety of tasks, such as expressing

* asynchronous actions in a concise manner, or logging every action payload.

* See redux-thunk package as an example of the Redux middleware.

* Because middleware is potentially asynchronous, this should be the first

* store enhancer in the composition chain.

* Note that each middleware will be given the dispatch and getState functions

* as named arguments.

* @param {…Function} middlewares The middleware chain to be applied.

* @returns {Function} A store enhancer applying the middleware.

*/

export default function applyMiddleware(…middlewares) {

return (createStore) => (…args) => {

// 之后就在这里先建立一个store

const store = createStore(…args)

let dispatch = store.dispatch

let chain = []

// 将getState 跟dispatch函数暴露出去

const middlewareAPI = {

getState: store.getState,

dispatch: (…args) => dispatch(…args)

}

//这边返回chain的一个数组,里面装的是wrapDispatchToAddLogging那一层,相当于先给

//middle剥了一层皮,也就是说

// 接下来只需要开始传入dispatch就行

chain = middlewares.map(middleware => middleware(middlewareAPI))

dispatch = compose(…chain)(store.dispatch)

// 翻译过来就是

// wrapCrashReport(wrapDispatchToAddLogging(store.dispatch))

// 此时返回了上一个dispatch的函数作为wrapCrashReport的next参数

// wrapCrashReport(dispatchAndLog)

// 最后返回最终的dipatch

return {

…store,

dispatch

}

}

}

// 第二行有个比较巧妙地地方,就是传入createStore跟…args,为什么要这么做呢

// 可以先看看我们在用applyMiddleware的时候是怎么用的

createStore(

rootReducers,    //reducer

preloadedState,

applyMiddleware( //enhancer

thunkMiddleware,

createLogger

)

)

createStore的部分源码是这样的

export default function createStore(reducer, preloadedState, enhancer) {

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.’)

}

// 在这里可以看到,如果第三个参数是函数也是我们的applyMiddleware,那就会直接返回这个,对于我们的applyMiddleware来说,就是直接把creatStore的权利放在了自己身上啊~

return enhancer(createStore)(reducer, preloadedState)

}

if (typeof reducer !== ‘function’) {

throw new Error(‘Expected the reducer to be a function.’)

}

}

HTTP

  • HTTP 报文结构是怎样的?

  • HTTP有哪些请求方法?

  • GET 和 POST 有什么区别?

  • 如何理解 URI?

  • 如何理解 HTTP 状态码?

  • 简要概括一下 HTTP 的特点?HTTP 有哪些缺点?

  • 对 Accept 系列字段了解多少?

  • 对于定长和不定长的数据,HTTP 是怎么传输的?

  • HTTP 如何处理大文件的传输?

  • HTTP 中如何处理表单数据的提交?

  • HTTP1.1 如何解决 HTTP 的队头阻塞问题?

  • 对 Cookie 了解多少?

  • 如何理解 HTTP 代理?

  • 如何理解 HTTP 缓存及缓存代理?

  • 为什么产生代理缓存?

  • 源服务器的缓存控制

  • 客户端的缓存控制

  • 什么是跨域?浏览器如何拦截响应?如何解决?

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值