无图,纯分析,慎入
Mirror 是一个 react、redux 及 react-router 的封装。有几个显著的特点让开发变得更加简单:
- reducers 及 effects 的函数名即为用户要使用的 actions 中的方法名。只需要定义一个模块的 reduces 和 effects (如 app 模块下定义的
reducer : { add ( state ){ return state + 1 } }
),并在使用的过程中执行actions中对应模块的方法(actions.app.add(props))即可执行reducer,进而实现对 store 的修改。无需再定义 action type 及 actionCreator。 - 在 mirror 内部对 router 进行了封装,替开发者做了将router状态集成到redux内的工作,并暴露出router的接口,无须再在项目中引用react-router。
更详细的特点参考这个指南。
在实际开发中,不可避免会有大量的样板代码,选择细读 Mirror 源码,一方面是因为 Mirror 在简化 Redux
的流程上的惊艳,再也是为了学习如何对复杂的流程进行精简以方便开心的编写代码。
Mirror 的代码比较简单易懂,适合初学者掌握 redux 开发之后有一定实践后阅读。
代码
根据下面这个版本的 Mirror,写的比较枯燥同时打开对比来看会好些,另外还添加了一些注释可以作为参考:
https://github.com/zhangyu921/mirror/tree/master/src
index.js
唯一的目的暴露接口,这里用了export default {}
和export {}
两种形式,方便引用的时候可以使用import Mirror from 'mirrorx'
或者 import {connect} from 'mirrorx'
这两种引用方法,其中Mirror是一个对象,由 export default 暴露。
defaults.js
defaults.js 暴露 options 及 defaults 方法。Mirror.defaults() 可能是使用 Mirror 第一个要调用的方法。
options,Mirror 的全局配置对象,其中有默认配置,用户调用 defaults 方法对 option 改写。
defaults 方法首先对用户传来的配置信息(historyMode, middlewares, addEffect)进行验证,随后将配置写到options里。
render.js
render 函数是比较重要的环节,在 render.js 定义,不同于 react-dom 中的render,这个函数同时兼顾了store的创建和包裹 Provider 组件的操作,所以使用 Mirror 必须要使用 render方法渲染到 DOM。
第一次执行render时,会调用createStore生成store,后面会讲到。值得注意的是当再次render函数的时候,执行了replaceReducer,这个和createStore函数在store.js中定义。
store.js
store.js 提供store,createStore和replaceReducer方法。
createStore相对于 redux 的 createStore 区别多加了一个参数models,这是在models.js中定义的所有models的数组,之后会详细说,还部署了Redux devtools,可以在devtools里对store进行直观的操作。
replaceReducer 与 store.replaceReducer 操作逻辑相同。这里除了reducers和model注册的reducers,还有一个routing,即为react-router-redux的routerReducers。所以在routing不能再次被定义。
createStore与replaceReducer同时引用了一个方法 createReducer。这是因为Mirror里reducer定义在models里,在生成store或者调用store.replaceReducer时需要重新组合reducers。
model.js
model.js 提供了 Mirrorx 中最常用的方法 model,也暴露出models记录用户定义的model的集合,但它本身做的事情并不多。
model方法首先对用户传来的参数m进行了校验。validateModel中的函数filterReduces方法,确保了reducers里仅有function类型的属性。
第9行开始,总的来看是用 getReducer 方法获得了reducer,然后存到_model
再直接存到了models里。随后执行了addActions()。
我们细看getReducer做了什么事情,返回一个方法,这个方法接收state和action,返回reducers[action.type](state, action.data)
也就是执行这个reducers里的action.type
变量代表的方法。
到这里我们知道resolveReducers做的事情应该是返回了一个对象,每个键名代表action.type,值是接收state和action.data的函数。
actions.js
actions.js对外暴露了上面model.js用到的 resolveReducers、addActions方法,还有一个actions对象供用户调用。
resolveReducers 功能如上所述,根据modelName、SEP和当前键名组合新的键名,值为reducer方法。
addActions 方法用了两次遍历 分别将reducers和effects做了处理,对reducers的处理是将actionCreater返回的结果根据modelName和actionNave直接写到actions里。actionCreator方法接收两个name,返回一个方法,一眼看过去就明白这其实就是封装了 dispatch 方法,这个方法是在middleware中引入的,这个后面会说。
addActions在遍历effects的时候与reducers基本相同,区别是调用了options.addEffect和添加了一个标志位。猜测在调用effect的时候,会根据标志位进行不同的操作。需要注意的是options.addEffect与effects.js中的addEffects不同,option中的是执行过effects.js之后的结果。
可以看出actions.js中做的工作主要是将dispatch函数写到到actions对象里。
middleware.js
middleware.js 提供了 actions.js 中的dispatch函数,以及getState和createMiddleware方法。
在调用createMiddleware之前,dispatch和getState方法都相当于是warning方法,createMiddleware方法是在render的时候调用的createStore方法中 applyMiddleware 的时候执行的,换句话说必须使用Mirror自身的render方法。
createMiddleware顾名思义就是个中间件,中间件写法大家应该熟悉
store => next => action => next(action)
所以这里的MiddlewareAPI即为store,取出dispatch和getState赋值到外层,Mirror.actions所使用的dispatch才能正常使用。将这个操作放在中间件中,会使得每一次调用中间件都先对dispatch重新赋值。
createMiddleware也负责了对effects的接收调用。 也就是说在Mirror中,对Effect的调用相当于在中间件中看是否有Effect注册了,如果有就调用,然后把之前的结果舍弃。
随后依次调用hooks,这里也说明hooks是在中间件做处理。
effects.js
effects.js 提供了 effects 的集合,和一个方法addEffect方法。整体很简单,不在赘述。
hook.js
hook.js 提供一个hooks的集合和一个hook方法直接在 index.js 中被暴露出来。很显然hook方法用于注册钩子函数到hooks数组。返回一个函数,这个函数被执行之后,会将指定的钩子函数剔除。
router.js & routerMiddleware.js
这两个模块主要作用将 react-router集成到Mirror中。应该是集成react-router必要的步骤,在这里不作重点了。
值得注意 routerMiddleware 是第一个中间件,当接受到指定的action.type后操作跳转,短路后续操作。
总结
这时候对Mirror的工作流程就非常清晰了:
初始化:
Mirror.defaults定义配置
-> 各个模块定义的Mirror.model 组织reducers、actions
-> Mirror.render 生成store、包裹Provider、渲染DOM
调用actions:
执行某个action
-> 相当于执行dispatch
-> 对应Store中reducer执行,相当于用户定义的reducer执行
调用effects:
同样执行某个action
-> 但在reducers中没有这个action type( render.js Line:30 参数model不包括effects )
-> 中间件拦截之后,在effects中找到这个函数并执行
好的,结束~