Redux 的困扰与如何技术选型
文章的名字我想了很久,备选项有“我再不推荐 Redux”,“Redux 为什么令我头疼”,“Redux 进化启示录”等等。通过这一系列名字我想你大概能猜到我接下来想聊的问题是什么,但这个问题放眼望去不是 Redux 独有,而是在做技术决策时经常会遇到的,即使对于非前端背景的开发者也同样成立。最后决定用一个带有开放式标题也许能够引起更多的共鸣。
这篇文章三年前(2019年)就想动笔,幸运的是当三年后再次拾起这个话题时,发现当初的观点依然成立。三年的时间 Redux 的社区和Flux 的生态都有了巨大变化,这些变化可以作为我们讨论的补充
如何在 Redux 里发起异步请求
如果你对 Redux 还算熟悉的话,那么不妨回答这个问题:如何在 Redux 里发起异步请求?我们暂且不管 Redux Toolkit(以下简称 RTK)的场景,把时钟拨回到三年前 RTK 还未诞生(RTK 1.0 发布于 2019 年10月23日 )前,用最核心的 Redux 技术思考这个问题
我这里给出一个备选答案看行不行?为了后面引用我们把这段代码称为方案1:
const mapDispatchToProps = (dispatch) => {
return {
fetchUser: () => {
dispatch({
type: 'FETCH_START'
});
fetch('https://randomuser.me/api/')
.then(res => res.json())
.then(({ results }) => {
dispatch({
type: 'FETCH_END',
results,
});
})
},
}
}
在 Redux 的官网教程中有专门一节来教授如何处理异步逻辑和数据抓取,无论是当下还是三年前(感谢 web.archive)的教程,它推荐的都是借助 redux-thunk 这个中间件来达到目的,这里我们称之为方案2:
function fetchUser() {
return dispatch => {
dispatch(requestUserStart())
return fetch(`https://randomuser.me/api/`)
.then(response => response.json())
.then(json => dispatch(requestUserEnd(json)))
}
}
然而在围观过 Dan Abramov(Redux 和 React 的核心成员) 在 StackOverflow 上解释为什么你应该使用 redux-thunk 的回答("How to dispatch a Redux action with a timeout?"、"Why do we need middleware for async flow?")之后,你会发现方案1并非有什么大的过错,它可行,不过当用例复杂之后代码可维护性会下降。
随之而来的是,如果你使用了 redux-thunk,它提供的 getState 方法你是否应该使用?在这个 StackOverflow 上的回答(Accessing Redux state in an action creator?)里我们看到的是两位的官方维护者(Dan Abramov 和 Mark Erikson)的两种想左意见,Mark 还专门有一篇长文解释这个问题(Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability)
Redux 是模式,而非框架
我们从上面看到了 Redux 社区里的有趣现象,即是“原则”与“实践”的分离:redux-thunk 作为官方出品并且推荐使用(但非必须,因为 redux-saga 或者 redux-promise 也能起到同样的效果)的中间件,并不在默认 Redux 安装包中。同样的,Redux 的官方文档中有大篇幅来谈 immutable 的重要性,或者是如何对数据进行 normalizing, 但在工具上它却推荐使用第三方类库进行代码约束(主流的第三方插件在官方文档中的 Ecosystem 一页中都可以找到)
与 getState 的情况类似,在另一个“如何处理数据加载过程中报错”的问题中(What is the best way to deal with a fetch error in react redux?),Dan 给出了他心目中的最佳实践。Dan 的权威性让人很难不把他的回答当作为来自官方的建议。但是让人疑惑的地方在于,我们看到的有关 Redux 的最佳实践来自于 StackOverflow 上,而非官方文档中。
为什么出现这样的结果?我的理解是 Redux 从始至终并未被当作一款常规框架来设计,它绝不会指出完成工作的唯一方式。引用核心维护者 Mark Erikson 的原话来解释就是:
Redux is not intended to be the "most concise way of doing things", but rather to make data flow obvious and readable……Docs are written in a deliberately verbose style for clarity and learning, and not specifically intended as "the one true way to write Redux code",……Redux is a generic framework that provides a balance of just enough structure and just enough flexibility
话虽如此,但我很难不认为他们自己也在动摇。上面的引用摘自在 Github 上的一次关于 Redux 广泛讨论(这是一次很重要的讨论,有兴趣的同学建议读完):Request for Discussion: Redux "boilerplate", learning curve, abstraction, and opinionatedness。正如讨论的标题所示,以灵活性优先的设计理念,以及为学习而非实践服务的文档内容,给其他开发者带来了与我同身受的问题:高昂的学习成本、高度的抽象以及缺少对最佳实践的指导。
这也是 Redux Toolkit 诞生的原因,从 Mark 对于 RTK 的愿景(My Vision for Redux Starter Kit)中就不难看出它的目标在此:
- Make it easier to get started with Redux
- Simplify common tasks
- Opinionated defaults guiding towards "best practices"
- Provide solutions to make people stop using the word "boilerplate"
RTK 在当下已经作为 Redux 项目的标配而存在了,在 Redux 官网的第一章 Getting Started with Redux 我们便会看到:Redux Toolkit is our official recommended approach for writing Redux logic——“approach”是个有意思的词,它让我感觉原始的 Redux 框架更像是摸不着的理论而存在——而它的下半句 Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications. 将是我们聊技术选型的切入点。
从 Redux 到技术选型
"prevent common mistakes"
我反复向人推荐过一篇 StackOverflow 联合创始人Jeff Atwood 的文章 Falling Into The Pit of Success,简而言之它的中心思想是,好的系统设计应该很容易的让人们把事情做对,杜绝把事情做错。比如 type checking
很显然 RTK 之前的 Redux 并不符合这一条件,甚至与之相悖。因为“容易做对”实践起来务必会削减框架提供的技术选项(甚至你最好只采用这一种方式去做),而 Redux 主要目的是服务于灵活而非最佳实践。另一点是官方用大量段落的文字去讲述 immutable、normalize 的重要性,但是在技术层面却不提供任何约束。
而 RTK 彻底解决了这个问题吗?RTK 解决了代码模板的问题,天然集成了最佳实践,但它移除不掉高昂的学习成本,这是我最担心的。
如果你有兴趣去拿 Redux 文档与前流行的几个 Flux 框架文档进行对比,比如 Mobx、Zustand 以及 Akita,你会发现 Redux 所需要掌握的概念和篇幅长度令人发指(很有意思 Angular 是另一个极端,学习 Angular 同样要掌握很多概念,但是对于你想做的每件事情它都帮你想到了,在文档中给出了最佳实践)。我理解工程师热爱“挑战”,但是在现实中,团队是由不同水平的个体组成,团队所能接纳的“难度”常常不尽如人意。如果他们不理解他们的工作,那么他们就很难把工作做好。
"simplifies most Redux tasks, easier to write Redux applications"
Clojure 的作者 Rich Hickey 在 2011 年有一篇很有意思的演讲,名为“Simple Made Easy”
他给 easy 和 simple 做出了精确的定义
- Simple:单个事物比如一件任务、一个角色、一个维度;简单的事物不应该包含交织的概念,比如一个实例,一次操作。simple 是客观的
- Easy:凡是我们熟悉的或者近在咫尺的事物都会让我们感到简单,比如说我们熟悉的语言,使用我们常用的 IDE,easy 是主观的
这两者看起来区别不大,但是他认为的这两者对开发速率的影响是:
easy 可以优化你的启动速度,但如果你只是一味的追求 easy 而忽略了复杂性的话长远看还是事倍功半的。比如在 Flux 之前的 MVC 时代,在 model 中去更新 view 或者在 view 中直接调用 model 是多么 easy 的事情,但这却让状态管理的复杂性大增。
Redux 算 easy 吗?不,从 Flux 到 Redux 是一整套全新的概念;它算 simple 吗?未必,在你设计 reducer 的时候你很难不去思考 normalize
流行
“流行”或者“标配”不应该是技术选型的参考之一。
你的前端项目需要 Redux 吗?你也许会用反问来回答我这个问题:Redux 不是 React 项目的标配吗?
并非如此。无论是在 Hacker News 上网友对于 Redux 的抱怨 (God I hate redux)里,还是在 reddit 上在对 Redux 的仇恨讨论中(Why all the sudden hate for Redux?),Mark 都解释到团队从来都没有以 Redux 作为 React 的主流状态管理工具去营销它,它之所以变得主流一方面是因为它赢得了 2015 年的 Flux Wars,另一方面它切实解决了开发者的问题。Dan 也写过一篇文章来刻意强调你也许并不需要 Redux(You Might Not Need Redux)。
如果说我们把 2015 年的 Flux War 比喻成第一次 Flux 世界大战的话,那么当下这个时间点第二次世界大战正进行的如火如荼,在市面上我们可以看到更多优秀的 Flux 框架,比如 Mobx,比如冉冉升起的 Zustand,比如小众的我很喜欢的 Akita。Redux 很大可能不会是你下一个新项目的最佳选项
在 StafkOverflow 直白的发文求网友推荐技术框架一类的问题通常都会被关闭。因为在没有任何上下文的前提大部分答案过于主观了。如何思考我建议你至少参考这个回答(How can Stack Overflow help developers evaluate technologies?),先问问自己:
- 我需要什么(Know what you need)
- 我不需要什么(Know what you don't want)
- 我还想要些什么(Know what you want)
但我可以理解“简历驱动开发”是选择当下的流行技术的重要原因,我也深表同情。
你可能会喜欢
- CSS 里的整洁架构
- 前端架构 101(一):在谈论它们之前我们需要达成的共识
- 前端架构 101(二): MVC 初探
- 前端架构 101(三):MVC 启示录:模块的职责,作用域和通信
- 前端架构 101(四):MVC的不足与Flux的崛起
- 前端架构 101(五):从 Flux 进化到 Model-View-Presenter
- 前端架构 101(六):整洁(Clean Architecture)架构是归宿
- 【译文】【前端架构鉴赏 01】:Angular 架构模式与最佳实践
- 【译文】【前端架构鉴赏 02】:可拓展 Angular 2 架构
- 【译文】【前端架构鉴赏 03】:Angular 与 MVP 模式
- 微前端说明书
- 从美团这篇文章聊聊微前端的聚合问题
- 写给前端看的架构文章(1):MVC VS Flux
- 从MVC模式在前端开发中的局限性谈起
- Flux与Redux背后的设计思想(二):CQRS, Event Sourcing, DDD
- Flux与Redux背后的设计思想(一):Command Bus, Event Bus, Service Bus
Redux 的困扰与如何技术选型的更多相关文章
- [转]聊聊技术选型 - Angular2 vs Vue2
转载:https://juejin.im/post/58cab85b44d9040069f38f7a "Come, and take choice of all my library, An ...
- 聊聊技术选型 - Angular2 vs Vue2
作者介绍:李旸,美团点评前端工程师,3 年 Web 前端开发经验,现在是美团点评点餐团队的一员. "Come, and take choice of all my library, And ...
- react 前端项目技术选型、开发工具、周边生态
react 前端项目技术选型.开发工具.周边生态 声明:这不是一篇介绍 React 基础知识的文章,需要熟悉 React 相关知识 主架构:react, react-router, redux, re ...
- #数据技术选型#即席查询Shib+Presto,集群任务调度HUE+Oozie
郑昀 创建于2014/10/30 最后更新于2014/10/31 一)选型:Shib+Presto 应用场景:即席查询(Ad-hoc Query) 1.1.即席查询的目标 使用者是产品/运营/销售 ...
- 老王讲自制RPC框架.(一.前言与技术选型)
(#)背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构 当网站流量很小时,只 ...
- Atitit 开发2d游戏的技术选型attilax总结
Atitit 开发2d游戏的技术选型attilax总结 1.1. 跨平台跨平台:一定要使用跨平台的gui技术,目前最好的就是h5(canvas,webgl,dom) +js了..1 1.2. 游戏前后 ...
- 《2016ThoughtWorks技术雷达峰会----js爆炸下的技术选型》
JS爆炸下的技术选型 刘尚奇 ThoughtWorks, 高级咨询师 JS每6个星期出现一个新框架,那么如何进行JS的选型.以下从四个方面来分析. 1.工具 NPM for all the t ...
- 手机web站点和手机app 技术选型的困惑于思考
今年一直在关注移动端技术的发展,自己也用博客园的rss接口玩了半年,关于技术选型的困惑和大家说说 一 趋势 随着手机硬件不断的升级,外加4g牌照的发放,不出2年时间移动端web站点和手机app一定会进 ...
- atitit.技术选型方法总结为什么java就是比.net有前途
atitit.技术选型方法总结为什么java就是比.net有前途 #----按照不同的需要有不铜的法... 一般有开发效率,稳定性上的需要.. 作者 老哇的爪子 Attilax 艾龙, EMAIL: ...
- 消息中间件的技术选型心得-RabbitMQ、ActiveMQ和ZeroMQ
消息中间件的技术选型心得-RabbitMQ.ActiveMQ和ZeroMQ 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs RabbitMQ.Active ...
随机推荐
- 手把手教你君正X2000开发板的OpenHarmony环境搭建
摘要:本文主要介绍基于君正X2000开发板的OpenHarmony环境搭建以及简单介绍网络配置情况 本文分享自华为云社区<君正X2000开发板的OpenHarmony环境搭建>,作者: 星 ...
- 云原生之旅 - 2)Docker 容器化你的应用
前言 上文中我们用Golang写了一个HTTP server,本篇文章我们讲述如何容器化这个应用,为后续部署到kubernetes 做准备. 关键词:Docker, Containerization, ...
- Traefik SRE 之使用 Prometheus 进行监控报警
当我们使用 Traefik 作为 Kubernetes 的 Ingress 控制器的时候,我们自然也非常有必要对其进行监控.本文我们将探讨如何使用 Prometheus 和 Grafana 从 Tra ...
- Traefik 2.0 实现自动化 HTTPS
文章转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247484457&idx=1&sn=35112e98 ...
- fastdfs-zyc管理FastDFS的web界面
俩压缩包根据大小重命名以下,按图片所示 把1_fastdfs-zyc.7z重命名为fastdfs-zyc.7z.001 把2_fastdfs-zyc.7z重命名为fastdfs-zyc.7z.002 ...
- 在k8s中部署前后端分离项目进行访问的两种配置方式
第一种方式 (1) nginx配置中只写前端项目的/根路径配置 前端项目使用的Dockerfile文件内容 把前端项目编译后生成的dist文件夹放在nginx的html默认目录下,浏览器访问前端项目时 ...
- Elasticsearch:反向代理及负载均衡在 Elasticsearch 中的应用
文章转载自:https://elasticstack.blog.csdn.net/article/details/108365746
- 原生js如果将string类型的数进行值
原生的tring类型比较会进行隐式转换,如'100'>90 为true
- 基于python的MD5脚本
摘要 鉴于网上的各大MD5爆破网站,当网络差时访问速度慢,至此小弟写了个基于python的MD5爆破脚本,欢迎各位师傅在评论区留下您们宝贵的意见. 开发思路 1.通过 string模块 自动生成字典: ...
- C++自学笔记 面向对象程序设计OOP(Object Oriented Programming)
什么是对象? Objects = Attributes + Services Data : The properties or status Operations: the fuctions C语言中 ...