全新的redux模块化框架,redux-arena

#1

毫无疑问,redux是一个成功而且可扩展性极强的状态管理器。它维护了一个描述整个应用状态的树形对象,应用的各个组件只需要关注相应的节点的变化就可以方便的进行相应的渲染工作。在最开始的开发中,我们充分的享受到了redux带来的好处,但是随着应用的代码量与功能复杂度的与日俱增,redux的管理和维护也变得越来越复杂。为了解决这些问题,我们开发了redux-arena。

介绍

github地址:点我

在线示例:点我

示例Demo使用了redux-devtools动态展示了redux的内部状态变化:

对于redux-arena,你可以简单的理解为它是redux的一个增强版,不同于一般意义上的redux中间件,它一定程度上改变了redux的行为。当前版本的redux初始化过程是与react组件独立的,而redux-arena会将action/reducer/saga与react组件绑定在一起加载,在react组件的生命周期结束时,对应于这个组件的redux信息将会被自动清理掉。

redux-arena的主要特点:

可以为不同的模块自动分配reducerKey。
扩展了mapStateToProps函数定义,可以获取当前模块的reducerKey。
支持redux-saga,可以在模块内部的saga中获取reducerKey,直接更新模块state。
支持react-router,可以使用SceneSwitch、RouteScene组件替代react-router中的Switch和Route。
提供sceneSaga和sceneReducer方法,使模块saga/reducer只接收本模块发送的事件。
支持react-hot-loader,在动态刷新时模块会被重新mount,但state保持不变。
注意事项:

redux-arena会使用模块的action中的_sceneKey字段保存reducerKey信息,如果被占用会有警告提示。
如果你不希望使用_sceneKey,可以在模块的options中添加{isPlainActions:true}。
redux默认行为不会被改变,action依旧会被广播到所有的saga/reducer中。使用sceneSaga和sceneReducer方法可以使saga/reducer忽略_sceneKey字段不匹配的action。
如果你使用redux-devtools,一旦state被redux-arena动态更新,rollback功能将不再可用。
下面我们介绍两个实际开发中会遇到的问题案例,以及使用redux-arena的解决方案。

redux-arena使用案例

案例一,企业管理系统

案例分析

这个案例中使用了react-router,一个基于浏览器history的路由方案。以及redux-saga,redux副作用处理中间件。

在企业管理系统中,往往会存在很多页面,这些页面会和url相关联,并且功能相互独立。在这种场景下,非常适合对于每一个页面注册一个单独的reducer,页面仅需发送自己的独立action,关注自己的state和框架的state(如一些用户相关信息)就可以完成渲染工作,当然这仅仅是在理想情况下。

传统的多页应用,因为浏览器的限制,往往单个页面不会塞入太多功能,不同的功能会在不同的页面操作跳转后操作。但是在浏览器技术飞速发展的背景下,用户体验较差的页面跳转方案逐步被抛弃,单个页面中会包含多个原本应跳转操作的功能。

以常见的表单提交功能为例,它包含了表单验证、异步提交、错误处理等多个功能,直接塞入主页面会使主页面代码膨胀,难以维护,拆分成独立组件看起来是一个比较好的方案。但是在抽象组件的过程中,你会发现这种组件的尴尬之处在于,由于包含了很多业务逻辑,它往往并不是可以复用的公共组件。pageA中的formA就是这种业务逻辑组件。

这种业务逻辑组件很可能会和其它层级的组件有一定的交互,或者发送异步请求,比起维护一个内部的state,放在redux中统一管理无疑是更好的选择。于是我们为formA单独注册了一个reducer。

这个架构的主要问题是,当页面越来越的复杂情况时,state、reducer以及saga会膨胀的非常厉害。很多时候,企业管理系统会包含多则数十张页面组件,而每个页面组件会包含数个业务逻辑组件,最终我们这个app会包含非常多的state,如果没有良好的state命名和组织规范,无论是新增功能还是维护旧代码,都会带来很大的困难。

同时,因为redux默认的生命周期是独立于react的,也就是说当url进行切换时,虽然旧url上挂载的react组件已经被卸载,但是其注册在redux上的功能依然生效,带来了额外的开销。

解决方案
在企业管理系统中,不同页面的state往往互不可见,我们只需要使用redux-arena将不同的页面组件打包,并在页面切换时,自动卸载它的redux相关信息即可。

可以看到,pageB卸载后,相应的redux的action/state/reducer/saga全都被卸载了。

案例二,基于redux的公用组件

案例分析
在不使用redux-arena的情况下,如果你想做一个基于redux的公共组件,那么需要你小心的维护组件在redux上的注册信息,以免和其他现有的信息冲突。

举个例子,某个页面中已经现存了一个基于redux/redux-saga实现的图表组件,它会定期请求后台数据并刷新展示的图表。某一天因为需求变动,这个图表组件需要展示多次,只是请求的url数据不同,这是按照以前的开发方式,你需要为新增的图表组件注册三个不冲突的reducer,让它们独立运作。

解决方案
使用redux-arena将图表组件打包,实例化时redux-arena会为图表组件生成一个reducerKey,避免了自行注册reducer带来的冲突。

关于redux-arena更详细的说明文档将会逐步更新,如果有任何使用上的疑问,欢迎提issue:hapood/redux-arena

我的邮箱:wanglejia@gmail.com

2 Likes
#2

为什么没什么回复?这么好的开源项目。

#3

为什么react组件卸载之后,要清掉store里相关的数据?

#4

楼主考虑到了 redux 需要事先声明 store 和 reducer 带来的问题。不知道有没有考虑过 action 和 reducer 存在的必要性

#5

这个要看需求,redux-arena我们内部使用的场景是企业应用开发,会有非常多的独立页面,独立页面中还会有很多拥有提交/验证逻辑的表单。在多人协作的模式下,redux模块化的工具能让开发者更专注于当前页面的开发工作,不需要关心其他页面组件的state/reducer。

#6

redux-arena只是一个扩展,依然兼容常驻reducer这种模式。

#7

数据生命周期和组件生命周期绑定到一起,那么是不是只需要state就能管理,而不用引入redux了呢

#8

redux追求全局单一数据来源,即整个App所有的状态都是源自redux。它解决的主要问题之一,就是组件间互相通信。如果全部使用内部state,那么在跨组件通信时会非常痛苦。

#9

你在文章提到这样一段话:“只需要使用redux-arena将不同的页面组件打包,并在页面切换时,自动卸载它的redux相关信息即可。”
我是不是可以把他理解为和页面组件的state一样的功能。

#10

不一样,页面组件的state只能在组件内部使用,很难暴露给外部,而redux的state是全局共享的。

#11

页面跳转卸载了redux的信息,怎么去共享?

#12

如果信息需要共享,不使用redux-arena提供的高阶组件包裹即可。redux-arena只是扩展,在需要使用地方的使用它。