React 已经疯了,而没有人谈论它

最近,我做了一个副项目,我在另一篇文章中写到了它。作为该项目的一部分,我原本只打算写几段关于 React 糟糕的地方——但我就是停不下来。

于是,这里是一篇完整的、独立的博客文章,比它所衍生出的那篇还要长,全部关于 React 糟糕的地方。而且这可能并非 React 本身的过错。

老派 Angular

在我初出茅庐的岁月里,我曾靠开发 Angular.JS 谋生。当时,它是一项真正优秀的技术。毫无疑问是当时最大的 JavaScript 框架,而它最重要的是,可能是 web 开发领域首次出现“框架”概念。在此之前,它们都是“库”,而Angular是第一个不仅提供了一组函数供你使用,还提供了实际的框架来构建你的网页应用。

但事物的好坏总是相对的,Angular之所以好,是因为它的前辈们不够好。当时,我们还有其他单页应用框架,如Backbone和Knockout,但它们没有留下那么大的影响。不,Angular真正击败的对手是jQuery。

元素周期表尽管jQuery只是对(当时确实非常粗糙的)HTML DOM API的封装,但它仍成为构建复杂网页应用的实际标准。其工作原理相当简单:你在 JavaScript 中手动创建 HTML 元素,然后对其进行修改、移动,做任何必要的事情,以使网站像应用程序一样具有交互性。

对于简单应用程序来说,这完全没问题,但你可以想象,对于更大规模的应用程序,这会变成维护噩梦。而这种情况正是开始发生。你不能真的怪罪 jQuery,而是现代用户对这种交互性的需求日益增长。因此,开发者被迫继续使用 jQuery,尽管它已不再适合这项工作。

随后 Angular 出现并解决了所有问题。你可以专注于编写 UI 和应用逻辑,而不是手动拼凑 HTML 的各个部分。它确实是一个改变游戏规则的库框架,因为你终于有了一个合适的工具来创建大型交互式应用程序。它的一些神奇功能:

A) 组件。好吧,它的命名有点奇怪,所以这些实际上被称为“指令”,但无论如何,你可以定义一个简单的 HTML 和 JS 文件组合,代表 UI 的一个部分,然后在应用程序的多个地方重复使用它。

B) 双向绑定。你定义一个变量,只要它发生变化,UI中的所有相关位置都会自动更新。这种机制运行得非常顺畅。后来,人们开始抱怨这种双向数据流存在问题,于是开始推动使用单向(自上而下)绑定,虽然从技术上讲这听起来更合理,但在实际应用中却让一切变得更加复杂,并引发了一系列讨论,最终导致我们今天不得不使用Redux。所以,谢谢。

在我第一份工作中,我参与了将一个庞大且难以管理的 jQuery 应用程序重写为 Angular 应用程序的项目。整个过程和最终结果都相当不错。

然而,几年后不得不将相同的界面在 Angular 2 中重新编写,这并不理想。我庆幸自己及时离开了那家公司,否则他们可能会让我在之后用 React 进行第三次重写。

React的出现

后来我确实有机会学习React,并在几个项目中专业地使用它。

我至今仍记得初次见到它时那股清新感。当时与主流框架Angular 2的对比尤为明显——后者虽是对原版的完全重写,但带来了两倍的冗余代码、开箱即用的TypeScript、单向绑定、响应式/可观察模式等特性,这些单独来看都是好事,但天啊,它实在太复杂、开发速度慢、构建速度慢、运行速度慢。

React将 pendulum 摆回了简单性的一端,人们对此欣然接受。一段时间内,简单性得以保留,React凭借其易用性成为构建单页应用(SPA)的头号库。

是的,我们又开始使用“库”这个术语,这表明它确实更简单了。但仅凭一个库无法合理地构建复杂应用。你需要多个库来处理应用程序的所有需求,同时还需要一定的代码结构。React的“自带啤酒”方法意味着你基本上需要自己构建一个框架,而这带来了所有相应的缺点。

最终结果是——没有两个React应用程序是相同的。每个应用程序都基于从互联网上随机找到的库构建了一个定制的“框架”。

我当时不幸参与开发的应用都让我产生了同样的看法——即使是Angular 2也比这更好。JSX“核心”似乎很稳固,但周围的一切都一团糟。

所以我离开了,开始编写一些Java后端,这我想已经说明了一切。

正当我以为自己已经脱身……

人们常说,一个人永远无法真正学会任何东西——要么你知道,要么你不知道。显然我不知道,所以最近我不得不重新投入React的学习。

诚然,这只是一个业余项目,所以我并没有像开发严肃的生产应用那样全面体验它。但即便如此,这次经历不仅证实了我的低预期,还远远超出了我的预期。React让人感到疯狂,我不知道为什么其他人没有在讨论它。

架构、组件、状态

首先,让我们从React强制要求的架构开始。如前所述,React只是一个库,所以它不会强迫你做任何事情,但仍然,使用JSX的隐含限制会让一些模式自然浮现。很久以前,我们曾讨论过MVC、MVVM、MVP等架构模式,它们本质上都是同一主题的不同变体。那么React属于哪一种?我认为这是一种相对较新的范式——我们可以直接称之为“组件化架构”。

乍一看,这一切都很有逻辑。你拥有组件,构建一个自上而下的组件树,然后你的应用就完成了。React会进行一些内部处理,确保它与您提供的数据保持同步。这很简单。

但不知何时,它开始表现得比它应该的更聪明。对于一个简单的“UI库”,React确实有很多专业术语。而对于一个与“函数式编程”无关的库,它内部却有很多函数式编程的名称。

让我们从状态开始。如果你有一个自上而下的组件树,那么自上而下传递状态是合乎逻辑的。但在实际中,当组件数量众多且规模较小时,这种做法会变得非常混乱,因为你需要花费大量时间和代码来连接各种数据片段,以确保它们到达所需的位置。

这个问题通过使用 React 钩子将状态“侧载”到组件中得到了解决。我还没听说有人对此提出过异议,但你们是认真的吗?你们是在说任何组件都可以使用应用程序的任何状态吗?更糟糕的是,任何组件都可以触发状态变化,而这些变化又可以更新到其他任何组件中。

这种设计怎么能通过代码审查?你们基本上是在使用全局变量,只是加上了更复杂的状态修改规则。这些规则甚至算不上规则,只是形式上的规定,因为没有任何机制能阻止你从任何地方修改状态。人们真的认为,只要给某个东西起个像 reducer 这样聪明的名字,它就突然变成了“良好架构”吗?

既然自上而下和侧载方法都行不通,那么解决这个问题的方案是什么?我真的不知道。事实上,我唯一能想到的是:如果我们无法优雅地解决这个问题,那么也许整个“组件架构”本身就是一个错误,我们不应该将其称为“优秀设计”的典范并停止创新。也许这次我们真的需要另一个 JavaScript 框架来尝试更好的解决方案。

React Hooks

接下来,让我们来谈谈那些“我们不确定它们是如何通过代码审查的”内容,比如 React Hooks。不可否认它们很有用,但它们的存在至今仍让我感到困惑。

我甚至不想提人们如何将组件称为“纯函数”,却在其中嵌入状态管理黑盒。考虑到其可组合性,这更像是层层嵌套的状态管理黑盒。

但我要重点吐槽的是 useEffect。什么是“副作用”很简单。你修改了状态,然后需要执行一些外部操作,比如将结果发送到 API。这种将“重要应用逻辑”与“副作用”分离的思路在理论上说得通。但在实践中,你真的能干净利落地做到这一点吗?

我最大的不满在于,useEffect 被用作“在组件挂载后执行某些操作”的工具。我理解当 React 从类迁移到钩子时,这是最接近 componentDidMount 的替代方案,但说真的——这难道不是一个巨大的 hack 吗?

你正在使用一个“副作用”钩子来初始化组件?好吧,如果你必须从那里进行 API 调用,我同意那是一个副作用。但然后那个 API 调用……它……它也设置了状态。因此,一个完全无害的“副作用”钩子实际上在管理组件的状态。为什么没有人讨论这有多疯狂?

此外,如果你想依赖那个状态并在之后做些什么,那么你……你……必须定义另一个依赖于第一个钩子设置的状态的 useEffect

这是我从最近以数千万美元收购的一家公司的生产应用程序中提取的代码。我在这里稍作修改,使用了更简单的 “房子 “和 “猫 “实体,而不是实际存在的实体。不过,你可以去看一看,并试着分析一下这段代码是按什么顺序执行的。准备好后,答案就在下面的图片中:

所以,类似这样的情况,原本可以使用简单命令式代码实现的一系列状态变换,现在却分散在两个异步函数中,而它们的执行顺序唯一线索就是每个函数底部的那段“依赖数组”。而你实际在脑海中解析它的方式,实际上是从底部到顶部。

我记得JavaScript的Promise曾因其“then”结构被认为难以驾驭,而在此之前我们还有“回调地狱”——但无论如何,这都比现在的情况好。

我明白这些问题可以通过两种方式解决:a) 将它们移至单独的文件中,这只是在掩盖问题,或b) 可能使用Redux或其他类似方案,但我对这些技术还不够熟悉,无法确定具体实现。

“模式”

所有这些结合起来看起来很丑陋,背离了 React 在“Hello world”示例中承诺的简洁性。但等等,我还没说完。我读了一篇熟人写的博客文章,标题是“最常见的 React 设计模式”。尽管我不知道会看到什么,但这些模式的复杂程度和理解它们所需的脑力开销还是让我震惊——而这一切只是为了在屏幕上渲染一个列表。

最令人震惊的是:文章甚至没有提及这一点。所有这些复杂性都被视为理所当然。人们显然真的用这种方式构建 UI,而没有人感到惊讶。

然后,仿佛这还不够,你们中的一些人甚至会写“CSS-in-JS”,然后为此获得报酬。我同意JSX最初表明“关注点分离”并不等于“文件分离”,实际上将HTML和JS写在同一个文件中是可以的。但将CSS也扔进去并使其成为强类型?这不是走得太远了吗?

为什么

仅仅说 React 简直疯了,然后继续我们的生活,这太容易了。但作为理性的灵长类动物,我相信我们可以做得更好。我们可以尝试理解它。

我再次沿着记忆的高速公路回溯,想起我的第一份工作和来自那个“jQuery 迁移”项目的同事。一位经验丰富的后端工程师,架构师类型,在软件领域备受尊敬。

我对他印象最深的不是他的技术解决方案,而是他对我们前端工作的评判。每次看到Angular应用的任何部分,他都会说:“你们在这里到底在做什么?为什么一定要这么复杂?”

这并不是说我们做得不好——我们也是一群对软件毫不妥协的团队。只是当时,从一个传统后端开发者的角度来看,整个Angular架构似乎完全不可理喻。

如今,我大致和他当时的年龄相当,却在这里写一篇关于Angular React如何不可理喻的博客文章。有些事情是不可避免的,我想。

但让我们跳出固有思维,尝试理解为何会如此。

首先,我想我们都能认同,大多数网页应用本就不该是网页应用。人们选择SPA(单页应用)即使当下不需要,但未来可能需要,因此从头开始采用SPA似乎成本更低。

但我认为,这种做法实际上是有成本的。只是我们已经习惯了“默认使用SPA”的方式,以至于忘记了其他方案的简单性。一个简单的、由服务器端渲染的页面,其复杂度比考虑使用React要低几个数量级。无需承担API通信的开销,前端非常轻量级,你的UI代码可以是强类型的(如果你的后端是强类型的),你可以对整个堆栈进行重构,一切都会加载得更快,你可以更好地缓存它,因为一些组件非常静态且对所有用户都相同,因此你可以只渲染一次,等等,等等。

不过,你确实会失去根据产品经理的意愿实现复杂交互逻辑的灵活性。但这可能只部分正确,因为我敢打赌,仅凭纯 JavaScript 的“渐进增强”就能走得很远,直到你真正需要处理复杂状态管理时,才需要引入 React。

好吧,我的意思是我们使用 React 仅仅是因为我们之前用过它。这不足为奇,惯性确实是一种强大的力量,但这仍然无法解释为什么这段代码会变得如此难以置信的复杂。

我对这个问题答案的回应,令人惊讶的是,不再批评 React,而是转向相反的方向,不仅为 React 辩护,也为 Angular、jQuery 以及它们之前的其他技术辩护。我认为这段代码糟糕的原因在于,创建一个任何组件都可以更新其他组件的交互式 UI,本身就是软件开发中最复杂的事情之一。

想想你日常生活中使用的其他系统。你的厨房水槽有两个输入,热水和冷水,以及一个输出,即流水。你的厨房搅拌机或电钻可能有按钮,但无论你做什么,它只影响旋转部分的动作。烤箱可能有三个、四个或五个旋钮,可能有相同数量的输出,这已经开始听起来相当危险了。

相比之下,网页上的交互式用户界面可能拥有无限数量的输入和无限数量的输出。你如何期待为这种情况编写“干净的代码”?

因此,关于 React 的这番抱怨……这甚至不是 React 的错。Angular 或 jQuery 也不是。简单来说,无论你选择哪种技术,都必然会在构建响应式用户界面的不可能复杂性下崩溃。

如何解决

我们该如何解决这个问题?我既不够聪明,也不够深入细节来真正解决这个问题,但我可以提出一些想法。如果我们把网页的输入/输出模型视为一个真实存在的事物,那么也许我们可以开始尝试减少其输入和输出的数量。

在输入方面,没错,这就是我在说:“减少按钮数量”,这可能并不总是,甚至永远无法强制执行。但无疑,功能越少,代码库就越易于管理。

这似乎显而易见,无需多言——但事实真是如此吗?产品经理是否知道,在某个界面中增加三个按钮而非两个,会导致5%的 bug 增加,并使未来该界面的设计与实现复杂度提升30%?没有人会去衡量这些指标,但我相信这些结论可能是成立的。

