摘要:用法源码由在年创建的科技术语。我们除去源码校验函数部分,从最终返回的大的来看。这个返回值无法被识别。洋葱模型我们来看源码源码每个都以作为参数进行注入,返回一个新的链。改变原始组数,是一种副作用。
@(Redux)[|用法|源码]
Redux 由Dan Abramov在2015年创建的科技术语。是受2014年Facebook的Flux架构以及函数式编程语言Elm启发。很快,Redux因其简单易学体积小短时间内成为最热门的前端架构。
@[三大原则]
单一数据源 - 整个应用的state被储存在一棵object tree中,并且这个object tree只存在于唯一一个store中。所有数据会通过store.getState()方法调用获取.
State‘只读’ - 根据State只读原则,数据变更会通过store,dispatch(action)方法.
使用纯函数修改 -Reducer只是一些纯函数1,它接收先前的state和action,并返回新的state.
[TOC]
准备阶段 柯里化函数(curry)//curry example const A = (a) => { return (b) => { return a + b } }
通俗的来讲,可以用一句话概括柯里化函数:返回函数的函数.
优点: 避免了给一个函数传入大量的参数,将参数的代入分离开,更有利于调试。降低耦合度和代码冗余,便于复用.
举个例子
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的源码
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为参数,其余函数以上个函数结果为入参数执行。
优点: 通过这样函数之间的组合,可以大大增加可读性,效果远大于嵌套一大堆的函数调用,并且我们可以随意更改函数的调用顺序
CombineReducers 作用随着整个项目越来越大,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 作用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 作用首先,说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 作用介绍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的注入)
Feedback & Bug Reportgithub: @同性交友网站
Thank you for reading this record.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/94974.html
摘要:大多的初学者都会使用中间件来处理异步请求,其理解简单使用方便具体使用可参考官方文档。源码的源码非常简洁,出去空格一共只有行,这行中如果不算上则只有行。官方文档中的一节讲解的非常好,也确实帮我理解了中间件的工作原理,非常推荐阅读。 总觉得文章也应该是有生命力的,欢迎关注我的Github上的博客,这里的文章会依据我本人的见识,逐步更新。 大多redux的初学者都会使用redux-thunk...
摘要:原文地址数据流通过这张流程图,我们可以更好的理解和直接数据如何流通,关系如何映射。函数只是一个纯函数,它接收应用程序的当前状态以及发生的,然后返回修改后的新状态或者有人称之为归并后的状态。的更新意味着更新。 原文地址:https://github.com/YutHelloWo... showImg(https://segmentfault.com/img/bVRQRK?w=1205&h...
阅读 1870·2021-11-25 09:43
阅读 3159·2021-11-15 11:38
阅读 2707·2019-08-30 13:04
阅读 482·2019-08-29 11:07
阅读 1491·2019-08-26 18:37
阅读 2695·2019-08-26 14:07
阅读 580·2019-08-26 13:52
阅读 2276·2019-08-26 12:09