Mickey 是一款基于 react、redux、redux-saga 和 react-router 的轻量前端框架,其大部分思路借鉴了 dva,提供了更方便的 model 设计思路和更简单的 actions 管理方案。
为什么
基于 redux 的应用避免不了大量的样板代码,还要维护大量的 action-type 常量字符串,这些都是低效和重复的劳动。dva 基于 elm 概念,通过 reducers
, effects
和 subscriptions
来组织 model,在减少样本代码层面前进了一大步:
|
|
看一个更接近实际的例子:
|
|
仔细看上面代码,对一个异步 action 处理通常会经历以下几步:
- 在
effects
中设计异步 action 处理方法:*query
- 在
reducers
中设计对应的同步 action 处理方法:query
,这里我们将 UI 状态置为 loading - 异步接口调用成功后通常会分成功和失败两种情况分别触发
querySuccess
和queryFailed
两个同步的 action
实际项目中 model 可能会更复杂 ,需要在 model 的 effects
和 reducers
两个大结构中跳转编辑才能完成对一个异步 action 的处理,也就是说,我们需要先在 effects
完成 *query()
的逻辑,然后在 reducers
中完成 query()
、querySuccess()
和 queryFailed()
三个同步 reducer。这样的跳转使编写代码、阅读代码和排查问题都非常不便。
就近原则
我们都知道,相同逻辑或者相关的代码放在一起是模块化思路之一。同理,对于一个异步 action 的所有处理属于强相关代码,在 Mickey 中可以这样来实现上面的 model:
|
|
对上面 query
的结构有几点说明:
- 包含不超过 1 个异步处理方法,方法名随意
- 可以包含任意个同步处理处理方法,
prepare
这个方法名固定 dispatch({type: 'users/query'})
时,将同时触发*effect
和prepare
,所以这两个方法需要在上面的结构中至少出现一个- 除
effect
和prepare
其他两个方法success
和failed
可以统称为回调方法,回调方法的方法名和数量都随意
不修改原生API
dva 对 saga 的 put
方法和 store 的 dispatch
方法做了重新封装,封装的思路是自动判断和添加 namespace
,如上面示例中的 put({type: 'querySuccess'})
。
如果没有这层封装会不会更好呢?一方面不会给开发者带去理解上的困难,另一方面也保证的原生 API 的纯净。但是,如果没有这层封装每次在 model 内部调用 put
或 dispatch
就非常麻烦,必须指定完整的命名空间。
在上一节中提到,在 model 中除了 *effect
和 prepare
之外的方法我们统称为回调,这些回调方法通常会在异步请求完成之后之后通过 put
一个 action 来触发,既然这样我们何不直接将这些回调方法的名称作为 *effect
的参数,在 *effect
内部就可以直接调用:
|
|
通过在 *effect
方法中注入回调函数,不仅不需要修改原生 dispatch
和 put
的行为,同时不再需要关心和维护 action-type 常量字符串。
在 Mickey 中 *effect
方法的完整签名:
|
|
同步 action 处理方法签名:
|
|
对比原生 reducer 方法:
区别在于方法的第二个参数,正是由于我们不再需要关心和维护 action-type 字符串,所以在 mickey 中直接使用了 payload
作为第二个参数。
完整示例
看下面计数器的例子:
|
|
更多示例
- Counter:简单的计数器
- Counter-Persist:搭配 redux-persist 使用
- Counter-Immutable:搭配 ImmutableJS 使用
- Counter-Persist-Immutable:搭配 redux-persist 和 ImmutableJS 使用
- Counter-Undo:搭配 redux-undo 使用
- Simple-Router:基于 react-router@4.x
- mickey-todo (demo): 简单的 TODO 应用
- mickey-vstar (demo):查询指定 Github 账号中被加星项目并按加星数排序