为什么,如果我告诉你我们需要在后端添加Redis,你会告诉我“不,我们需要控制技术复杂性”——但如果产品经理要求添加一个全局应用范围的过滤器,可以从任何地方应用到任何内容,你就会埋头写一些怪物般的代码,人们将在未来10年试图摆脱它。

简而言之——请停止添加这么多按钮,我恳求你。你甚至可以,我知道这听起来疯狂,尝试移除其中一些?

然而,在输出方面,情况有所不同。撰写此文让我意识到,服务器端渲染的页面本质上将页面简化为单一输出。你与之交互的任何内容,都会重新构建整个页面。这意味着,讽刺的是,移除受 FP 启发的 React 后,服务器端渲染的页面实际上成了状态的纯函数。没有前端状态 = 极简主义的胜利,如果你能承受的话。

不可避免地,当你在服务器端渲染的“应用”中需要一些脚本逻辑时,也许明智的做法是只在最必要的地方添加。越少越好。

我认为一个合适的名称是“交互性岛屿”。然后我谷歌了一下,发现这已经是一个现有的概念。尽管那篇文章仍然提到了Preact、SSR和清单文件,所以我不敢确定我们是否真的在同一页上。人们总会把事情复杂化。

但我相信,如今我们有足够的带宽,可以加载一个小型 React 应用,它只在经典的服务器端渲染页面中渲染一个交互性岛屿。我认为这种混合方式并不糟糕,但我尚未尝试过,而我的下一个项目可能会尝试。

因此,我尚未验证的保持前端代码干净可维护的方法是:在服务器端渲染所有内容,然后只在真正需要的地方插入 React 或其他技术。

这真的不会比现在更糟糕了。

本文文字及图片出自 React Still Feels Insane And No One Is Talking About It

