@(Redux)[|用法|源码]javascript
Redux 由Dan Abramov在2015年建立的科技术语。是受2014年Facebook的Flux架构以及函数式编程语言Elm启发。很快,Redux因其简单易学体积小短期内成为最热门的前端架构。前端
@[三大原则]java
state
被储存在一棵object tree
中,而且这个object tree
只存在于惟一一个store
中。全部数据会经过store.getState()
方法调用获取.State
只读原则,数据变动会经过store,dispatch(action)
方法.Reducer
只是一些纯函数1,它接收先前的state
和action
,并返回新的state
.[TOC]git
//curry example const A = (a) => { return (b) => { return a + b } }
通俗的来说,能够用一句话归纳柯里化函数:返回函数的函数.
优势: 避免了给一个函数传入大量的参数,将参数的代入分离开,更有利于调试。下降耦合度和代码冗余,便于复用.github
举个例子编程
let init = (...args) => args.reduce((ele1, ele2) => ele1 + ele2, 0) let step2 = (val) => val + 2 let step3 = (val) => val + 3 let step4 = (val) => val + 4 let steps = [step4, step3, step2, init] let composeFunc = compose(...steps) console.log(composeFunc(1, 2, 3)) // 1+2+3+2+3+4 = 15
接下来看下FP思想的compose的源码redux
const compose = function (...args) { let length = args.length let count = length - 1 let result let this_ = this // 递归 return function f1(...arg1) { result = args[count].apply(this, arg1) if (count <= 0) { count = length - 1 return result } count-- return f1.call(null, result) } }
通俗的讲: 从右到左执行函数,最右函数以arguments为参数,其他函数以上个函数结果为入参数执行。api
优势: 经过这样函数之间的组合,能够大大增长可读性,效果远大于嵌套一大堆的函数调用,而且咱们能够随意更改函数的调用顺序数组
随着整个项目愈来愈大,state
状态树也会愈来愈庞大,state的层级也会愈来愈深,因为redux
只维护惟一的state
,当某个action.type
所对应的须要修改state.a.b.c.d.e.f
时,个人函数写起来就很是复杂,我必须在这个函数的头部验证state
对象有没有那个属性。这是让开发者很是头疼的一件事。因而有了CombineReducers
。咱们除去源码校验函数部分,从最终返回的大的Reducers
来看。安全
Note:
- FinalReducers : 经过
=== 'function'
校验后的Reducers
.- FinalReducerKeys :
FinalReducers
的全部key
(与入参
Object
的key
区别:过滤了value
不为function
的值)
// 返回一个function。该方法接收state和action做为参数 return function combination(state = {}, action) { var hasChanged = false var nextState = {} // 遍历全部的key和reducer,分别将reducer对应的key所表明的state,代入到reducer中进行函数调用 for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] // CombineReducers入参Object中的Value为reducer function,从这能够看出reducer function的name就是返回给store中的state的key。 var previousStateForKey = state[key] // debugger var nextStateForKey = reducer(previousStateForKey, action) // 若是reducer返回undefined则抛出错误 if (typeof nextStateForKey === 'undefined') { var errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 将reducer返回的值填入nextState nextState[key] = nextStateForKey // 若是任一state有更新则hasChanged为true hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state }
combineReducers
实现方法很简单,它遍历传入的reducers
,返回一个新的reducer
.该函数根据State
的key
去执行相应的子Reducer
,并将返回结果合并成一个大的State
对象。
createStore
主要用于Store
的生成,咱们先整理看下createStore
具体作了哪些事儿。(这里咱们看简化版代码)
const createStore = (reducer, initialState) => { // initialState通常设置为null,或者由服务端给默认值。 // internal variables const store = {}; store.state = initialState; store.listeners = []; // api-subscribe store.subscribe = (listener) => { store.listeners.push(listener); }; // api-dispatch store.dispatch = (action) => { store.state = reducer(store.state, action); store.listeners.forEach(listener => listener()); }; // api-getState store.getState = () => store.state; return store; }
源码角度,一大堆类型判断先忽略,能够看到声明了一系列函数,而后执行了dispatch
方法,最后暴露了dispatch
、subscribe
……几个方法。这里dispatch
了一个init Action
是为了生成初始的State
树。
首先,说ThunkMiddleware
以前,也许有人会问,到底middleware
有什么用?
这就要从action
提及。在redux
里,action
仅仅是携带了数据的普通js
对象。action creator
返回的值是这个action
类型的对象。而后经过store.dispatch()
进行分发……
action ---> dispatcher ---> reducers
同步的状况下一切都很完美……
若是遇到异步状况,好比点击一个按钮,但愿1秒以后显示。咱们可能这么写:
function (dispatch) { setTimeout(function () { dispatch({ type: 'show' }) }, 1000) }
这会报错,返回的不是一个action
,而是一个function
。这个返回值没法被reducer
识别。
你们可能会想到,这时候须要在action
和reducer
之间架起一座桥梁……
固然这座桥梁就是middleware
。接下来咱们先看看最简单,最精髓的ThunkMiddleware
的源码
const thunkMiddleware = ({ dispatch, getState }) => { return next => action => { typeof action === 'function' ? action(dispatch, getState) : next(action) } }
很是之精髓。。。咱们先记住上述代码,引出下面的ApplyMiddleware
介绍applyMiddleware
以前咱们先看下项目中store
的使用方法以下:
let step = [ReduxThunk, middleware, ReduxLogger] let store = applyMiddleware(...step)(createStore)(reducer) return store
经过使用方法能够看到有3处柯里化函数的调用,applyMiddleware
函数Redux
最精髓的地方,成功的让Redux
有了极大的可拓展空间,在action
传递的过程当中带来无数的“反作用”,虽然这每每也是麻烦所在。 这个middleware
的洋葱模型思想是从koa
的中间件拿过来的,用图来表示最直观。
咱们来看源码:
const applyMiddleware = (...middlewares) => { return (createStore) => (reducer, initialState, enhancer) => { var store = createStore(reducer, initialState, enhancer) var dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 每一个 middleware 都以 middlewareAPI 做为参数进行注入,返回一个新的链。 // 此时的返回值至关于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next做为其参数 chain = middlewares.map(middleware => middleware(middlewareAPI)) // 并将链代入进 compose 组成一个函数的调用链 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
applyMiddleware
函数第一次调用的时候,返回一个以createStore
为参数的匿名函数,这个函数返回另外一个以reducer
,initialState
,enhancer
为参数的匿名函数.咱们在使用方法中,分别能够看到传入的值。
结合一个简单的实例来理解中间件以及洋葱模型
// 传入middlewareA const middlewareA = ({ dispatch, getState }) => { return next => action => { console.warn('A middleware start') next(action) console.warn('A middleware end') } } // 传入多个middlewareB const middlewareB = ({ dispatch, getState }) => { return next => action => { console.warn('B middleware start') next(action) console.warn('B middleware end') } } // 传入多个middlewareC const middlewareC = ({ dispatch, getState }) => { return next => action => { console.warn('C middleware start') next(action) console.warn('C middleware end') } }
当咱们传入多个相似A,B,C的middleware
到applyMiddleware
后,调用
dispatch = compose(...chain)(store.dispatch)
结合场景而且执行compose
结果为:
dispatch = middlewareA(middlewareB(middlewareC(store.dispatch)))
从中咱们能够清晰的看到middleware
函数中的next
函数相互链接,这里体现了compose
FP编程思想中代码组合的强大做用。再结合洋葱模型的图片,不难理解是怎么样的一个工做流程。
最后咱们看结果,当咱们触发一个store.dispath
的时候进行分发。则会先进入middlewareA
而且打印A start
而后进入next
函数,也就是middlewareB
同时打印B start
,而后触发next
函数,这里的next
函数就是middlewareC
,而后打印C start
,以后才处理dispath
,处理完成后先打印C end
,而后B end
,最后A end
。完成总体流程。
Redux applyMiddleware
机制的核心在于,函数式编程(FP)
的compose
组合函数,需将全部的中间件串联起来。compose
对单参函数的使用,对每一个中间件采用currying
的设计。同时,利用闭包原理作到每一个中间件共享Store
。(middlewareAPI
的注入)Thank you for reading this record.