Redux的最根本问题在于它要求数据存储是扁平的,然而真实世界的数据模型却从未扁平——而是树状的。
原生的 redux 和浏览器 api 一样,在真实项目中,不适合直接使用,
约定大于配置,能提升开发效率吗?能,而且不少。
约定一些东西,来减少模板代码,提升开发效率。

  • 约定 type 和 reducer 的 key 一致,
  • 约定 action 返回 promise 的处理方式,
  • 约定 action 中包含某个 key 改怎么处理(即引入处理这个 key 的中间件),
  • 约定 promise action 都以 _REQUEST, _FULFILLED, REJECTED 结尾,
  • 约定 action, reducer, selector, const 都在同一文件中(需要注意循环引用)。

缺陷

1、Pure Problem

pure reducer 是个好东西,有很多优点想必大家都心知肚明。但是在我的职业生涯中,为了写出 pure reducer 而把一个概念拆分成2个,而且写到2个文件里,上下文切换麻烦。而带来的好处相对我频繁的建文件,切换上下文已经微乎其微了。

对策:Redux 之所以提出 Pure Reducers 主要是没办法优雅的像 Elm 那样管理副作用。所以 Redux 就不解决了,抛给 Middleware 去解决。这里我建议直接使用可以写副作用的 Reducer。由于异步函数的存在,即使是副作用我们也能写成同步的感觉。

2、Dialog Problem

假如你有2个页面A和B,A里面有一个 button 组件B 和 Dialog 组件 D。现在从A组件切换到B,D组件需要被卸载。Redux 的话你需要在 store 创建阶段就要提前声明所有的状态,包括 Dialog 的状态。如果 Dialog 组件被卸载了,Dialog 的状态仍然残留在 Redux 中。这就是问题。

对策:Redux state 只适合放领域数据,包括前端的领域(eg. router、history 等等)和后端领域数据(eg. User, topic 等等)。Redux 没办法管理本地状态和临时状态。
Dialog 是随着组件一起加载和卸载的,Dialog state 因此是一种临时状态(volatile state)。可以借用组件的设计思路,让 Dialog state 也可以加载和卸载,即动态加载。
然而动态加载并不只是让 Reducer 动态加载就行了。Redux 有个 api 叫 replaceReducer,这个 api 只是把 store 的行为替换掉了(可以把串起来的reducer chain看做一个Behavior)。而没有让 Reducer 要处理的那部分状态动态起来。
所以正确的动态应该是让 state 和 state 对应的那部分行为抽象成新的概念,让这两者动态起来,我把这层抽象叫做 Store。

3、State Problem

如果你的组件触发了某个行为,这个时候你是知道需要修改 Redux 中的哪部分状态的。但是你还是得把这部分 state merge 到 Redux 中。最后组件只能把全部状态拿出来,再识别出自己需要的那部分状态。

对策:如果能让 state 动态起来,这个问题其实也好解决了。因为你只需要修改这部分状态,你的组件也只需要按需订阅需要的各种拆分出去的状态。

4、Single Store of Truth

Redux 之前的状态管理方案都是多 store 架构的,Redux 提出了这个新的概念作为早期卖点之一。在多子项目架构中,父项目 portal 希望把自身的某些状态共享给所有的子项目,比如当前登录用户信息等等。这个需求 Redux 就不好实现了。

对策:早期的状态管理方案 flux 中,Store 是需要继承 event emitter 的(没记错的话…)。Redux 已经看出来问题了,多 event emitter 不好管理。却默认接受了 Store = Event System 的观点。其实我觉得多 Store 架构的缺点其实是多 Event System。我们只需要让 event system 和 store 分离,让 event system 保持 single,即 Single Event System of Truth。而让 store 仍然保持 multiple 可以实现多项目共享数据的问题。同时很方便实现动态 Store 的需求。
Portal 项目创建一个 event system,把 UserStore 挂在 system 底下。子组件导入父组件的 event system,把自己的 AStore 挂在 portal 的 system 底下,就可以让 UserStore 和 AStore 交换消息了!

5、分形

项目越来越大,Store 越来越多,如果所有的 Store 都往根节点挂的话,难免有命名的问题。
特别是 Portal 已经往 system 挂了一个 UserStore,当你把 system 共享给子项目A, A也有一个 UserStore。
这就会有冲突了。

对策: Store 可以创建 Store。比如 Portal 可以创建 AStore,BStore 挂到 system 中。子项目A把自己的 Store 都往 AStore下面挂,而不是往 system(根节点) 下面挂。这样可以完美避免命名冲突的问题。

  • 内部状态:有助于解耦、由全局视角降低为局部视角,更利于应用的维护
  • 应用级别的全局状态
    • 跨模块、跨组件通信
    • 在组件销毁后,依然保持组件“操作”过的状态

不是简单得把所有东西都扔到全局,即使是全局状态,我们依然需要有模块、有规则得去管理起来,这就需要框架、工具去解决这类问题。

核心-发布订阅模式

  • react 提供的 context、Provider、Consumer
  • 发布订阅模式

难以维护