共有 280 条评论

  1. 当 React 发布时,官方文档只是让你将编译器放在一个脚本标签中。最复杂的部分在于人们意识到我们有这些深度嵌套的组件,需要通过多层冒泡机制才能修改某些内容(Facebook 当时说“天啊,我们当然使用全局状态”,社区问“我们能用你们用的东西吗?”,他们回答“哈哈,不行。”于是大量社区库开始涌现(或者直接使用 window.state = {} … 那些日子真好)。

    也就是说,一如既往,真正的问题是网络从未被设计用于应用程序,而且仍然是一个糟糕的构建应用程序的地方,但“React”作为一个技术栈已经接管了“网络应用程序”想要做的一切。没有任何东西可以改变这种复杂性,除非重新思考整个平台的本质,而遗憾的是,这不会发生。

    • 网络从未被设计用于应用程序

      我并不反对,网络是一个被胶带封住嘴巴的系统,它正拼命试图喊出“它只应用于简单文档”的呼声,却被迫接受又一个CSS预处理器。但公平地说,我从未见过任何用于构建人机界面应用程序的系统不是架构灾难。这样的系统比比皆是,甚至在你不曾预料的地方。每个游戏引擎都内置了用户界面元素系统,而每一个都难以使用,以至于常见的抱怨是无法将它们构建为网络应用程序(我迫不及待想看到有人将网络引擎集成到虚幻引擎中,让它再多一个糟糕的理由!) 也许问题在于,前端UI本身就是一个天生复杂且糟糕的东西,试图用这些垃圾构建出坚固、可靠、易于理解的模块,不过是徒劳无功,我们最好还是学会在随意堆砌时找到合适的倾斜角度。

      • 有些游戏的UI实际上是用HTML、CSS和JS构建的,就是出于这个原因。

        • 不过需要澄清的是,这种情况通常受到极大限制!完整的浏览器对大多数游戏的帧率要求来说过于笨重,因此行业中常见的是经过优化的CEF或WebKit分支,这些分支专为良好的帧率和易于嵌入而设计,通常会剥离许多API,代价是放弃对某些功能的支持,比如React。(还有一部分完全放弃HTML,仅使用CSS和JS。例如:Source中的Panorama)

        • Flash用于游戏UI曾经(现在仍然?)是一种常见做法,哈哈

        • 直接用Canvas自己搭建GUI系统吧,哈哈

        • BeamNG让我联想到,而这正是我唯一讨厌该游戏的地方。

      • 也许问题在于前端 UI 本质上就是一个无尽复杂且糟糕的东西,试图从中构建出坚固、可靠、易于理解的模块可能是一项徒劳无功的努力,或许我们应该学会在随意堆砌时掌握正确的倾斜角度。

        这是文章作者得出的结论。我在阅读过程中陷入了如何改进UI的思维漩涡,最终也得出了相同的结论。

        界面设计他妈的太难了。编写代码难,设计难,就连汽车仪表盘上静态的指针都难!想象一下,如果仪表盘要根据某个经理的喜好每两周就更换一次!

        尽管如此,我们所拥有的以及我们如何向他人传达意图仍然令人惊叹。也许我们可以做得更好。但我认为这是一个不断变化的目标。对一代人有效的方法对下一代可能无效。

        • 过去几周我一直在用C++开发一款游戏。我使用了SDL库,但其他部分都是从零开始实现。我设计了一个支持基本按钮和拖放功能的自定义UI。两周前我决定重构UI以支持嵌套视图。经过四次糟糕的尝试后,我终于找到了一个自己还能接受的方案。

          迭代1只是一个基本的面向对象UI。它易于使用,但功能极其有限。

          迭代2仍然是面向对象的。它实现了一个难以维护的信号/槽系统。信号/槽用于组件之间的通信。调试起来非常困难。

          迭代3也是面向对象的。信号/槽系统被有向无环图(DAG)取代,用于单向传播数据流。虽然更容易调试,但编写起来却困难得多。

          迭代4是函数式的。它易于调试,易于构建,但性能远不及前几版。考虑到界面简单且C++本身具有良好的基础性能,这是我选择的方案。

          我的用户界面系统非常基础,目前只支持基本组件。不过,我还是对前几版的质量感到惊讶。即使是最新版本也不算出色。它只是勉强够用,我现在不想再投入更多时间去改进它。

        • 各种界面的一个重要特点是,人们的需求并不总是相同。

          可能是非常细微的差异,比如我希望这两个按钮放在不同位置。或者汽车显示公里数还是英里数。

          也可能是根本性的差异,即他们对该界面的核心需求完全不同,几乎没有重叠。这在软件领域尤为突出,比如大型企业使用的软件。公司内部不同岗位可能因工作性质略有差异,对软件功能的重视程度也各不相同。在一定程度上,你可以为某些功能设计不同的界面。比如提供不同的仪表盘,或者允许用户以自己喜欢的方式进行设置。

          但你做得越多,就会有越来越多的人希望软件能开箱即用,或者不要太复杂。

          一个有趣的例子是《流放之路》中的搜索框。人们觉得它难以使用,因为它支持正则表达式。如果你只搜索一个东西,还算简单。但如果你想搜索多个东西,很多人期望能做“这个+那个”之类的事情,但它并不支持。他们试图弄清楚正则表达式,但觉得太复杂了。

          与此同时,那些了解并使用正则表达式的人认为它非常强大,不想放弃它。

          如果这两类用户比例极不均衡,甚至接近对等,就会成为问题。如果一半用户热爱它,另一半讨厌它,或者更糟糕的是大多数人讨厌它而一小部分人极度喜爱它。你该如何处理?

          • 在搜索框中添加一个复选框,允许用户启用或禁用正则表达式。

            • 基本上就是 IntelliJ 的搜索功能

            • 虽然操作简单,但现在每个使用该功能的指南和教程都必须添加一条说明,确保复选框已勾选。当然,人们不会阅读这些说明,而是会留下负面评论,称指南无效或过滤不正确。

              它仍然可能是一个不错的选择,但我的观点仍然成立,即不可能找到一个适合所有人的解决方案。而且很难找到一个适合大多数人的解决方案。

        • 发生了什么?我习惯了被后端开发人员告诉前端很简单,我很快就会失业。

          • 学习Java,编写一些代码。当出现 bug 时,后端开发人员询问问题出在哪里,就把责任推给 AI。

      • 我仍然认为Flutter是迄今为止最好的尝试,但我不是前端专家。只是一个使用过多种语言和框架的全栈开发者。不幸的是,它正在停滞不前,而且很难找到相关工作。我在上一份工作中参与过一个Flutter移动应用的开发。

        • Flutter看起来像是一个没有试图重新发明轮子的平台。阅读Flutter代码比阅读React代码要容易得多。不幸的是,它没有庞大的用户基础,谷歌可能会像往常一样放弃它。(首先它必须变得稍微成功一些,然后他们就会切断我们的支持…)

          • Flutter比React更易于阅读?这取决于你的背景是否是面向对象编程(OOP)

          • 哦,我不同意。这就像有人设计了一个基于无限嵌套C++函数参数的框架。我就是不喜欢这种风格,它太难保持代码整洁了。

          • 根据谷歌的数据,30%的新iOS应用使用Flutter。这可不是小数目

            顺便说一下,我们刚组建了一个新团队,学习了Flutter,不到一年就重写了我们的原生应用并上线了。虽然这不是世界上最复杂的应用,但我仍然认为这很了不起——我们中没有人是有移动开发经验的。

            https://developers.googleblog.com/en/celebrating-flutters-production-era/

            • 在苹果应用商店中… 2024年所有追踪的免费应用中近30%

              太棒了!!这无疑让Flutter成为开发iOS应用的顶级技术之一(可能仅次于Swift UI,我猜)?

      • 我认为UI本身并非天生就是无法解决的混乱。我认为在很多情况下,我们所有的思维模式和“惯常做法”在核心层面都存在偏差。

        就连这篇文章也轻描淡写地指出“潜在无限输入”之类的问题?为什么?为什么软件设计师,尤其是UI设计师,会做出如此糟糕(但技术上准确)的假设,比如“无限可能的输入”?

        一个示例应用程序会接收用户提供的邮政编码,并返回附近咖啡店的列表。这当然意味着我们还需要对用户输入进行验证和清理,以避免出现漏洞或注入风险,因为这就是处理未知用户输入时应该做的事情。或者,只接受5位数字,对于无效的邮政编码返回空值即可,无需担心所有验证和清理工作,因为这些已经不再相关。

        钻头设计师不会担心有人试图用它当锤子用。我们也不应该担心。

        • 简而言之 我希望在编码时可以假设钻头就是钻头,但由于层层MBA地狱,我不得不将钻头视为整个机加工车间。

          我认为问题的一部分是,质量保证团队因为尝试将钻头当作锤子使用而创建了错误报告。管理层看到后,要求我们处理钻头被当作锤子使用的情况,并为钻头被当作锯子或其他工具使用的情况编写代码,以防万一。如果管理层决定围绕无效的钻头使用制定KPI/OKR,那可就麻烦了。

          我最近经历过一场争论,两个团队同意的JSON规范是“value”键只能包含整数值。这就是联系。质量保证团队在测试时使用字符串、布尔值、数组、对象或其他非数值类型的JSON类型时,就打开了问题。

          我与我们的 QA 团队讨论了这个问题,并解释说我们已经定义了规范,出现错误并停止进一步处理是预期行为。从业务角度来看,这些测试输入毫无意义。我们无法针对其他类型编写代码,因为业务方已向我们保证只会有一种类型。他表示理解,但几层管理层要求 QA 测试一些本不应发生的情况,以便我们能更好地展示代码的测试质量。

          • 如果你能编写一个 JSON 模式并让其他团队同意,那么你可以使用该模式来验证请求,任何不符合模式的类型都会被定义为验证错误。

            至少在我过去的经验中是有效的

            • 我们可能应该在代码中更好地定义模式以进行验证。我们使用Java,并使用Jackson进行所有JSON映射,它很好地实现了模式建模,并在无法将JSON映射到对象时抛出错误。事后看来,我的设计中有一部分是错误的。我们合作的团队定义了约30个模式,这些模式最终需要处理,而每个模式中90%的数据是相同的。剩下的10%不同部分可能会有我们关心的一个或两个键,具体取决于模式类型,所以我将该根键保留为JsonNode,并手动定义了模式类型(作为JSON中的值)与路径之间的映射,以及如何将其转换为Java对象。虽然最初被要求处理30多个模式,但在项目尾声时我才得知其中仅有1个模式已被正式标准化,其余均处于“进行中”状态。

              我们还与消息中间件合作,因此如果收到无效消息,我们不能简单地向上游返回500错误,也不可能将无效消息传递给下游,因为下游与第三方工具集成,该工具无法处理此类情况。我们能做的最好办法是设置监控和警报,如果监控触发警报,则通知上游。

            • 是的,在我们的情况下,发送方和接收方都会根据与 API 文档一同托管的 JSON 模式对请求进行验证,但我想在你的情况下,达成这种共识更多是政治问题而非技术问题。

              这种架构让我联想到要么是极端规模(因此必须接受缺点以应对负载),要么是极端系统架构师。

            • 这是疯狂的负载。我目前在汽车制造领域的车载信息系统工作。在初始概念验证部署中,我们要求单个实例能够处理10k tps,且最大允许延迟为100ms。我希望我能找到我记录的实际数字,但至少在当前项目中,我正在处理的单个工厂每天产生约20-30百万条消息 (仅限于我当前项目所需的数据集。系统其他部分处理的消息量甚至更大)而类似的工厂有30家左右。我认为数据量与我在FAANG公司处理的量相当,甚至更多。

          • 我不明白。他们可以进行测试,发送错误类型的消息并获得错误响应。他们是否期望系统仍然正常工作?还是说系统对错误类型有非常糟糕的反应,这将是一个真正的问题?

            • 该系统构建的方式是:消息会从消息中间件接收,并通过另一项服务进行有效性验证。如果根据另一项服务的配置,格式正确的消息被标记为无效,则该消息可被发送到死信队列(DLQ),或带有一个标记(指示其无效)转发给下游团队。如果配置服务更新,DLQ中的消息可被重新处理为有效。不符合规范的消息会被记录为错误并丢弃,注明其不可处理。质量保证(QA)团队希望这些不可处理的消息被发送到下游或死信队列,尽管这样做会导致下游故障。由于他们的测试套件是这样设计的,他们需要一条消息来进行断言。由于消息被丢弃并记录,导致测试中出现异常。

          • 我的意思是,我理解应用程序在有人违反合约时不应崩溃的必要性,但如果我理解正确,它已经处理了错误。那么质量保证部门到底想要什么呢?

        • 除非主要操作系统供应商合作创建一个共同的API,该API能够提供足够的功能来实现合理的日常应用程序,否则浏览器(VHS)将默认获胜。鉴于他们可能永远不会这样做,那么最糟糕的应用程序平台将继续胜出。

          当然,还必须考虑这样一个事实:可能有相当多的人支持当前趋势,因为他们希望原生应用程序消亡(连同个人计算革命一起),让我们回到由大型企业完全控制的时代,我们不得不向它们支付租金。

      • 好奇你是否尝试过Godot?我对它的UI组件印象深刻。我主要从事网页开发。

        (总体上同意你的观点)

      • …网络是一团糟,就像用胶带封住一个系统嘴巴,而这个系统正拼命试图喊出它只应用于简单文档,却被迫接受又一个CSS预处理器。

        我喜欢它。我猜你的代码也写得同样优雅。: )

        也许问题在于,前端UI天生就是一个复杂且糟糕的东西,试图构建它总是困难重重…

        确实如此。

        但看起来这么简单,对吧?与我们后端所做的相比,那些真正复杂且至关重要的事情,UI 显得如此简单,对吧?甚至非常基础,对吧?对吧?: (

        它总是比最初看起来难得多,每个问题提供的解决方案都会引发另一个问题,以一种累积的方式,直到我们得到比我过去使用过的某些操作系统更大的网页。

      • 是的,UI 是一场噩梦——当它几乎被牢牢固定在特定硬件上,拥有特定屏幕/分辨率/方向且毫无变动时,情况还算简单,但即便如此,要覆盖所有场景也绝非易事。

        如今,你的 UI 必须兼容 32 英寸显示器、$200 的智能手机、视网膜屏 iPad,且这些设备可能处于纵向或横向模式……

        尽管如此,网页和CSS尤其像用胶带和绳子拼凑的摇摇欲坠的积木塔。

      • 几十年来,UI设计一直采用面向对象的方法,但这种方法本质上存在缺陷,因为按钮本质上并非矩形。由于这种愚蠢的层次结构,UI会做出各种令人困惑的操作,比如理论上可以在复选框图标的框架内嵌入VLC播放器。

        现代网络通过扁平化对象层次结构消除了这一问题,但同时也引入了全新的一系列问题。

      • Flutter 还不错,甚至可以说很好

      • 等你看到新的 BeamNG.Drive UI 时就知道了!(剧透:它很糟糕)

      • 我迫不及待想看到有人将网页引擎集成到虚幻引擎中

        我们最终会在每个游戏中看到Electron,对吧?

        • 可能恰恰相反

        • 不,可能只是网页视图。Unity 也有 OneJS,允许你使用 Preact 构建 UI,但它底层使用的是 UI 工具包,而不是网页引擎(这遗憾地限制了你能做的事情。例如,经过 7 年,Unity 仍然没有为 uss 添加 z-index,所以如果你需要这个,那你只能自认倒霉了)

          据说他们还会添加更多框架,包括我最喜欢的Svelte,所以一旦实现,我可能会切换到那个。Unity UI真是让人头疼

      • 另外,他们不得不重新制作大量旧的近期重制版资产,因为其中很多都是基于Flash播放器

      • 我不反对,网络就像一个被胶带封住嘴巴的系统,它拼命想喊出自己只应用于简单文档,却被迫接受又一个CSS预处理器。

        10/10的比喻。没有异议。

      • 我不得不坦率地说,在Godot游戏引擎中构建用户界面其实挺不错的。我并不是在否定你的观点,但我个人非常喜欢使用它。

      • Unity支持直接使用React进行游戏UI开发。2023年发布的《Cities Skylines 2》其整个UI均采用React组件实现。

        • 我无法想象它的性能会有多差

        • Unity 默认不支持这一点,对吧?我知道资产商店里有相关资产,但据我所知,它只是在 Unity 运行时旁边启动一个 JavaScript 后端,而实际的 UI 通过将你的 React 组件转换为 Unity 的 UI 工具包来实现。

          (《城市天际线》可能没有使用那个资产,但他们可能自己开发了)

    • Web 是一个优秀的 UI 平台。它是最好的。尝试构建一个 WinForms 应用或 WPF 应用,然后与 React 应用进行比较。

      只是 JavaScript 生态系统逐渐演变为复杂和疯狂。

      我开始使用 Angular 并结合信号,对我来说,它看起来很棒。

      • 我同意它是“最佳”的,因为它有一个压倒一切的杀手锏功能,即非供应商锁定的跨平台开发和分发。除此之外,它从头到尾都是一团糟。

      • 最佳?

        我认为Swift在iOS/macOS上,以及Kotlin Jetpack Compose在Android上,不仅提供了更好的开发体验,还提供了更强大、更丰富的用户体验,这是只有原生应用才能做到的。

        一个高质量的原生应用,尊重平台的设计规范并利用平台的原生能力,当它被精心打磨时,即使是最好的网页应用也无法与之匹敌。

        • 我从未真正理解Compose,我曾尝试用它做一个项目,结果后悔没直接用XML。不过我确实很少在Android上工作,只是偶尔做些玩具级别的业余项目(这是几年前的事了)

        • 这完全是夸大其词,我完全不知道自己是在用WebView还是原生应用

          不过我一直通过浏览器使用旧版Reddit,自从第三方应用事件后,也许我不是一个好的评判者

      • 这是最好的

        自网络出现以来,世界并没有停滞不前。我个人认为Flutter要好得多——两者根本不在一个水平上

        • 网络也没有停滞不前。新的网络技术不断推出。有些只是在追赶,有些则是真正的进步,比如网络组件技术(尽管存在痛点)、模块、WebAssembly、画布、网络音频API。网络开发环境一直在不断完善,并且将继续如此。

          如果需要说明——我明白网络技术仍然是一堆API以历史决定的方式拼凑在一起。但你的陈述暗示我们仍在将整个HTML文档作为字符串发送给用户的Netscape浏览器。当网络成为现实时,包括网络本身在内,没有任何东西停滞不前。

      • 网页是一个优秀的用户界面平台。它是最好的。试试构建一个 Windows 窗体应用程序或一个 WPF 应用程序,然后与一个 React 应用程序进行比较。

        我对 Windows 窗体和 WPF 并不特别热衷,但它们缺乏 React 所拥有的海量功能,而 React 则充满了尖锐的边缘。如果说有什么不同,Windows 窗体至少是简单的。WPF 有它自己的丑陋之处,但 useEffect 和 useState 到底是怎么回事?这种糟糕的抽象让我无言以对,有人居然能看着它说“这没问题”。它他妈的就有问题。React 将呈现与逻辑混杂在一起,使得创建极其混乱的应用程序变得非常容易。

        我们从 Svelte 切换到 React,是因为我们聘请了几位顾问,他们声称可以用 React 更快地开发应用程序,因为他们之前从未接触过 Svelte。现在我们手头有一个巨大的烂摊子。在我看来,由于 React 的设计方式,React 应用程序非常容易崩溃。

      • Web 是一个优秀的 UI 平台。

        它不是。它是目前最糟糕的 UI 平台。基于文本的 DOM 从未设计用于富客户端应用程序。布局完全是事后考虑(CSS 网格和 FlexBox 并未解决任何问题),性能也极其糟糕。

        我宁愿使用桌面 GUI 工具包开发的应用程序,也不愿使用 Web 应用程序。

        Web 应用程序受欢迎的唯一原因是部署成本为零。

    • 说实话,我记得当 Backbone 和 Marionette 推出时,客户端应用开始流行,当时 everyone 都努力理解这些技术。令人惊讶的是,10 多年过去了,情况反而更糟了。

      • 我所在的公司直到最近还在维护几个 Backbone 应用。React 要好得多。

        • 是的,我不知道这里的人在想什么,当他们认为jQuery或Backbone“好得多”的时候。我用过jQuery,用过Backbone和Marionette,也用过AngularJS。它们都有各自的问题,都让人头疼。我讨厌webpack,但现在有了vite和其他更好、更稳定的选项,尤其是随着TypeScript的出现,我认为网络开发正处于有史以来最好的状态。

          • 是的,开玩笑说自己制造的工具链地狱,或者说React有点像前端PHP,确实很有趣。但我发现自己还是经常使用这些工具。

      • 作为过去15年几乎在所有主要框架上都进行过开发的从业者,现在的情况绝对不是“更糟”——至少从开发体验(DX)的角度来看是这样。虽然在某些地方添加了抽象层来隐藏复杂性(或者至少试图这样做),但现在启动一个项目时,无需花费数天时间配置框架和构建环境,这要容易得多。不过话说回来,现在你可以做的事情比以前多得多,这可能会让人感到不知所措,但其中许多功能其实并不必要,可以在需要时再添加。

        • 至少从开发体验(DX)的角度来看

          终于说出实话了。虽然开发体验可能有所改善,但用户体验(UX)却一团糟。

    • 就像往常一样,真正的问题是网络从来就不是为应用程序设计的,而且仍然是一个糟糕的构建应用程序的地方

      完全正确。网络应用程序“获胜”的唯一原因是零部署。在其他方面,使用网络技术构建富客户端应用程序远不如一个好的桌面图形用户界面工具包。

  2. 这怎么可能通过代码审查?你基本上在使用全局变量……

    既然自上而下和侧载方法都糟糕,那解决办法是什么?我真的不知道。……

    问题解答:它通过代码审查是因为没人知道更好的实现方式。指责别人愚蠢却不提供更优方案,这只是幼稚行为。

    “量子力学与广义相对论不相容!那些愚蠢的物理学家怎么让一个不完整的理论成为标准理论。白痴!”

    • 有6000个React替代方案,但没有一个成功。最有希望的方案是“和React一模一样,但我们用信号悄悄更新,而不是重新渲染到阴影DOM”。

      • React使用虚拟DOM,不是阴影DOM

      • 秘密更新信号听起来像是“幽灵行动”。量子力学观点成立 /s

      • 我用Preact构建了上一个小型项目。与React相同的语法,但没有冗余代码。我喜欢它。

      • 有6000个React替代方案,但没有一个流行起来。

        你经常听到React开发者这样说,但这完全错误。是的,React是最流行的,但这并不意味着没有人使用替代方案。事实上,我们在工作中使用Angular。

    • 类和继承所有权

      这并不是什么神秘未解的问题。自2000年代以来,这个问题在各处都被讨论过。

      当你对面向对象编程怀有非理性的仇恨,并想要废除类层次结构时,问题就出现了。

      • 想要废除类层次结构。

        马克思主义导向编程

      • 类和继承与该部分讨论的问题无关。

        “你的意思是任何组件都可以使用应用程序的任何状态?更糟糕的是,任何组件都可以触发状态变化,而这种变化又可以更新到其他任何组件中。”

        类和继承无法解决这个问题,因为UI中的一小部分状态可能会出现在许多组件中,而这些组件之间通过类层次结构没有任何关联。

        • 坦白说,只要你的数据作用域和语义清晰,这根本不是问题。

          一个名为“isPlaying”的视频作用域布尔值可以存储在代码库的任何位置,只要满足两个条件:(1) 访问它需要正确的视频ID,(2) 当布尔值改变时,UI会自动更新。

          在70年代,对用大括号包裹变量作用域的执着是有道理的,但在现代依赖注入框架中就没那么有意义了。

      • 类层次结构很糟糕。你无法设计一个需要从两个父类继承才能获得一个根据复选框状态播放不同视频的复选框,或者在滚动区域中以圆形排列的单选按钮。

        90年代的遊戲設計中,遊戲物件會進行渲染,因此你可以繼承它來創建動畫遊戲物件,但你還需要一個帶有物理屬性的遊戲物件,而腳本編寫者只是想要一個不渲染任何內容、僅用於存儲變量的遊戲物件!

        到2000年代,“是”模型已被“有”模型取代。因此,一个游戏对象可以拥有绘制模型、物理引擎、碰撞检测、移动能力、动画能力、粒子系统、脚本或一组变量,但并不一定必须拥有这些。每个都是一个独立的类,通过引用保存在主类中的地图/数组中

        • 很可能,你应该优先选择组合而非继承。一个具有某种行为的事物并不需要由该行为来定义,它可能更适合拥有管理该行为的组件。

          • 完全正确。这就是“拥有”模型,通过组合实现,尽可能减少继承,甚至不使用继承。

          • 这很有趣,因为虽然这是事实,但我从未见过有 UI 框架这样做,我认为可能是因为这会让框架更难学习和使用。你不能只是想“我想要一个输入框”,而是必须先考虑你想要的行为,然后将它们组合成输入框。

            • 典型的UI不需要像游戏那样做到完美。游戏对象具有更复杂的逻辑(交互、物理、运动、声音、图形),而且所有内容都需要每秒重新计算60次。

              UI通常只需要以下方式:* 返回描述如何绘制它的对象(HTML)* 响应用户操作* 将数据传递给其他组件

              其余部分较为罕见,且可以采用任何方式实现

            • UI 的复杂部分在于如何正确布局 HTML,以便浏览器能美观地渲染,而这无法通过代码解决。这就是为什么正确居中一个 div 被认为是个难题

    • 指责他人愚蠢却不提供更优解,这只是幼稚的行为。

      https://mbrizic.com/blog/react-is-insane/#the-how

      作者承认自己不知道如何解决这个问题,但提供了一些初步的思路。这并不显得幼稚,反而像是开启讨论的好起点。

      • 我并不认为这些是解决所提问题的合法方案。

        该博客对“React架构糟糕”的解决方案是“设计师需要改变设计方式”和“系统需要减少使用React”。这根本不是对所提问题的解决方案。这就像你抱怨刀具制造商的刀具变钝,然后说也许解决办法是少做点饭。

        如果这些是他的建议,那么整篇文章的立论基础就是错误的。React 并非“疯狂”。他只是所在团队在滥用它。

  3. 这真的只是技能问题。不是说 React 不好,但显然你并不真正理解它。

    这是通过使用 React 钩子将状态“侧载”到组件中来解决的。我还没听说有人抱怨过这个,但你们是认真的吗?你是在说任何组件都可以使用应用程序的任何状态部分?更糟糕的是,任何组件都可以触发状态变化,然后在其他组件中更新。

    我真的不知道你在抱怨什么。默认的 React 钩子(useStateuseReduceruseContext)都没有这样做。状态总是从上到下流向组件。事件总是从下到上流。钩子并没有改变这一点。

    你基本上是在使用全局变量

    不,你没有。

    我甚至不想提人们如何将组件称为“纯函数”,却在其中使用钩子作为微小的有状态黑盒。考虑到它们的可组合性,这更像是层层叠叠的微小有状态黑盒。

    请注意,React 钩子实际上与 效果系统 类似。这无法在 JavaScript 类型系统中表达,但 React 组件函数并非普通函数。它需要在适当的上下文中调用以提供这些副作用。在这种情况下,函数始终由 React 调用,并提供必要的状态。

    你在使用一个“副作用”钩子来初始化组件?好吧,如果你必须从那里进行 API 调用,我同意那会是一个副作用。但那个 API 调用……它……它也设置了状态。所以一个完全无害的“副作用”钩子实际上管理了组件的状态。为什么没有人谈论这有多疯狂?

    你用错了。你不应该使用副作用来初始化组件。这甚至没有意义。组件在副作用执行时已经初始化了。

    [一些图片随后] 这是我从一家最近以数千万美元收购的公司生产应用中提取的代码。

    由于上下文信息被大量删除,现在已经无法继续跟踪了,但看起来你可能握持方式有误。如果你从后端获取的数据依赖于前端状态,请使用react-queryswr等库来明确表达这种依赖关系。不要使用useStateuseEffect来实现这一点。

    “模式”

    我甚至不知道该从哪里开始。HOCs(高阶组件)早在十年前就已过时。事实上,正是钩子功能让它们销声匿迹。

    渲染 props 的使用非常有限。而且它们其实并不复杂。它本质上就是一个接受参数并返回一些标记的函数……

    自定义钩子。没错,这就是“非常复杂”模式文章中的一节。

    关于 React 有很多值得讨论的地方,但这些都是初级水平的观点。

    • 这真的只是技能问题。

      我经历过的每一个 React 代码库都和这里的所有抱怨一模一样。如果每一个代码库都有同样的问题,那么问题出在库本身,而不是用户。

      做错事情应该是很困难的。

      • UI比编写无状态的CRUD接口难得多,但不知为何行业却把它当成相反的情况。React代码库糟糕是因为前端被交给初级开发者。没有人真正关心架构或强制执行任何标准。

        • 完全同意。我一生主要从事后端开发,使用C#/SQL,第一份工作时用过一些JQuery。重新学习现代前端对我来说简直疯狂。它过于复杂,与服务器端代码的工作方式差异巨大,因此必须成为独立的专注领域和专业领域。有无数种方式可以彻底毁掉你的代码库,而这必须由专家来完成。然而……整个行业在胡说八道,却做了相反的事情。在前端部分变得极其复杂时,强迫每个人成为“全栈开发者”,并将他们的初级员工和海外承包商扔到最糟糕的部分。这在世界范围内的大部分前端代码库中造成了超出任何人想象的灾难。

          在我目前的公司,我非常害怕,非常害怕不得不涉足前端。相比之下,后端基本上是一个已解决的科学问题。这甚至还没有涉及到节点生态系统和项目/包管理的复杂混乱。

          • 前端开发是我的专长,我热爱它。但公司让我做开发运维(DevSecOps),因为找不到人来做这个岗位。虽然复杂,但这100%是问题所在,因为管理层和客户往往将UI视为次要考虑因素——毕竟UI是可视化且易于非技术人员理解的,而服务器端代码则不然。因此,时间表被延长,优先级转向后端工作,因为它“神秘且技术性强”,而UI被搁置一旁,因为“它只需要工作,然后我们可以打磨它”。

            问题是“打磨”和“易用性”不是一回事。但没关系,我现在要回去在Docker容器中实现FIPS标准了。

          • 试试 Phoenix/LiveView,感觉像是为前端开发者准备的

        • 回想起我第一份工作时,作为初级开发者被派去担任前端开发,而后端只有一位资深工程师,但他对前端一窍不通。

        • UI比编写无状态的CRUD接口难得多

          我部分同意,但并非在所有情况下都如此。

          构建一个安全、可靠、可扩展的普通MVC CRUD后端所需的知识范围,远大于构建至少80%的用户界面JS应用所需的知识范围,这些应用由这样的后端提供服务。

          你不需要Spring Boot来驱动像Wordle这样的应用。我喜欢Wordle这个例子,因为它实际上没有后端。

          对于那20%真正复杂的面向用户的产品,它们仍然需要一个在复杂性范围上远超前端的后端。想想Reddit、Facebook、Jira、Figma、Google Sheets等。

        • CRUD 本身并非无状态。在多个同时读写者存在的情况下,确保数据的一致性非常复杂。幸运的是,数据库作为工具消除了 99% 的复杂性。没有数据库,我无法进行后端开发。正是由于这些优秀的工具,后端开发才变得容易。

          • 嗯,没错……没有数据库这样的第三方软件,后端开发会困难得多,但这就像说没有网页浏览器,前端开发会更难一样……这不是一个特别有用的陈述

            • 显然,没有网页浏览器网站就无法运行,所以我们可以把这一点视为理所当然。

              我的意思是,CRUD接口之所以简单,是因为工具很棒。前端开发之所以复杂,是因为工具很糟糕。

            • 是的,我明白这听起来很愚蠢,但同样愚蠢的是,似乎认为后端工程师不需要数据库工具,仿佛他们天生就应该知道这些工具的存在。

              无论后端CRUD操作为何简单,都无法否认它在许多方面比前端更简单。

              后端开发的挑战不再是代码,而是基础设施、扩展性和系统设计。

            • 100%。后端开发人员不需要提醒数据库的存在,就像前端开发人员不会忘记浏览器负责渲染一样。你的观点完全没有被理解。

              我也不会说现代挑战仅限于基础设施。当任务仅仅是CRUD时,代码和基础设施都相对简单,前端也是如此。

              但一旦真正的业务规则出现,代码和基础设施都会变得同样复杂:

              • 后端:状态转换、一致性级别、长期运行工作流、未来兼容的模式

              • 前端: 复杂的 UI 状态、性能预算、设计系统漂移、无障碍性、良好的用户体验

              这些难题源于本质上的复杂性:解决当前独特的领域问题,同时为未来演进留出空间。选择正确的技术栈很重要,但捕捉规则的代码同样重要。它们的成败与共。

              我认为争论持续存在是因为,开发小型CRUD应用的工程师有时会使用为解决大型企业问题的工具,然后抱怨一切都显得过度设计。如果你只需要一辆$5,000的旧车,就不要支付$10,000,000的游艇账单。选择与旅程匹配的工具。

          • 数据库在网络出现之前就解决了多用户并发问题。真正的痛点不是“多个读写者”,而是有状态的应用服务器。我们曾将会话绑定到单个服务器,这使得扩展和故障转移变得困难。将会话数据放入共享缓存(带事务,就像数据库一样)解决了这个问题。

            如今,无状态的CRUD API对数百万用户而言已是小菜一碟。后端真正困难的是设计一个能够扩展、容错并保持复杂业务规则清晰的复杂系统。所有棘手的工作都源于对业务领域的理解和选择正确的架构。

            前端也是同样的情况。一个基本的CRUD界面可以从模式中生成,或者使用表单库直接实现。真正耗时的是打造一个丰富、快速且能够持续演进而不变得混乱的用户体验。这涉及组件架构、状态管理、性能调优以及设计系统漂移等问题。

            在两个层面上,困难都源于本质上的复杂性:我们被雇佣来解决的独特领域问题。我们不幸地擅长“ cargo-cult 工程”,在解决方案中引入大量不必要的偶然复杂性,因为我们读到“ FAANG 构建的有趣新工具”,并认为我们需要它们来解决简单得多的问题。

            “在技术或管理技巧方面,没有任何单一的发展,仅凭自身就能在十年内带来生产力、可靠性或简单性方面一个数量级的提升。” ——弗雷德·布鲁克斯,《没有银弹》(1987)

            “软件工程的目的是控制复杂性,而非创造复杂性。”——帕梅拉·扎维

            为了更好地理解这种荒谬性:迪杰斯特拉早在1965年就发表了经典的信号量互斥解决方案,而同一时期,战略家们提出了“相互保证毁灭”作为核威慑概念。我们同时解决了计算机同步和遏制超级大国的问题,然而60年后的今天,我们仍在为线程安全和核战争(见伊朗局势的最新升级)而焦虑。也许我们只是天生就对错误的事情感到焦虑。

      • 如果每个代码库都存在相同的问题,那么问题出在库本身,而非用户。

        人们将React当作框架使用,但它本就不是框架。我同意React代码库往往不必要地复杂,但我不确定我们该如何让工具的制造者为用户未能掌握正确使用方法负责。

        如果有人坚持用螺丝刀而非电钻将螺丝拧入墙壁,你认为螺丝刀制造商该怎么做?只生产电钻吗?

        • 他们打造了一把精美的桃花心木手柄,却要求用户自行组装剩余部分,同时声称这是你永远不需要的最后一件工具。你可以用它打造任何工具,无论是螺丝刀还是锤子,但大多数人最终得到的是一件你从未见过的定制工具,且仅能勉强满足其需求。

          工具具有明确立场往往是好事——React几乎对JSX以外的一切都持中立态度,结果反而更糟。跨代码库的知识几乎无法复用。“了解”React几乎毫无意义,因为代码库往往会自行实现功能,却未充分理解其潜在影响

          • …声称这是你永远需要的最后一个工具

            我使用 React 已有 7 年,从未被误导认为这是事实。你从哪里听说的?

            工具具有明确立场往往是好事——React 几乎对任何事情都持零立场,除了 JSX,这反而使其更糟。几乎没有知识可以在不同代码库之间转移。“了解”React几乎毫无意义,因为代码库往往会自行实现某些功能,却并未充分理解其潜在影响

            在某些情况下,无意见工具确实有益,但其他情况下我完全同意相反观点。这是当前React生态中最大的痛点,而我认为解决之道是使用基于React的有明确立场框架。

            这未必是 React 的问题。正如我上面所说,这不是螺丝刀的错,而是你用它来做需要电钻的工作。

      • 我参与的代码库完全没有上述问题。这主要是因为,就像其他评论者所说,我们意识到前端开发复杂且需要优秀的软件开发人员。

    • 是的,这些关于‘useEffect’的抱怨听起来很刺耳。React 文档中有一整节内容明确指出“请不要这样做”。然而,作者却恰恰做了同样的事情,然后抱怨“React 不好”。

      React 并非万能框架,它确实存在问题。但就像任何技术一样,要有效使用它,你需要对工具了如指掌。我见过很多用AngularJS和Angular 2写的糟糕项目。个人而言,我宁愿面对糟糕的React代码库,也不愿面对糟糕的Angular代码库。

      • 公平地说(也与我之前的回复相关),这些文档直到两年前才存在。这意味着Facebook让社区在这些糟糕的实践中沉淀了四年。

        • 他们不仅让社区沉浸其中,还通过演讲和会议积极鼓励大量使用useEffect。

          这种洗脑行为只有Facebook的开发者才能想出来。

          • React 的整个概念是 Facebook 工程师为了解决他们面临的巨大工程组织/管理问题而设计的,而非技术问题。他们让一些非常聪明的工程师构建并维护了一个极其复杂的框架,其唯一目的是提升全球各地团队和组织中数千名工程师的代码/架构质量。这本质上是一个组织管理解决方案,却伪装成适用于网站的通用 UI 库。

            没有哪个理智的工程师在设计面向单个开发者或小型开发团队的系统/库/框架时,会发明虚拟 DOM、渲染循环、副作用(+Redux)等复杂机制,仅仅是为了服务于自己或其小型团队。

            此外,Facebook之所以内部开发了React,且其在公众认知、专业知识和文档方面最初需要较长时间才能获得 momentum,是因为Facebook并非试图构建界面技术的未来,他们只是在解决一个Facebook规模的人员问题。

            • 我认为你并不清楚“精神操控”的含义。React 的作者,如 Dan Abramov,曾大力推动人们使用 useEffect,随后社区对 useEffect 这一设计缺陷提出了强烈批评。

              如今,他们并未承认错误,反而声称是我们用户在过去五年里错误地使用了该库。

              现在他们又要求我们将 RSC 塞进每一个开放的接口。

              很明显,RSC是一个在寻找问题的解决方案。我不知道你想要多少东西塞进你的接口,但对我来说一次就够了。

              我这么说,是因为我一直使用React的预类组件,而且在我的职业生涯中,我只在网页开发中使用过React。

            • 抱歉,我表达得非常糟糕。我的意思是,Facebook工程师关于React是“正确”的编写应用程序方式的洗脑行为,而洗脑行为的具体部分是,对每一个投诉,无论大小、合理与否,都以“你只是不够理解它”作为回应。他们之所以要进行这种洗脑仪式,是因为整个事情从一开始就不是为了解决任何技术问题,而是组织问题。

              很容易看出RSC是一个在寻找问题的解决方案

              完全同意

            • 啊,明白了。精神操控确实是个不恰当的术语,而你现在用的短语“你只是不够理解它”与另一个常见短语“你拿错了”非常相似。

              我不知道你是否参与政治,但这与那些常以“不意外”或“这就是现实”为由进行射手谬误(而非辩证思考)的评论员如出一辙。

            • 没有一个正常的工程师……会发明虚拟 DOM……

              已更正

      • 是的,这些‘useEffect’的警告确实响了。React文档中有整整一节内容明确指出“请不要这样做”。然而,作者偏偏做了同样的事情,然后抱怨“React不好”。

        也许是因为其他框架没有这个功能?所以,React真的不好吗?

    • > 高级组件(HOCs)在十年前就已经过时了。事实上,钩子(hooks)正是导致它们消亡的原因。

      HOCs 虽然易于理解,但略显冗长,不过作为常规的 JavaScript 代码还是可以接受的。

      如你所言,钩子和效果系统超出了常规 JavaScript 语言范式,而钩子需要 React 在后台进行魔法般的记录工作才能让一切正常运行。

      UI 已经存在了几十年,而 React 带来的复杂性并不存在。高性能的 UI,使用比 React 少得多的资源,已经存在了几十年,而 React 似乎认为这些复杂性是必要的。

      存在如此多使用 React 的错误方式本身就是问题的一部分。对于 UI 框架而言,最基本的功能应该是“我在这段代码中有一个值,需要将其与另一个 UI 元素进行数据绑定”,而 React 却让这一操作比必要难度高出许多!

      • 有趣的是,我对HOCs的看法与您截然相反。在我加入当前公司时,代码库中包含大量旧版HOCs。每次将HOC重写为钩子时,结果都明显更优。

        但我也完全理解其他人可能喜欢HOCs。

        这一点是我认为最有趣的

        存在如此多种使用“React错误”的方式本身就是问题的一部分。

        我同意。我认为这是我对 React 的主要痛点之一。Facebook 之外对 React 的糟糕维护导致了诸多问题。“React 只是一个库”的说法以及普遍的放任态度导致了生态系统的严重碎片化。在那里发明的一些范式(如渲染时获取数据)已经变得如此普遍,以至于人们被洗脑认为它们是好的。

      • HOC 的真正问题在于,你可以用多个 HOC 包裹一个组件,而这些 HOC 注入了相同的属性名称。这意味着每个 HOC 都在相互竞争。

        坦白说,这可能通过命名规范来解决,但钩子(hooks)能解决相同问题并为开发者提供更多控制权

        • 同意,HOC 可能失控。不过据我所记(我已很久未涉足 HOC 领域),TypeScript 至少能防止这种情况发生,即便它没有提供优雅的解决方案。

      • 钩子和效果系统超出了常规 JavaScript 语言范式

        JavaScript 已经拥有函数式编程范式超过十年。当然,它不是 Haskell,但每个 JavaScript 开发者都熟悉函数式编程概念,即使他们不了解类型理论和代数数据类型的正式数学定义,或通过单子建模副作用的具体名称。

        React(其现代版本通过函数式组件和用于副作用的钩子)正是通过其DSL(JSX/TSX)以高层次方式将这些长期确立的范式引入UI和状态管理。

        而钩子需要React在后台进行魔法般的账目管理以确保一切正常运行。

        这就是关键所在。由其他高质量框架(如React)替你处理账目管理,让你能专注于业务逻辑。这就是更高层次概念和抽象的全部意义。

        你知道吗,当你在 Scala 或 Haskell 中编写函数式编程代码,甚至在 Java 或 C++ 中,你所描述的纯数学计算实际上并非以纯数学意义上计算,而是由一个肮脏的小状态机(你的 CPU)在底层执行,但它给人的感觉是与数学定义等价?这是一种抽象。

        • 效果并非 JavaScript 的原生特性,这就是为什么它们在 React 中感觉如此陌生。关于它们使用的众多规则进一步证明了这一点。

          我可以手动在C中创建虚函数表并进行动态分派,事实上多年来已有很多这样的系统被实现,但虚函数表并非C的原生特性!(而且从某种意义上说,从头开始在C中实现虚函数表实际上更强大,因为你可以使用任意复杂的逻辑重新分配函数指针,但这并不是这样做的很好理由!)

          > 但实际上,它是由一个肮脏的小状态机(你的 CPU)在底层执行的,但它给人一种与数学定义等价的表象?这是一种抽象。

          我非常清楚,我是一个从头开始的人,喜欢提醒函数式编程的纯粹主义者,他们所有的数学都是在丑陋而混乱的物理硬件上运行的。:-D

          > 这就是更高层次概念和抽象的全部意义。

          但如果抽象有太多规则,太多可能出错的方式(例如,它是一个泄漏的抽象,或者只是一个精神上复杂的抽象),那么它可能不是解决问题的正确方案。

          JavaScript 尽管存在诸多不足,仍是一种相对简单的小型语言。React 中的对象组件虽然冗长,但从抽象层面来看易于理解。

          为了消除冗长性,引入了一种更复杂的抽象概念。自此之后,人们一直对这一决策的正确性展开争论。

      • 记住我的话,HOC(高阶组件)正以服务器组件的名义卷土重来。

      • 不过,如果我们考虑他们最初解决的狭窄问题,直接使用 jQuery 操作 DOM 确实简单,但几乎无法测试或调试。

    • 作者表现出了最令人讨厌、最陈腐的开发者特征之一——对他们似乎并不理解的事情发表好斗的评论。

      我讨厌与在代码审查和规划会议上表现出这种态度的人一起工作。

    • 这确实是技能问题,但 React 要保持代码库干净且一致需要非凡的技能。我参与过很多 React 项目,代码总是乱七八糟,总是这样。

      我并不特别怪罪 React,因为 UI 状态本身就很复杂。但 React 的重新挂载和钩子机制并没有帮助。

    • 你的回应恰恰证明了 React 有多糟糕。在我看来,这并非技能问题,而是 React 本身过于复杂,问题出在库本身,而非用户。

      • 我的意思是,既是又不是。问题不在于 React 不好。尽管在我看来,它确实很糟糕。

        问题在于,作者甚至无法对 React 的缺陷进行客观分析,因为他们连基础都搞不懂。如果你每次尝试钉钉子时都砸到手指,这并不意味着锤子太复杂。

        在这个比喻中,锤子每敲一百下就会散架,但作者连使用它到这个地步都没到。

    • 我真的不知道你在抱怨什么。默认的 React 钩子(useState、useReducer、useContext)都没有这样做。[传递全局状态]

      Redux 才这样做。

    • 是的,我原本是想批评 React,但做过很多用 C++ 在 Qt 中开发的 UI 项目后……他抱怨的那些问题其实是这个领域固有的。

      如果说有什么不同,React 可能反而更糟糕。你还没真正经历过,直到你的应用因为一个事件处理程序被排队等待对 C++ 对象执行操作,而该对象在事件实际处理前就被 free 释放而崩溃。

      因为我习惯了信号/槽机制,所以看到那个来自生产环境应用的可怕流程图时,我一点也不惊讶。你在某个地方更改状态,而UI中关心该状态的其他部分会自动适应。这并不复杂(不过来自Qt领域的另一个小贴士……除非旧值与新值确实不同,否则不要发出“我已更改!”信号……这在终止更改事件传播时非常重要)。

    • 同意,这是技能问题。

      但“一切都是组件”且函数作为第一类公民的JS语法世界意味着,任何可想象的事物都有n种抽象方式。这导致在缺乏良好领导力/指导的团队中,代码库在数月内变成一团糟。

      JavaScript最大的弱点(也是其优势???)在于其灵活性。

    • React太棒了

  4. 此外,如果你想依赖该状态并在之后执行操作,那么你…你…需要定义另一个依赖于第一个useEffect设置的useEffect。

    这是反模式,官方文档明确指出不要这样做。

    • 正确的做法是什么?

        • 公平地说,直到几年前,React 的文档都是一团糟,所以难怪这么多人被鼓励去做错误的事情。

        • 我可能错了,但我认为文档并未解决用户的担忧。它们警告要避免对“计算”状态使用效果(及其他情况),这没错,但我没看到提及依赖状态变化的效果。在作者的示例中,状态变化触发了实际效果(获取数据)。不过公平地说,在效果中运行“外部同步”代码作为对状态变化的反应,在我看来并不荒谬,反而相当合理。或许我遗漏了什么。我正在努力思考如何简化用户的示例,并同意他们关于操作顺序和依赖层级的问题会给许多 React 代码库带来困扰。也许不要将`houseMapCats`作为状态的一部分,而是将所有逻辑放在一个钩子中,该钩子对`cats`和`houses`的变化做出反应,这可能更容易理解。好奇其他人怎么想。

          • 我可能错了,但我认为文档并未解决用户关切的问题。文档中提到要避免对“计算”状态产生副作用(及其他事项),这一点是正确的,但我未看到关于依赖状态变化的副作用的提及。

            链接页面第二段有一个标题如何移除不必要的副作用。该标题下的第一个要点恰好讨论了这一具体场景。

            请直接阅读文档吧,哈哈

            编辑:还有一个名为计算链的章节也讨论了这种情况

  5. 如果你完全忽视代码设计的理念,那么你可以做出任何糟糕的东西。数据库实际上只是一个全局变量。

    如果做错事情的能力让你如此烦恼,那就坚持使用不具备图灵完备性的东西。

  6. React给开发者留下了太多的自由,这导致了大量糟糕的React代码

  7. 不得不说我同意这一点。我一直在使用Vue,它确实更直观。并非更强大,但当你不再把所有时间都花在 React 上时,这些概念就更有意义了。我对 iOS 和 Android 的 Jetpack 也有同样的看法。尽管它们有(许多)缺点,但它们更容易理解。

    在 React 中,有很多事情你必须学习并接受。在 useEffect 中,返回值是清理函数。我的意思是,为什么。在编写时可能更容易实现,但现在它又成了你必须努力记住的另一件事。其他框架可能使用了独立的事件或两个函数参数,你可以说将它们在同一作用域中关联是优雅的,当你想要清理状态时。但它很奇怪且不明显。它让我联想到PHP,高效、强大,但有点疯狂。别让我开始谈论JSX。

    • Vue确实很好用。它的API似乎是为人类设计,既易于阅读又易于编写,同时在输出和概念上与React基本相同。可惜它没有那么受欢迎。

      • 它其实挺受欢迎的,我不明白它为什么会比 React 逊色。用 Vue 完全可以实现与 React 相同的目标。

        React 复杂得令人发指,设计得过于复杂,简直愚蠢至极。

        “没人讨论它”这句话挺有意思的,因为我几乎已经讨论这个话题十年了。

        • 我曾用Vue搭建过一家公司。我是它的忠实粉丝。但据我所知,它从未真正进入主流企业/商业应用领域。我几乎从未看到过使用Vue的公司招聘信息,基本上我查看的任何类型网页开发岗位都使用React。

          • 我也更喜欢Vue。据我所知,它在中国相当流行。

          • 你可能会感到惊讶,例如GitLab就是用Vue构建的,而且他们一直在招聘。React显然拥有最大的市场份额,但作为一名Vue开发者,我最近看到越来越多的Vue相关职位出现,我认为人们开始意识到它有多么优秀。

          • 如果/当我们扩展业务并招聘第二名前端开发人员时,我们会寻找具备Vue技能的人选,但我预计届时招聘会很困难,原因与之前相同——因为所有人都学习React,因为那里有更多工作机会。

            这是我们在最初选择使用Vue时就非常清楚的问题,但我仍然相信我们做出了正确的选择。对我来说,作为一名偶尔需要参与前端开发的后端开发人员,我一直发现Vue非常容易理解。我不确定如果使用React是否也会有同样的体验。

    • 对于清理工作,如何让一个在销毁时运行的函数能够轻松访问创建时的变量?你可以像Vue一样使用onMount和onUnmount两个钩子,但这样就需要手动赋值来共享变量。

      由于每次挂载都对应一次卸载,你必须一直这样做,这似乎比一次性学习API要麻烦得多?

      • 这就是我的观点。这虽然是个不错的捷径,但它破坏了 JavaScript 中函数的概念。你可以看看 Perl 中运算符能做的事情,那里有无数的捷径。我知道你从哪里来,因为我怀念在 Perl 中工作。

        一个函数返回另一个具有非常特定用途的函数是不正常的。这是你必须学习的内容,如果你不习惯的话,看到它会感到相当惊讶。就连“useEffect”这个名字也不够直观。作为一个对 React 还不完全熟悉的人,我每次都得查阅它确切的含义(它在初始渲染后执行,但在后续渲染前或后执行,具体取决于效果的原因)。相比之下,“onMounted”就非常清晰了。

        如果这只是 React 中唯一奇怪的地方,那还好,但事实并非如此。

        顺便说一下,在 Vue 中,每个组件只有一个 onMounted 和一个 onUnmounted,所以这其实不是什么大问题。而且,将你希望在 onMounted 中以同步方式运行的所有内容集中在一起(尤其是因为我们这里专门处理副作用)比有多个独立运行的 useEffect 调用更有意义。

        • JS 函数是一等公民,因此返回一个函数本身并不会破坏函数的概念,而是语言的强大特性。

          它返回一个高度特定的函数,这取决于它的使用方式。对于像生命周期这样的场景,你返回的是下一步操作,我认为这并不异常或反直觉,因为它与返回该函数的上下文相关。这在概念上是否与 Promise 的工作原理有些相似?

          • 你的观点有道理,但我认为 OP 指出的是,这个函数的具体性和目的性本身并不直观,这也是合理的。对我们 React 开发者来说,这只是在回顾时才显得简单,因为我们已经通过反复实践形成了这种行为习惯。

            我本人也做过很多 React 开发,因此很容易忽视这样一个事实:React 经常给开发者一把装满子弹的枪,然后花数年时间试图教导人们不要自掘坟墓。如果他们干脆不发放这些工具就好了。事实上,进入任何 React 代码库,你仍然会看到大量没有清理的副作用,而这些本应被清理。到了一定程度,用户就不是问题了。

            • 我对 React 的了解其实并不多。其中有些方面我还没学会欣赏,比如反向响应模型或 JSX 的看似缺乏表达力,但返回一个清理函数对我来说完全没问题。至于特异性,使用命名函数可以让其意图更清晰。

              忘记调用清理代码是任何框架中都非常常见的错误。我本人也犯过多次这样的错误。

    • 我真的不明白为什么有人会忽视Vue相较于其他框架的优越性。它在可扩展性和易用性上都远胜于React

  8. 今日新闻:本地人完全误解了某件事,并因其认为的本质而讨厌它。

  9. 完全同意……React 复杂得令人发指,坦白说,这与大多数标准企业技术栈类似。Svelte 5 或许值得一试。

    我认为 React、Kubernetes、 99% 的 AWS 服务等,本质上都是那些更热衷于学习工具而非业务领域的人的无意义工作。在 Zirp 时代,公司不惜投入巨资招聘技术和软件工程师,因此这种行为被视为可接受,因为最终一支由 20 名缺乏专注力和兴趣的技术人员组成的团队,也能产出某种程度上有用的东西(尽管不如 20 年前我们用更小团队开发的糟糕桌面应用)。这可能有些夸张,但我认为随着大型企业近期在技术招聘上的吝啬态度,这种趋势可能会逆转。

    • 我基本上不同意你关于第二点繁琐工作的说法。Kubernetes 反映了大规模基础设施运行的真实复杂性,你需要为数亿用户提供服务。只是99.9%的公司不需要这种能力,在大多数情况下使用它完全没有必要。

      • 这其实不是关于用户。而是关于部署的便捷性、可重复性和无需进行服务器维护。可扩展性只是一个“锦上添花”的功能。

        你需要Kubernetes来实现这些吗?绝对不需要。但我敢打赌,你的AWS账单上肯定有Kubernetes的影子,它就藏在那个Lambda的幕后。

        • “别担心,这是完全托管的Kubernetes!”

          但谁来管理这个托管的Kubernetes?
          “哦,这是由我们的无服务器控制平面在Lambda上处理的!”

          但谁管理 Lambda?
          “这是由我们的 GitOps 管道与 ArgoCD 自动化的!”

          但谁管理 ArgoCD?
          “它是通过我们的自愈式 Helm 图表部署的!”

          但谁管理 Helm 图表?
          “它们是由我们的 Terraform 模块生成的!”

          但谁管理 Terraform 状态?
          “ 它存储在带有版本控制和生命周期策略的 S3 桶中!"

          但谁管理生命周期策略?
          “我们的合规即代码框架负责管理!”

          但谁管理合规框架?
          “它被容器化并通过我们的 CI/CD 管道部署!”

          但谁管理 CI/CD 管道?
          “运行在容器中的 Jenkins!”

          那么谁来管理 Jenkins 容器?
          “它运行在具有自动缩放功能的 EC2 实例上!”

          那么谁来管理 EC2 实例?

          “…Dave。Dave 每周二通过 SSH 登录并运行 yum update。”

          一直都是乌龟,直到遇到 Dave。

          • 大胆假设过去两年内已执行过更新命令,或有人仍持有SSH密钥

            • 这让我深有同感。SSH密钥在员工的设备上闲置多年,直到他们离职后,服务器便无法再被访问。这种情况我见过不止一次。

          • 我讨厌自己能理解并感同身受这一点,哈哈

          • 我曾在一家具备GitLab管理一切功能的初创公司工作。流程顺畅得令人惊叹。

            容器注册表、包注册表、Terraform状态,所有这些都由与代码同在一个仓库中的GitLab管理。Kubernetes与Flux的集成也非常顺畅。持续集成系统同样直观易用,既能快速上手,又能根据需要构建复杂的管道。

            我现在的公司使用GitHub,相比之下感觉非常逊色。

        • 我们最近从 Azure Kubernetes Service 切换到了 Azure Container Apps。

          底层是 Kubernetes,但对我们来说维护和管理起来要容易得多。

        • 我完全同意,但有更好的选择可以实现这一点,比如 GCP 的 Cloud Run。

      • 对我来说,Kubernetes也存在许多“首创性”问题。并非指它在该领域是第一个实现的,而是指它是第一个规模如此庞大且试图同时解决如此多问题的系统。时间证明,许多设计决策并不理想,我认为假设中的K8s2可以解决许多痛点。

        • 确实如此,如果你仔细想想,我提到的这三种技术都有这个共同点。

      • 只是 99.9% 的公司不需要那种级别的性能,

        这在某种程度上认同了他的观点。

    • React 为何被称为“繁琐工作”?

      我经常听到这种说法,但对我来说这毫无道理。我从未深度使用过 React,但真的从未觉得它有多复杂:

      • 你可以使用类组件,如果这是你喜欢的
      • 钩子是基于调用时间(即在调用树中的位置)来确定状态的特殊函数。
      • 钩子有时会使用回调函数,而回调函数会使用闭包。但闭包本身并不难理解。

      人们觉得 React 还有什么难以理解的地方?RSC?

      下面有条评论说有人对 useEffect 回调函数的返回值是函数感到困惑。说实话,我真不明白这有什么难的。

      我不是在炫耀。我并不认为自己在 React 方面有多“厉害”。顶多算一般般。对我来说,那些在 JS 领域工作的人无法直观理解一等函数,这似乎很奇怪。

      但我认为这才是真正的问题,人们不理解语言本身,强迫自己继续摸索却没有掌握基础,然后怪罪 React。

      • 我编写过几个ATL COM客户端应用,人们根本不知道那有多难哈哈。React确实很酷,其响应式模式能节省时间,而钩子让函数式编程变得轻松。

      • 我认为React本质上是另一门需要学习的编程语言,所以我能理解有些人会问:“为什么我需要学习一种全新的编程范式,仅仅是为了渲染一个简单的网页?”

        如果你只是在开发小型网页应用,或者交互性较低的网站,React并不那么有用,而且它比像JQuery这样的工具复杂得多。因此,我能理解人们为何会质疑它成为网页开发领域的“默认选择”。

        React的价值真正体现于代码库规模庞大,或开发交互性极强的网页应用时。在这些场景下,我认为React表现优异,值得深入学习其复杂细节。但对于一个小型网页应用?或者一个没有复杂功能的大型网站?HTMX 或 JQuery 可能要简单得多。

        我认为很多反对意见来自于人们意识到,很多网站并没有 React 试图解决的问题,也许更简单的替代方案应该成为首选,而不是 React。

        • 说实话,人们在不该使用 React 的地方使用它,这不是 React 的问题。我见过有人在不该使用 C# 的地方使用它,因为那是他们唯一会用的语言。那段代码很糟糕,难以维护。这不是 C# 的错,而是那个只会一招的人的错。

      • 似乎很奇怪,大家都明白一个对象可以返回另一个对象,而这个对象上有多个方法,但他们却难以理解返回一种特殊类型的对象,这种对象只包含一个方法。

      • Web开发门槛较低

      • React基于JavaScript构建,你还在奇怪为什么人们讨厌它?

        ECMAScript是为修复一个在10天内创建的语言而制定的规范。

        • React 中与 JavaScript/ECMAScript 语法相关的特有的内容并不多。

          类、继承、闭包和一阶函数在许多其他编程语言中都能找到。

          因此,我并不认同“被三个等号污染”的论点,这在我看来有些敷衍。

          • 是的,想象另一种语言中类似 JSX/React 的东西并不难。

            哦,等等,看,这是 Elm,一种与 React 类似的函数式语言!虽然它编译成 JS,但它不是 JS。

            https://elm-lang.org/

            从指南中的第一个示例:

            “` view model = div [] [ button [ onClick Decrement ] [ text “-” ] , div [] [ text (String.fromInt model) ] , button [ onClick Increment ] [ text “+” ] ]

            “`

          • return ()
            
        • 我讨厌Java胜过JS

          • JS拥有Java的语法,这是其创建时的规范之一。

            • 哈哈,不,它们完全不同,JavaScript从未在任何有意义的方面与Java相似。

            • 你说得对,哈哈,这些负面评价是怎么回事。

            • 那些认为大括号和var关键字是“有意义”的相似之处的人吧……

            • [删除]

            • JavaScript。没有类型,三等号,特定函数赋值给var语法。

            • 你说得对。我真蠢。看起来相似,但绝对不兼容。

            • 这种相似性只是因为它们都是C风格的,这作为比较标准实在太低了。

            • 网景告诉JavaScript设计师,它需要模仿Java。像你这样的人连维基百科都懒得查。

              但那些只用过C语法的人,比如JavaScript、Java、C#……根本无法理解这一点。

            • 快点,用其他范式吓跑他们。

              视图模型 = div [] [ 按钮 [ onClick 减小 ] [ 文本 “-” ] , div [] [ 文本 (String.fromInt 模型) ] , 按钮 [ onClick 增加 ] [ 文本 “+” ] ]

            • 你错了,JavaScript 是一种基于原型的语言,面向对象编程是后来添加的。如果你想的话,你仍然可以分析原型链。

      • 我的意思是,当然,你可以使用类组件,理论上是可以的。但在 2020 年之后的 React 应用中尝试这样做,React 社区会把你和你的整个家庭列入黑名单。

  10. 所以,我想他没有在各个组件中注入 Angular 服务,并以与钩子完全相同的方式使用它们(我确实这样做了)。

    React 没问题。你可以用很多种方式做很多事情——但总体来说,它之所以处于当前位置是有原因的。我也可以抱怨一下 RxJS、Observables 和 Angular 中的怪异之处。但最终,两者都能制作出优秀的交互式网页应用。

    不过我可以这么说:当你将 React 与 Tanstack Query 结合使用时,你能获得一些我用 RxJS 和 Angular 从未体验过的魔幻级实用性和交互性。

    • 是的。我差点因为一个项目放弃 React,直到我遇到了 React Query(四五年前它叫这个名字)。终于有了一个务实的方式来处理变异。这大大简化了状态管理。

      • 它确实让 React 对我来说变得更加愉快。在 useEffect 中使用 fetch 或 Axios(更不用说在 Redux 中使用了)是如此令人痛苦——我也会抱怨的 🙂

    • 制作出优秀的交互式网页应用

      自从这些糟糕的、速度慢得要命的JavaScript框架/库出现后,互联网变得糟糕透顶,它们甚至在好日子里也对用户体验毫无价值

      页面从未完全加载,点击时总是跳动,1000个后台Ajax请求,“出错了”却没有明显的重试方式,按F5并试图回到这个组件,后退按钮和历史记录都被破坏

      “但但但,如果正确实现的话是好的”

      它们都是垃圾

      • 自从Ajax出现以来就是这样了,哈哈。我知道这很难接受,但如果你在处理这些问题时遇到困难,那你就是个不称职的开发者。

        • 我指的是现有(热门)网站的用户体验,而不是我个人的开发经验

          • 那么这适用于那些无法正确实现它的开发者。

            • 到了某个阶段,我不得不审视这些工具,因为世界上几乎每个团队都无法正确使用它们

              也许这对普通前端开发者来说太复杂了

              但我想我们仍然需要设计一个更易于正确使用的框架(可能已经存在,只是不够流行)

            • 工具无法在不牺牲开发灵活性前提下解决你提到的问题。

            • 也许必须牺牲一些灵活性

              让我们从每天回到办公室开始

            • 哦。你就是那种人。

            • 不,我讨厌那种破事

      • 完全同意

      • 不过,这些问题有一半甚至与使用框架无关。一个从后端和其他来源加载大量信息的网页应用,仍然会在后台产生大量请求。糟糕的开发者仍然不会提供良好的错误处理界面。在没有框架的情况下,单页应用(SPA)中管理后退按钮和历史记录也可能很困难。

        • 没错,问题出在网页本身,但随着这些框架与“网页开发”画上等号,SPA风格的网站正变得比应有的频率更高,即无论是否使用框架,开发者都不应默认干预用户的后退按钮和历史记录。直接给我一个网页就行。

          • 我同意它们比必要时更常见——但也有大量需求是针对超越单纯网页的工具/应用。比如我现在甚至在网页浏览器中做CAD设计。基于网页的程序和工具数量惊人,其中很多都非常实用

          • 这让我觉得很有趣,因为另一半时间我听到的是相反的抱怨,说React正在杀死单页应用程序(SPAs)。

      • 我怀念网页主要只是文档的时代,真正的网页应用被封装在Java小程序或Flash模块中。然而,由于Adobe和Oracle在安全问题上未能达成一致,我们最终只能在JavaScript中运行网页应用,并让网页完全实现交互式。

      • 你更喜欢jQuery吗?

  11. 当然,大多数抱怨都针对React Hooks。这是一个糟糕的主意。我几乎可以肯定有人觉得“无类”听起来很酷,于是开始实施。

    在我的职业生涯中,使用 React JS 时大多数组件都是类,这是最令人放松的项目之一。当某个参加过 React JS 课程的人开始要求我们将一切改为钩子和函数组件时,情况就糟糕了。

    过度工程化是代码破坏,这就是这里真正的故事。

    • 生命周期事件是 React 的巅峰。componentWillUnmount?太棒了。让我们清理一下那里。而不是这个一站式 useEffect 黑盒,它会抱怨缺少依赖项。

      • 他们根本没有进行组件化,同一关注点的逻辑被胡乱地散布在这些方法中,就像音乐节上移动厕所墙上的粪便一样。

  12. 对我来说,这更多是关于功能组件,而不是 React。哦,让我们使用无状态功能组件。太棒了!哦,等等,让我们通过随机使用会导致状态、内存和引用泄漏的‘use’函数来打破无状态性。哦,不!🫠

    • 哦,让我们使用无状态功能组件。

      哪个开发者曾说过使用 React 的优点之一是无状态性?React 的核心卖点正是其 有状态性 以及如何更新 DOM。

      • 过去十年 React 的重大转变是向 函数式 编程范式倾斜,而非传统的面向对象组件和 React 中的面向对象编程方式。

        函数组件的核心本质上是其 props 的纯函数,类似于值类,即代数数据类型。

        Hooks 是允许你突破 React 的抽象系统,以受控方式执行副作用或实现有状态行为的逃生 hatch。

        在任何函数式编程模型中,控制状态的表示和操作方式至关重要。例如,在 Haskell 中使用单子(monads)来建模输入/输出(I/O)。React 实现了一种称为“效果系统”(https://en.wikipedia.org/wiki/Effect_system)的机制。可以看出,React 的设计经过了深思熟虑,其概念体系与多种形式类型理论和编程语言理论相契合。

        即使在函数组件和钩子出现之前,已有 Redux 和高阶组件(HOCs),这表明 React 一直是一个非常函数式的范式,状态被推送到编程模型中一个精心定义的区域。其核心思想是 React 的核心用于 UI,而状态则由另一个与 React 组件有精心定义交互的系统管理,状态在它们之间以精心定义的方式流动。

        换句话说,React在某种程度上是无状态且纯粹的,就像Haskell一样。它只是提供了逃生 hatch,以便引入状态,但这些状态存在于一个与纯 React 代码通信的独立区域中。

        • 我完全同意你的观点,但这

          哦,让我们使用无状态的函数组件。太棒了!哦,等等,让我们通过随机的‘use’函数打破无状态性

          暗示 React“打破了承诺”或以某种方式误导了你。尽管推动了函数式模式,但从未承诺过无状态性,至少我所知是如此。

          就像 React 开发者选择使用函数而非类,并不意味着他们是在倡导函数式编程的无状态特性。你似乎被误导了,但 React 一直都是这样。useState 并不是一个“逃生 hatch”……它是核心功能之一。

  13. 这感觉很疯狂,因为你一开箱就能获得一个坚实的基础,遇到常见问题时无需重新发明轮子。

  14. 我不确定作者是否明白自己在说什么。他抱怨全局状态和 reducers,但这些其实与 React 无关。他错误地使用了 useEffect 并对此抱怨。他似乎完全不理解钩子。他链接到一篇关于设计模式的文章,提到了 HOC,但该网站提到 HOC 是为了展示一种更好的方式(多种更好的方式)。这就像他只读了文章的前半部分,然后说“HOC?!太难了!!”就没继续读下去。这也很有趣,因为HOC其实并不复杂。

    他的解决方案是将一切移到后端。我觉得还行。我个人更喜欢后端工作,所以支持这个方案,但这并不是React的用途,把它作为解决方案有点可笑。

    • reducers 并不是真正的 React

      Reducers 非常符合 React 的设计理念,这就是为什么 React 提供了 useReducer 钩子。

      • 是的,但他指的是全局状态,而基础 React 中的 reducers 本身并不处理全局状态。如果你想实现全局状态,必须将 reducers 与 useContext 或 Redux 结合使用,对吧?

        • 没错,reducers 只是将状态更新批量打包为动作的模式,每个动作都包含特定的状态更新逻辑。它们的存在并不暗示状态的作用域。

          但说“它们不是 React”是误导性的。我同意 OP 并不清楚自己在说什么。

          • 是的,我本可以表达得更清楚。我的意思是“全局状态和 reducers”作为一个整体,但我明白我的表述可以更精准。文章读起来似乎认为所有 reducers 都是全局状态。

  15. 酷炫的吐槽,兄弟。

  16. 哎呀,那个useEffect部分真的让我感同身受。

  17. 总有人会发现M和C,而不是把所有东西都塞进V

  18. useEffect 的示例使用了如此初级水平的反模式,以至于我无法认真对待文章中的其他内容。

    既然自上而下和侧载方法都行不通,那么解决这个问题的方案是什么?我真的不知道。

    哦,明白了。非常启发人心!

  19. 我发现很有趣的是,有多少人明显对 React 一无所知,却还在抱怨。每个展示的代码示例都是糟糕的编程,如果你安装了一个代码检查工具,它会告诉你为什么。每个示例都是反模式,开发者显然不理解 React。

    将 useState 称为全局状态就是证据。它实际上是局部状态。去读读文档吧,哈哈哈哈。

    如果你更喜欢编写 Java 后端而不是 React,我认为这意味着你更喜欢脑残编程。Spring boot 是一种脑死亡的编码,你永远不会思考自己所做的一切。React 要求你思考你要解决的问题?程序员为什么要思考?哈哈哈哈,天哪。接下来,这个人的博客会写:为什么大家都认为 Rust 中的借用检查器很酷?它让一切变得如此复杂。为什么我必须思考我的代码?

  20. 网络开发的时间线:

    • 一堆东西…

    • JQuery 正在崛起,实际上它并不那么糟糕

    • React 正在崛起,它非常糟糕 <— 所有大语言模型(LLM)都基于这个垃圾进行训练

    • Svelte 和类似框架开始崛起并逐渐成熟

    所以……现在我们似乎要永远被 React 绑住了。

    • 作为一个用 jQuery 实际构建过实时 UI 的人,我再也不想回到那个时代了。零类型或类型提示,脚本标签和文件散落在各个地方,让代码导航变得地狱般困难,从随机 CDN 加载脚本,然后使用某个模糊的全局命名空间的导出……这简直糟糕透顶。它很糟糕,我不知道为什么这么多人认为它不是。除非你只是让表单有一个提交按钮并调用一些 AJAX 之类的东西。

      • 说得对。就这一点而言,我个人更怪那个时代,而不是 JQuery。我假设我们把 JQuery 传送到了 React 的 2025 年(Typescript、一个不错的构建器和一个大语言模型(LLM),它们都经过大量训练,能够很好地实现自动完成和模板)。诚然,你应该说:“你的意思是人们使用VanillaJS,因为JQuery修复的那些怪癖现在也不存在了。”而VanillaJS和TypeScript遗憾地是相互排斥的,所以我们永远无法拥有这些美好的东西……

        • 是的,我对jQuery本身并没有太多意见,因为它确实填补了一个迫切的需求。在AngularJS之后的几年里,情况确实糟糕透顶。我认为Webpack尤其对行业造成了巨大困扰,没有人会怀念配置它时那种令人抓狂的经历。Vite和TypeScript的出现确实带来了巨大帮助。依我之见,我们正处于 web 开发史上最好的时代,事情正变得越来越稳定。

          最近的 Node.js 版本以及像 Deno 和 Bun 这样的项目,也一直在努力将原生 TypeScript 引入后端环境。

          • 我真的很担心大语言模型(LLM)训练数据的偏见会让我们在 React 作为主导框架存在的时间比它应有的时间多出 5 年以上。

            • 是的,我希望像代理这样的东西,以及框架开发者提供代理,能让使用新框架变得更容易。不过我不确定这方面的工作进展如何,也不清楚构建代理有多容易。

  21. 我得承认自己有点老派了,但我觉得Java Swing(过去或现在)都很有道理。

  22. 我对 React 没有意见。虽然学习过程有些挑战,但只要按照正确的方式操作,React 的表现相当不错。

  23. 作为从 React 项目转到原生 JavaScript 项目的开发者,你不知道自己有多幸运……

    现在我正在积极尝试获取React中的一些提升生活质量的功能……

  24. 我还记得第一次看到它时有多新鲜。当时,它与当时的主流框架Angular 2形成了鲜明对比。

    React比Angular 2早出现很多。事实上,Angular 2是对React的反应(哈哈)。

    我认为,在钩子出现之前,Pete Hunt在《重新思考最佳实践》中提出的基于组件的React,是前端开发领域的巅峰之作。

    遗憾的是,如何处理状态的问题当时还不太明确。Facebook随后发布了一篇关于他们解决方案“Flux”的论文,但其实现并非一个库,而只是一个他们声称受其启发的单一文件。相反,大家采用了Redux,这是一个完全过度复杂的垃圾,有点像Flux。

    然后他们聘请了撰写Redux的人来管理React,而我们现在都清楚事情的发展方向。

    • 请不要太认真对待这篇帖子。我已经不再编写前端代码,而且持有这种观点的人少之又少。
  25. 好文章。以下是我认为这篇文章说对的地方,以及它说错的地方。

    说对的地方

    • 没有工具能消除 UI 复杂性。优秀的 UI 由领域特性、产品范围和性能目标塑造。框架可以平滑道路,但无法铺就目的地。

    • React 的陷阱确实存在。 该库过于中立,导致初学者在不知不觉中犯错。useEffect 是典型例子;它看似无害,却让副作用无处不在。如果它有个更吓人的名字,团队可能会将其保留为自定义钩子,而非在应用代码中随意使用。

    不足之处:状态钩子并非隐藏的全局变量。

    • useStateuseReducer 将状态局限于它们所在的组件子树中。这与“幽灵般的远距离作用”恰恰相反。

    • 全局状态应尽量避免。当确实需要时,应将其封装在 reducer 或存储中,并通过清晰的高级动作进行暴露。这与对象上的方法一样,能保持封装性。

    • 全局变量才是真正的隐患,因为它们允许任何代码在没有契约的情况下修改数据。基于 reducer 的存储仍通过显式动作控制每次状态转换。如果你通过 props 或上下文将 setState 传递到树中,你已经搞砸了。

    结论

    React 的最大危险并非钩子 API 本身。问题在于该库允许你自由混合各种模式。防护栏在于开发者自身。默认使用局部状态,仅在业务需求时引入共享存储,并将 useEffect 限制在经过充分测试的自定义钩子内。做到这些,大多数“React 疯了”的痛苦将消失,只剩下框架无法解决的、产品特有的不可避免的复杂性。

    我们注定要永远争论这个问题吗?

    想想土木工程师。如果他们声称每条河流都有一个“最优”的桥梁设计,他们的执照会被立即吊销。这不仅会极大地浪费资源,还会导致数百万人死亡。这是不可想象的。正确的设计取决于跨度、土壤、交通和预算。没有一个蓝图适合所有情况。

    软件的 stakes 较低,但规则依然适用。我们继承了坚实的基础,但每个项目仍需与自身特性相匹配的工具和模式。当工具与需求完美契合时,你就无需与技术栈抗争。当没有工具适用时,请欣然接受。这意味着你可以去创新。

    所以请记住:

    如果你要开发一个小型CRUD应用,就别用那些高端工具链。一个$5的入门级工具就足够了;把那艘价值千万美元的游艇留给浩瀚的海洋吧。

    • 我想补充一点,我认为是时候重新掀起“软件职业运动”了。我们所有人都应该为这一运动注入动力,而不是将它交给思想领袖。上次推动这一运动的思想领袖如今已不再拥有话语权。技术自由主义已占据思想领导力的顶端,我们需要予以反击。

      二十年前,专业执照制度显得为时过早。当时软件尚未深度融入日常生活,人才池规模较小,风险似乎可控。如今代码运行于每家企业,存在于每个口袋中,但我们仍在通过专注于狭窄技术栈的训练营和将伦理视为附带考虑的大学来培养开发者。这种“西部拓荒”状态的代价如今每天都显而易见:隐私泄露、灾难性停机,以及缺乏一致性保障措施的人工智能热潮。

      持有执照的工程师在共同的职业道德规范下工作,本应从一开始就坚持实施管控措施。然而,如今我们只能眼睁睁看着企业、政客和研究人员在事后仓促补救。我们欠公众的是一门职业,而不仅仅是一门手艺。

      如果你不同意,问问自己为何抵制执照制度。你的工作是否如此千篇一律,以至于真正的工程决策在设计方案到达你桌面之前就已完成,使你成为软件匠人而非工程师?如果这是事实,那么这场讨论与你无关,你需要退出讨论,将公众利益置于个人利益之上。或者,你是否希望摆脱在代码可能危害公众时提出异议的道德义务?如果这是事实,那么你正是我们需要防范的那种工程师。或者,你是否因为财务动机而反对监管?如果是这样,请明白正是因为你的利益,公众才需要这些监管,而非你继续对行业进行单方面控制。

  26. 精彩的文章。我知道我的评论很可能被忽略,但无论如何,分享我的观点和经验是值得的。

    我必须说,我是一名后端开发人员,很少使用React,所以一切都是从我的角度出发。大约两年前,在为客户做一个项目时(我几乎只做后端),我们开始遇到网页应用的问题:速度慢、体积大、难以修改。这也在后端方面造成了问题。为什么?有多种原因。例如,同一个页面上同一个接口被调用了三次,因为三个不同的组件都需要相同的数据。由于一切都是无状态的,这意味着我们不得不处理三次而不是一次。

    我当时想的只有“为什么这被认为是前端的标准?难道SSR不能解决我们面临的这么多问题吗?”

    但我也清楚,Spring Boot 目前没有好的 SSR 解决方案。我们该用什么?JSP?算了吧。Thymeleaf 或 Freemarker 是标准选择,但它们也有自己的问题。几个月后,与 CTO 讨论前端整体状况时,他对我说道:“为什么不自己做个更好的呢?”

    于是,最初作为概念验证,我开始开发一个新的SSR框架,试图解决Thymeleaf和FreeMarker存在的问题。令我惊讶的是,它竟然奏效了。后来我决定将其发展为一个完整的解决方案并开源。

    我收到了很多负面反馈。我听到诸如“欢迎来到90年代!我们现在用SPAs了”或“既然有React,我为什么要用这个?”或“JSP已经失败了,赶紧放弃吧”之类的话。我看到的只是,人们已经习惯了前端开发中的复杂性,以至于他们甚至没有意识到事情可以简单得多,而且并非一切都需要是SPAs。更不用说在大多数情况下,SSR更快。

    希望有一天我的框架能被某个项目采用。

    • 在同一页面上调用端点三次是React中一个容易解决的问题。为了解决这个问题而在Spring上构建一个全新的框架简直是疯了 😂

      • 那只是一个例子,当然我并没有创建这个框架来解决这个问题。我觉得你可能有点误解了故事的重点。我认为SSR(服务器端渲染)会更合适,但也承认当前模板引擎确实存在问题。因此我研究了如何解决这些问题。讨论更倾向于“我看到了Thymeleaf的问题,以及为什么像React这样的JS框架会流行。但这些框架也有大问题。希望有更好的SSR解决方案,仅在需要更动态的页面时使用CSR(客户端渲染)”。

  27. 技能问题,说实话

  28. Vue > React

  29. Angular 2 已经变得非常出色,但似乎没有人讨论这一点。

    • Angular 过去和现在都是更好的选择,我对此坚信不疑。

      不过,它毕竟是一个框架,而且是一个相当复杂的框架。很少有公司愿意 upfront 支付成本;而独立开发者更是缺乏动力。更不用说一些范式——比如响应式流——真的不太直观。我见过太多围绕 .subscribe() 的变通方案…… 🙂

      它确实让 React 中存在的大多数问题变得无关紧要。它还会为你做出许多决策。我以前常说——当你加入一个 React 项目时,你需要学习其生态系统。当你加入一个Angular项目时,你已经了解Angular。

      但不得不说,我不会推荐Angular给任何新项目。你找不到人才;而且我很少发现前期成本甚至值得一试

      • 我同意独立开发者可能更适合使用门槛较低的框架。但对于真正的工程团队中的程序员?我发现Angular实际上对懂得编程的人更容易。它恰当地使用了编程概念,这让人立刻就能理解。你不需要学习那些用更简单的概念实现相同功能的时尚网页开发JavaScript技巧。如果你不需要这些,实际上认知负荷更低。目前市场上充斥着懂得编程的人。这并非什么稀有人才。

  30. HTML模板再次称王。回归SSR并使用HTMX,或采用Svelte。

  31. 你在说任何组件都可以使用应用程序的任何状态片段?更糟糕的是,任何组件都可以触发状态变化,而这些变化又可以更新到其他任何组件中。

    这种设计怎么能通过代码审查?你基本上是在使用全局变量,只是加上了更复杂的状态修改规则。

    正确。这就是应该有的样子。如果状态被多个组件访问,它本质上就是全局的,因此必须全局可访问,但这种访问应通过在钩子中封装和修改状态来控制。

    愚蠢的面向对象设计是将状态封装在一个组件中,然后让其他组件以某种方式与该组件通信。这就是你最终得到一只大猩猩拿着香蕉,而整个丛林都跟着它的情况。

    我最大的抱怨是,useEffect 被用作“在组件挂载后运行某些操作”。

    不,它是用于在发生任何变化时执行操作。这比旧的 componentDidMount 等系统要通用得多,使得理解生命周期和有状态效果变得容易得多。

    此外,如果你想依赖该状态并在其之后执行操作,那么你……你……需要定义另一个依赖于第一个设置的 useEffect。

    这通常是糟糕的代码,应在同行评审中被拒绝,除非你确实正在运行两个完全独立的效果,它们只是偶然可能触发彼此。

    它们执行的顺序唯一线索是每个钩子底部 的“依赖数组”。而你实际上在脑海中解析它的方式,事实上是从底部到顶部。

    这一点我同意——我一直认为,对于带有依赖项的钩子函数,依赖项数组应该作为第一个参数。

    然后,仿佛这还不够,你们中的一些人甚至会写“CSS-in-JS”,然后为此获得报酬。

    是的,因为在修改组件时,不得不去查找CSS文件并同时编辑JSX确实很烦人。

    但把CSS也扔进去并使其强类型化?这不是走得太远了吗?

    你宁愿不让静态分析器检查CSS以发现拼写错误等?

    不可避免地,当你在服务器端渲染的“应用”中需要一些脚本逻辑时,也许明智的做法是只在最必要的地方添加它。越少越好。

    是的,但这样你最终会面临两个交互/HTML生成系统——客户端的和服务器端的。使用SPA仅处理客户端交互要简单得多,而将服务器作为原始数据提供者而非渲染UI。

    当然,如果你根本不需要任何客户端交互,那么你可以选择服务器端渲染,或者像我更喜欢的,直接使用原始HTML来创建静态网站。

    • 这是状态而非交互性。如果你需要前端状态,那么与后端分离的良好交互性是必不可少的。

      用 TypeScript 的术语来说,如果你需要该接口来定义数据,那么你可能需要前端来实现它。

      • 没错。交互性是以状态的形式实现的,状态的变化会影响界面的显示内容。其他状态可以存储在cookie或后端,但当你需要界面根据状态变化进行更新(而无需进行整个页面加载或从服务器获取页面)时,你可能需要使用一个框架。嘿,这就是为什么它被称为React——它本质上就是关于界面如何对状态变化做出反应。

    • 你在CSS-in-JS这里就让我跟不上了。这是一种需要被淘汰的糟糕做法。我厌倦了每六个月就要在新的框架中重新编写CSS。我们甚至无法再使用选择器了。

      • 使用pandaCSS。它非常直观,语法也不像其他一些UI库那样容易发生重大变更。我认为它非常出色。

  32. 当我看到“return”和一段标记代码时,就是我开始讨厌React的那一天。有人认为这是前端开发的巅峰,这让我怀疑前端世界的智力水平。

    Svelte 为何尚未取代 React,这对我来说完全无法理解。

  33. 最终,每个 JavaScript 开发者都会意识到,HTML 和超文本才是真正的革命,你只需要与 W3C 合作修复它,并利用 HTTP 实现你真正想要的一切。

  34. 任何JS都是疯狂的

  35. 我每天都用React,它很棒。

  36. Angular是“可能是网络开发中第一个‘框架’?”

    这个作者是个傻瓜。

  37. 你是说没有反应吗?

  38. > 最终结果——没有两个 React 应用是相同的。每个应用都使用从互联网上随机找到的库构建了定制的“框架”。

    这背后有其原因:早期开发节奏_如此_之快,传统框架难以跟上创新步伐。因此,你的框架要么随时间推移变得臃肿不堪,要么只能自行拼凑框架。

    由于项目是库的拼凑体,你可更轻松地根据需求或库过时情况进行重构。

    如今,事物运作方式已更加标准化,因此这种需求不再那么迫切,但你仍能在NextJS中看到这个问题,他们几乎每个月都会废弃框架的核心部分。

    别误会,这曾经是,而且仍然是,一个令人头疼的问题。但长期运行的项目无法承担全面重构以迁移到最新库版本的成本。即使NextJS也保留了旧有的工作方式。

  39. 这个子版块能否制定一些反标题党政策?

  40. 这是我浏览这篇文章时突然想到的,如果想法不够成熟请见谅:

    任何组件都能更新其他组件,这是软件开发中最复杂的事情之一。

    这听起来很像一个发布/订阅系统,可能更符合 Redux 通过显式分发 Action 对象来管理状态的方式。组件发布状态变化,任何其他组件都可以订阅这些变化。

    如果我这样想,它听起来并不像作者所暗示的那样疯狂。

  41. 写下这些让我意识到,服务器端渲染页面本质上是将页面简化为单一输出。

    天啊,本质上将页面简化为单一输出!?我好奇它到底在做什么。

  42. 不,不是我们习惯了它。我们只是擅长它。你只是个新手。是的,你可以有几年的经验,但如果你改变技术,你就会成为那个领域的初学者。

  43. 为什么不讨论惯例与法律,看看为什么一个比另一个更好。你看,网络之所以混乱,是因为我们引入了“自由实现标准”和“递归海龟”的概念,这导致了一个令人作呕的局面。这就是为什么在20世纪60年代,人们就讨论过API在ARPAnet中不可扩展的问题。(利克莱德)而我们看到结果如何。

    我们现在拥有的蜘蛛网结构几乎没有任何“意义”。但惯例已经胜出,几乎没有“法律”,所以你可以选择哪个平台的惯例对你最有意义,并尽最大努力保持在边界内。

    哦,还有,UI总是被视为次要考虑,并且被技术管理层严重低估,因此它充满了初级开发人员生成并混淆代码库,留下了一片无法维护的混乱。问我吧,我不得不重新构建两位初级开发人员实现的 Angular 应用程序,因为他们在忙于为团队做“更重要的事”时,构建了可能存在的最糟糕版本。但它“似乎能用”,因为它是 UI,而且“看起来正确”。哈哈。

  44. 标题是噱头,但隐藏在对React的批评之下,其实有一个相当不错的观点。

    “我认为这段代码很糟糕,因为创建一个交互式用户界面,其中任何组件都可以更新其他组件,这简直是软件开发中最复杂的事情之一。

    “……相比之下,我们在网页上使用的交互式用户界面可能拥有无限数量的输入和无限数量的输出。你如何指望为这种情况编写‘干净的代码’?

    因此,关于 React 的这番抱怨……这根本不是 React 的错。Angular 也不是,jQuery 也不是。无论你选择哪种技术,在构建响应式 UI 时,都不可避免地会因其难以想象的复杂性而崩溃。

    "….为什么,如果我告诉你我们需要在后端添加Redis,你会告诉我'不,我们需要控制技术复杂性'

    真正的启示是,React可以有用,但也倾向于复杂性。无论如何,记住KISS:

    保持简单,傻瓜。

  45. 没人讨论这个?你到底躲在什么鬼地方?这几乎是过去我都不记得多久以来,几乎所有人都在写的东西,而且它真的变得非常老套。我们都懂了,React 是撒旦的后代,你的妻子为了一个 React 工程师离开你,或者什么的。天啊,我讨厌互联网。

  46. 有趣的是,你几乎没有提到为什么 React 感觉如此疯狂。你最接近的讨论是在谈论 useEffect 时。我关于这个主题的草稿博客文章的标题是“React 是缓存的分形,具有元变异性”。我真的应该完成并发布它。

  47. 我基本上同意这篇文章!

    “岛屿”架构是正确的方向。React作为最初被发明和设计用于的框架,可以很好地融入其中。

    我是Astro的忠实粉丝,作为我的底层框架。它很棒,因为你不必绑定到任何特定的前端框架,所以你不必永远使用React。

    回归到为网站的静态部分编写简单的CSS和HTML,然后引入React、Vue、Svelte、Solid甚至Web Components来处理复杂的交互部分,这真是令人耳目一新!

  48. 我不明白为什么 React 会流行起来。与 Angular 2+ 相比,它感觉像是明显的降级。虽然 React 在表面上看起来“更简单”,但这只是因为它做的事情少得多,你最终不得不从各种库中拼凑出一个完整的框架,这通常会导致一个混乱的 mess。我永远无法理解为什么有人会更喜欢这种不一致的框架。

    我几乎可以立即开始处理任何 Angular 2+ 应用程序。框架引导开发者采用特定的架构风格。而 React 应用程序几乎不可能做到这一点,你首先需要花大量时间弄清楚这个混乱的系统到底该如何运作。

  49. 有人用过 HTMX 吗?

  50. Web开发给你太多的自由,让你自己陷入困境。这就是为什么我喜欢Angular,你需要强烈的意见和约定,才能创建任何可重复的结构和秩序。否则,很容易偏离脚本,创建可怕的意大利面条代码。让它“自由开放”从来不应该是任何JS/TS框架或库的目标,Web开发者需要护栏。归根结底,使用原生JS或JQuery做任何想做的事情其实非常容易,这就是整个问题所在!我们需要有用、直观的抽象和结构。因此我讨厌React。

  51. 又一个对技术一知半解却发表愤怒、居高临下评论的开发者。

    这个行业迫切需要更多好奇且富有同情心的人加入。

  52. React 可以很简单,只要你保持简单。但它非常容易因为 Typescript 和 Redux 之类的 boilerplate 代码而变得臃肿,即使你其实不需要它们。有些开发者就是喜欢过度复杂化。

    • 说实话,你会讨厌我这么说,但 Redux 是所有选项中相对较好的。它为你提供了非常精确的代码放置位置,而 RTK 消除了大部分 boilerplate 代码。

      • Zustand 相当不错,大部分代码都易于阅读。相比之下,Redux 的等效代码在我看来相当凌乱。Redux 的复杂性远超基于基本原则应有的简单程度。

      • 我更倾向于全程使用 React-Query。

  53. 我对 React 的问题,按随机顺序列出:

    1) 某些钩子功能在组件外部调用时很有用;例如 translatenavigate。并非所有内容都应属于组件。

    2) 状态与 UI 的结合。在我看来,这是错误的。MVC(模型-视图-控制器,类似 Smalltalk)能让开发者的工作轻松得多。

    3) JSX 组件与 DOM 节点之间的分离使得事情比必要时更加复杂。除了 DOM 之外存在另一棵树的唯一原因是 React 需要比较树结构以进行更新。

    4) 当 React 将代码划分为工作批次时,无法访问调用堆栈。在断点处,你获得的是 React 调度器的调用堆栈,而非应用程序的调用堆栈。

    React 其实毫无意义。如果我有一个好的 MVC JavaScript 框架,我可以用更少的时间实现相同的效果。

    React 真正存在的原因是对函数式编程的痴迷。不知何故,一些开发者认为让我们的 GUI 代码看起来更具函数式纯粹性会更容易维护,不知何故。

  54. 一個完全無害的「副作用」鉤子實際上在管理組件的狀態。為什麼沒有人討論這有多瘋狂?

    斯德哥爾摩綜合症。

    到這個地步,人們已經習慣了,所以不再質疑它。

  55. 都 2025 年了。Z 世代的人們真的還在發帖討論 React 嗎?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


京ICP备12002735号