Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mirror 源码解读 #3

Open
zhangyu921 opened this issue Oct 12, 2017 · 0 comments
Open

Mirror 源码解读 #3

zhangyu921 opened this issue Oct 12, 2017 · 0 comments
Labels

Comments

@zhangyu921
Copy link
Owner

zhangyu921 commented Oct 12, 2017

无图,纯分析,慎入

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中找到这个函数并执行

好的,结束~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant