刚开始使用 redux 是只是忙于学习他怎么用,从未考虑过他是不是有什么缺点,直到看到了 dva/rematch 时,才意识到 redux 在某些层面上确实存在一些“问题”,下面是一些思考的记录
Redux 的学习使用
开始学习 redux 时,会遵照在网站上查询到的如下内容书写:
import { createStore } from 'redux';
const reducer = (state,action) => {
switch(action.type){
// todo...
}
}
const action = data => ({
type: 'xxx',
data
})
const store = createStore(reducer)
store.dispatch( action(2) )
上面的代码展示了创建 store,reducer,action 到调用 action 的过程,如果你熟悉 redux,会对这些代码非常熟悉,当你需要新添加一个 action 时,你都需要添加对应的的 reducer 去处理,不然就做不到改变状态,这些的写法很规范的展示了单项数据流,数据流向清晰。
但是会在项目中出现很多个 action 已经 action.type。而且可能会将多个 action 的处理放在一个 reducer 中,这也会导致一个 reducer 过于臃肿,使后续的开发和维护变得艰难。
这时,可能会做以下的优化:
- 将 action.type 单独写出来,方便调用
- 适时的将 reducer “减肥”,防止其中过多的处理代码
要实现上述两点,需要每个参与开发人员进行规范的定义,还要防止 action.type 出现重复。这样子可能会使参与开发者的负担加重,因为还要思考注意一些非项目逻辑的问题。
说道这里,其实 redux 的“问题” 已经暴露出来了,它需要开发者去维护很多东西,因为它足够“简单”。所以不帮你处理过多的事情。及时你想执行一个 function action ,你也需要自己去找到 reduxthunk/redux-sagas 中间件进行处理,这在无形中为你增加了一些“额外的工作量”。
现在设想一下,如果存在一种“方便”的 redux,可以通过以下便捷的方式实现项目使其需求
import {init} from ‘xxx’;
const moduleA = {
state: {},
reducers: {
in: () => {}
}
....
}
const store = new init({moduleA})
store.dispatch({
type: 'in',
data: 2
})
这种方式的 “redux” 简化了定义过程,他将 reducer 和 action.type 进行了合并,但是同时语义更加清晰。键表示 action.type 而值则表明了这个 action 想做的操作,这样子就可以是 action.type 和要执行的 reducer 对应起来,就不用再向以前一样,那这个 action.type 去满世界的寻找 reducer 进行维护。
同样的,这种方式没有解决 action.type 重名的问题,但是可以思考一下方式:
import {init} from ‘xxx’;
const moduleA = {
name: 'moduleA',
state: {},
reducers: {
in: () => {}
}
....
}
const store = new init({moduleA})
store.dispatch({
type: 'moduleA/in',
data: 2
})
通过给每个 module 添加一个 name 属性,调用 action 时加上前缀,可以极大限度的解决重名的问题。同时通过 module 的分割,可以把 redux 整个状态树切割成小块,使整个树的结构更加清晰。
如果实现了上述的 “redux”,还可以减少书写 redux 模板代码的次数,而且 reducer 变成了一个对象,也就解决了 switch 语句变得臃肿难维护的问题。
仍然存在的问题
当需要进行异步请求的时候,redux 天生不支持 function action,我们通过一些中间件 reduxthunk/redux-sagas,可以增强 action 的功能,达到执行异步的目的。但是此时,异步/同步 action 是混合在一起的定义的(可能有些项目中时分开的),这会导致在阅读时产生极大的障碍,因为不得不在一堆同步/异步中找到我们的目标。一般期望情况下,需要将异步与同步区分开来,方便维护和阅读。可能是下面这样子:
import {init} from ‘xxx’;
const moduleA = {
name: 'moduleA',
state: {},
reducers: {
in: () => {}
},
effects: {
be(){
// 异步操作
}
}
....
}
const store = new init({moduleA})
store.dispatch({
type: 'moduleA/in',
data: 2
})
这样子,同步和异步 action 就分开定义,更好地阅读和维护。因为 action 在调用时我们不知道他是异步还是同步的。
上面的代码片段即使你是一个 redux 的初学者,你也能在不知道reduxthunk/redux-sagas等中间件的时候很好的使用 redux 进行状态管理,而且理解上的成本也很低。最主要的是简化了整个流程的创建步骤和过程。
正片开始
当你也有这些思考的时候,你就需要了解下 dva/rematch 这些在 redux 基础上进行封装的状态管理库,他们实现了上述的所有功能,使你只学习简单的 api 就可以愉快的使用 redux,而不需要关注太多与你实际代码逻辑无关的问题。
这里简单的提一下,dva/rematch 和 vuex 的思想类似,他们都是提供了极简的 api,就可以实现应用状态管理。