redux-thunk之谜

如果你在用 redux 的话,那你估计也会用到 redux-thunk 这个库。区别于作者的说法,以下是日了狗的想法。 thunk 求值 日了狗说:“你可能得先了解下 thunk 。” 编程语言早期,计算机学家在研究如何编译时,对“求值策略”产生了争执。举个栗子: const x = 1; function foo(num) { return num * 2; } foo(x + 3); x + 3 该在何时计算?引发了两种意见: 传值调用,在进入 foo 之间计算,等同于 foo(4) 传名调用,只在用到时求值,等同于(x + 3) * 2 两种计算方式各有利弊,传值调用相对简单,但有可能根本没用到,比如: function bar(a, b) { return b + 2; } bar(1 + 2 * (3 - x) / x -4, x); 定义的 bar 函数体内没有用到 a ,根本无需计算,传值调用可能会造成性能损失。所以有些计算机学家更倾向于传名调用 thunk 求值就是基于传名调用的编译器实现。 const a = 1; const thunk = function thunk(x) { return x + 3; } function foo(thunk) { return thunk() * 2; } 在 JavaScript 语言中,thunk 意义略有不同,可以看做是种方式。来看以下两种场景: // Round 1: 普通写法 const sum = x => x + 2; const division = x => x / 5; sum(division(1)); // 2.2 // Round 2: 带 thunk 方式 const sum = next => x => next(x + 2); const division = next => x => next(x / 5); const func = x => x; sum(division(func))(1); // 0.6 两种场景,函数调用顺序一致,执行顺序相反。Round 2 中,除法没有立即求值,而是等乘法真正调用之后才求值。next 入参在这里就充当于一种 thunk 的作用 日了狗说:“好的。我们现在开始回到 redux-thunk 。” 为什么要用 当你在 redux 中触发 state 变化时,你肯定会这样 dispatch(action)。处理同步代码当然没问题,但是异步代码呢?比如说异步请求,emmmmm...跟着官方的代码走是这样的: function success(data) { return { type: 'SUCCESS', data, }; } function asyncFunc(payload) { return dispatch => { fetch(url, payload) .then(res => res.json()) .then(data => dispatch(success(data))) }; } dispatch(asyncFunc({})); 如果你没用 redux-thunk 的话,你应该会收到一条错误提示。这是因为 redux 里做了对 action 的校验: // 必须要是个包含 type 属性的纯对象 // 而 asyncFunc 返回的是个 Promise if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } 聪明的你会想到另一种解决办法。执行 Promise,dispatch 对象: // store.js const store = createStore(); // action.js function success(data) { return { type: 'SUCCESS', data, }; } // index.js fetch(url, data) .then() .then(res => /* import store from store.js */ store.dispatch(success(res))); 福兮祸所依,这样子的写法有几个问题: 写法不规范。createAction 不统一 保持对 store 的引用。污染全局、修改此对象都可能造成影响 重复代码。同一个请求多处写 日了狗说:“ok,现在我们来看看 redux-thunk 做了什么事。” 做了什么事 直接看源码。只有区区不到 15 行的代码: // redux-thunk 功能代码 const thunk = ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState); } return next(action); } 光看这里代码一脸懵。可能大家一直在纠结 next 是什么,action 又是什么。那不妨多纠结一下,看看 redux 的中间件怎么应用的: // 核心代码 function applyMiddleware(...middlewares) { const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) } 主要做了两件事: 将 dispatch 注入中间件 将所有的中间件 compose 成一个 dispatch redux 中的 compose 函数很有意思,将多个函数整合成一个,按顺序同步执行,上一个函数的返回值为下一个函数的入参。详情可看源码 Tip: 如果看源码不容易理解的话,可以尝试看下测试用例 了解了 applyMiddleware 后,再应用 redux-thunk,你就会发现原本的 dispatch 函数被改变了。在 action 触发之间会先执行一遍 redux-thunk 功能代码,判断传入的 action 是否为函数类型,如果是,则注入 dispatch,执行此函数 简而言之,redux-thunk 直接让 dispatch 跳过了 action 类型验证,然后就没啥了。。。 最后,日了狗觉得事情并不简单。

Markdown - 语法


JavaScript

上一篇:钢铁是怎样炼成的

下一篇:打酱油

Ctrl + Enter