最近在做项目重构的时候,准备采用next js进行服务器端的渲染,在状态管理方面一直没有找出一个很好的方案,因为自己有以下的需求点:
1. 配置更加简单,少些模板化代码
2. 状态管理跟着路由走(next js)里无路由配置,可以说成是跟着模块走吧。
3. 更好的调试工具
期间考虑过以下几个状态管理方案
1. redux: redux设计的太重,actionType, action creator, reducer,异步处理等太过分散,虽然可以使用redux-action进行一些模板代码的编写工作,但是整个说来还是过于繁杂。
2. mobx: 挺好的状态管理工具用的人也比较多,但是一直对这种更改数据结构的库没有啥好感,毕竟是团队开发,所以数据结构这种东西应该要严格的进行规范。
3. dva: 重构前项目使用的就是dva1.x版本,感觉下来是挺不错的,因为商城项目也并不大,所以感觉整体用下来感觉还挺不错的。dva的动态化加载是跟着路由走的,所以对于next js来说不太适合。
所以就有了下面这篇文章,文章是翻译过来的。过段时间等项目重构完了,来说下感受吧!
重新设计 Redux
状态管理工具不是应该用来解决问题的吗? 直观的来说,开发者似乎知道这样一个事实: 状态管理似乎是比较难管理的. 在这篇文章中,我们探讨您可能会问自己的一些问题:
- 您需要一个状态管理库吗?
- 最流行的Redux库是否是值得采用的? 为什么选择,或者为什么不选择?
- 我们是否能够制定一个更好的管理库哪,如果要做,应该如何做?
状态管理是否需要一个库?
作为一个前端仅仅只是调调样式; 真正的开发者应该知道在哪里存储你的状态. 简单的来说: 它是复杂的, 但又不是复杂的.
在使用基于组件的视图框架/库(如React)时,让我们看看我们的选项:
1. Component state
存在于单个组件内的状态。 在 React中, 想想一下 state
被 setState
更新.
2. Relative state
状态从父组件传到子组件. 在React中, 想想一下 props
作为一个属性从父组件传到子组件.
3. Provided State
状态保存在一个根的 provider中, 由组件下的 consumer访问而不管是否是接近的. 在React中, 想想一下 context API
的使用过程.
很多的状态都在View中, 它影响着UI的显示. 但是影响着基础数据和逻辑的代码在哪里哪?
将所有的数据放在页面中会导致 separation of concerns: 它将你的javascript与你的试图联系在了一起, 这会导致代码比较难以测试, 最大的发恼是: 您必须不断思考和重新调整存储状态的位置.
由于设计发生变化并且通常很难分辨哪些组件需要哪种状态,因此状态管理变得复杂。最直接的选择是从根组件中提供所有状态,此时您可能最好只使用下面所说的管理方式。
4. External State
状态可以被移到视图层以外. 这个库可以用 “connect” 将 provider/consumer 链接,以保持同步.
也许最流行的状态管理库是Redux。在过去的两年里,它已经大受欢迎。那么为什么这么喜欢一个简单的库?
Redux性能更高吗? 不会。每次必须处理的新动作都会稍微慢一些。
Redux更简单吗? 当然不是.
那么为什么每个人不适用 global.state = {}
?
</section>
<section>
为什么 Redux?
在内部,Redux实际上和根对象一样。
在Redux中你不能直接修改状态. 唯一修改的办法是用: dispatch 发出一个 action 进入这个管道去更新状态.
管道中有两个监听器: middleware & subscriptions. 中间件可以监听到传入的action, 例如 “logger”, “devtools”, or a “syncWithServer” listener. Subscriptionsare 是用来将状态的更改广播出去.
最后, reducers 将状态更新分解到更小, 更模块化和更好管理的块.
Redux对开发者来说更加简单比使用一个全局状态来作为应用的状态来说。
将Redux作为一个一个状态到后一个状态的全局对象,以及到下一个状态的简化方法。
</section>
<section>
但Redux不是太复杂吗?(自己总结的痛点)
- 复杂的Api,和配置
- 不知道如何组织action, action creator, reducer
- 如何处理异步
</section>
<section>
重新设计Redux
我认为Redux值得重写。我带来了7个应该改进的地方。
1. 设置
让我看下最基本的设置 例子。
在第一步之后许多的开发者是对这比较迷惑的. 什么是 thunk? compose? 一个方法可以 做这些事吗?
Redux是否要基于组合配置. 最右侧的设置是不是更加友好.
2. 简化的 Reducers
Redux中的Reducers让我们远离不必要的切换语句。
假设reducer与action类型匹配,我们可以反转params,这样每个reducer都是一个接受状态和动作的纯函数。也许更简单,我们可以标准化操作并仅传入状态和有效payload。
4. Async/Await over Thunks
Thunk通常用于在Redux中创建异步操作。在许多方面,thunk的工作方式似乎更像是一个聪明的黑客,而不是官方推荐的解决方案。
- 您调度一个动作,它实际上是一个函数而不是预期的对象。
- Thunk中间件检查每个动作以查看它是否是一个函数。
- 如果是这样,中间件调用该函数并传入一些存储方法的访问权限:dispatch和getState。
真的?对于简单的动作来说,作为对象,函数,甚至是Promise动态地键入是不是不好的做法?
就像右边的例子,我们不能只是异步/等待吗?
5.两种行为
当你考虑它时,实际上有两种行为:
- Reducer action:触发reducer action并改变状态。
- Effect action:触发异步操作。这可能会调用Reducer操作,但异步函数不会直接更改任何状态。
区分这两种类型的操作会更有帮助,并且不会混淆上述用法与“thunks”。
6.没有Action Types作为变量
为什么以不同的方式对待action creator和reducer是标准做法?没有其他的存在吗?改变一个不影响另一个吗?
action creator和reducer是一枚硬币的两面。
const ACTION_ONE = 'ACTION_ONE'
是action creator和reducer分离的副作用。将两者视为一体,不再需要导出类型字符串的文件。
7.作为action creator的reducer
通过使用Redux的元素进行分组,您可能会想出一个更简单的模式。
(https://gist.github.com/ShMcK/6e8a7b576ffac1975ec2868c7816c37c)
可以从reducer中自动确定action creator。毕竟,在这种情况下,reducer可以成为action creator。
使用基本命名约定,以下是可预测的:
- 如果reducer的名称为“increment”,则类型为“increment”。更好的是,让命名空间:count/increment。
- 每个操作都通过“payload”传递数据。
现在count.increment
我们可以从reducer生成动作action creator。
</section>
<section>
好消息:我们可以拥有更好的Redux
这些痛点是我们创建Rematch的原因。
Rematch是Redux的包装器,它提供了一个更简单的API,而不会丢失任何可配置性。
请参阅下面的完整Rematch示例:
(https://gist.github.com/ShMcK/194360c2b54c2e55c1008a17e486510c)
在过去的几个月里,我一直在使用Rematch。作为见证,我会说:
我从来没有花太多时间思考状态管理。
Redux不会消失,也不应该消失。拥抱Redux背后的简单模式,学习曲线更少,样板更少,认知开销更少。
尝试rematch,看看你是不是喜欢它。
</section>
</article>