“框架主义终究无法实现。正确的答案不是再搞出新的工具,而在于具备大刀阔斧进行工程设计的勇气。”有着十多年经验的软件工程师 Alex Russell 在他最近《React 不行,那到底什么行》的博客中提到。他在文中直接了当地表示,“如今任何人都不该用 React 构建新项目。”这篇博客迅速引起了开发者们的关注,并展开了一场围绕 React 的激烈讨论。
有开发者赞同 Russell 的观点:可能许多开发者都不知道如何在没有 React 的情况下构建东西。他们对 html、服务器端渲染、渐进增强或 React 出现之前我们用来构建数据应用表单的任何东西一无所知。更糟糕的是,这影响了整整一代人。当然,也有很多开发者反对他的观点,理由也很多,比如对于性能问题,有开发者表示前端性能问题几乎从来都不是最紧迫的问题等。
值得一提的是,Russell 是微软 Edge 团队的合作伙伴项目经理和 Blink API 的所有者。在 2008 年到 2021 年的 13 年间,他在谷歌担任软件工程师,从事 Chrome、Blink 和网络平台的工作。Russell 曾是 Chrome 的第一位网络标准技术领导(2015-2021 年),并且是 W3C 技术架构组的三届当选成员(2013-2019 年),以及十年的 TC39 代表。我们翻译了这篇文章,并在不改变作者愿意地基础进行了调整,以飨读者,以下为译文。
过去这十年来,我的主要工作就是通过团队合作,为桌面和移动设备构建雄心勃勃的 Web 类产品。在此期间,我也得以近距离观察过百个项目所涉及的各色团队、产品和技术栈。
虽然我也希望能把更多时间花在改进 Web API 上,但之前跟合作伙伴一起做的主要是修复由“现代”前端框架(例如 React、Angular 之类)及其相关文化引起的性能和可及性问题。必须承认,这些问题在当今基于 React 的技术栈中已经相当明显。
在我看来,作为一项遗留技术,React 在全新应用程序中的普遍存在着实令人不安。
更令人惊讶的是,有些人坚持认为 React 具备“现代性”。我倒是觉得 React 的这种“现代性”跟所谓“现代艺术”颇有几分相似——既没有体现出现代设计,也跟现代技术没半毛钱关系。它们并非为了满足当前需求或者性能标准而出现,纯粹只是一种成本高昂、能让人们回想起上个时代陈旧观念的集合体。
为了帮助更多团队避免“踩雷”,我想通过这篇长文提醒各位管理者和开发人员清晰意识到这种所谓“前端正统观念”的错误与危害。
简而言之,2020 年代了,任何人都不该用 React 构建新项目。
引发失控的文化
在服务器上运行的代码有着明确的成本计算方式。服务器端系统的性能和可用性由配置部门进行控制,延迟则由开发人员和 DevOps 工程师们主动管理。
相比之下,在客户端上运行的代码则纯由计算机本身决定。期间出现的一切延迟、客户端资源不足甚至是 API 功能都不在开发者的控制之内。
客户端 Web 开发应该是最典型的面向影响编程。就是说一旦代码离开数据中心,Web 开发人员就只能祈祷一切平安无事、再无插手的余地。
于是乎,一种不合理但有效的策略就强调尽量减少发出的代码。实际上,这意味着优先使用 HTML 和 CSS 来替代 JavaScript,因为前两者可以更优雅地降级并具有更高压缩率。声明式表单每发送一个字节都能生成更多功能性 UI,由此带来的弹性改进与成本降低将在网站的整个生命周期内发挥积极作用。
基于 React、Angular 及其他面向遗留系统,而且以桌面为中心的 JavaScript 框架,其技术栈则往往采取截然相反的做法。这类生态系统嘴上总说,“种种控制措施是为了防止不必要的客户端垃圾大量扩散所设立,”但由此带来的后果就是 NPM 合并的包充满了冗余,例如 core-js、lodash、underscore、不再存在的浏览器 polyfill、用户空间 ECC 库、moment.js 以及其他更多让人头痛的杂项。
这种文化必然引发失控,以至于 2024 年的 React 开发人员似乎也无法在不招惹这些遗留内容的前提下构建聊天机器人, 甚至还在使用非常笨拙的 MathML 或者 TeX 格式库来显示公式——而这一切,都只在极少数的会话中才被用到。
技术负责人和管理者应当打破这种魔咒,强制推动有益于客户的决策。换言之,直接禁止在所有新项目中继续使用 React。
那接下来怎么办?
这个问题看似简单,但却需要分成两个部分来看待:
对于其中的狭义部分,相信具备适当产品决策的团队都能通过客观的测试来找到答案。比如建立多个小型概念验证项目来确定各种方法的影响因子与限制条件。这就是工程学的魅力所在,先充分理解约束条件、再尝试新方法以改善用户感受。
注意:构建 SPA 或者客户端交互岛的开发人员已经有很多选择,因此本文不会推荐特定工具。简单来讲,Svelte、Lit、FAST、Solid、Qwik、Marko、HTMX、Vue、Stencil 等数十款当代框架都值得一试。
尽管它们的初始成本较低,但实际投资任何一款都需要注意严格控制客户端的有效负载和复杂性,毕竟按照字节计算,JavaScript 的成本仍然相当于等效 HTML 和 CSS 的 3 倍以上。
相信大家也感受得到,近年来技术栈决策面临的实际限制已经发生重大变化,至少是跟网站用户群乃至产品经理 / 技术主管的预期大不相同。收集这类数据有助于对技术栈进行初步筛选,快速提取出部分更具价值的选项来运行和比较。
但耗费时间和精力最多的,还不是这个环节。
很多人会问,“如果 React 不行,那到底什么行?”他们自以为问的是狭义形式,但实际上是在努力解决广义问题。更令人震惊的是,相当一部分积极且充满善意的产品经理和工程师们并没有认真关注过所使用架构的具体细节,而是随大流地选择了人气最高的架构。
对一部分人来说,放弃 React 会让他们突然没了依凭,不知道自己要以怎样的方式理解前端世界。身陷这种困境的团队开始审视自己的价值观和决策认识论,比如要如何判断自己的技术选择比其他人好?他们为什么要选择某项技术,而非其他方案?
很多人对自己的理解没信心,必须要辅以“行业权威”之类的证明。于是框架主义就成了当今前端领域的主导信条,其坚持认为只要团队努力构建框架,一切用户问题都将迎刃而解。但这明显不合逻辑,甚至可以说是本末倒置。实际上,让 Web 体验变好的唯一方法就是关注用户体验——具体来讲就是关注边缘用例的体验。技术方案总是来了又去,而起到决定性作用的永远是谁更关注用户感受。
所以 真正的难点,在于说服管理者和技术主管,让他们更多从用户的需求出发。Public Digital 曾对这一点做出很好的总结,“为用户需求做设计,而不是为组织便利而设计。”
这种思维转变的基本要素,就是用基于研究和证据的约束条件取代假大空的承诺。这也更多回归了工程设计的本质——即在已知的约束条件之下,为用户和社会设计出具有实践意义的解决方案。
而工程思维的反面,就是假定不存在约束条件或者你的产品不受约束,而这简单总结就是“纯纯意淫”。
但拒绝这种根深蒂固的观念并不容易。框架主义所宣扬的用户体验改善思路,是在框架的生态系统中引入更多(或者不同)工具。这在表面上看似乎很符合工程思维,但事实却并非如此。其甚至成了一种宗教式的承诺,导致框架主义者渐渐失去在框架之外主动寻求解决方案的习惯和能力。
在他们眼中,能够为用户带来重大收益的非传统模式反而成了需要被消除的错误。而如果没有数据或者证据来抵消意淫者的粗暴论断,又有谁能说他们真的错了?于是脱离量化结果的传统观念在传播中继续被歪曲和玄学化,而踏实可靠的实践却被视为异端而遭受严厉制裁。
这,就是荒谬的现实。
现实主义者当然不会沉溺于玄学,他们会尝试对一切做量化。现实主义者关注世界的本来面目,而不相信自己直觉中的样子。从这个意义上讲,现实主义与框架主义成了一对截然矛盾的反义词。
打破这种魔咒最有效的方式,就是让管理者重新以用户为中心来看待系统技术。 比如采用 Rum 数据的形式,例如核心 Web 生命力(https://web.dev/articles/vitals)或者由经过良好配置的测试台(例如 WPT,https://webpagetest.org/)提供的实验室结果。检测关键用户旅程并讨论业务的目的在于快速跟进动向,保证团队能够把握趋势并制定出切合变革方针的业务案例。
Rum 和基准数据源堪称框架主义的“解毒剂”,因为它们能够切实提供由数据驱动的基准素材。掌握数据的团队可以借此权衡盲目行动所带来的实际成本与潜在回报,而不是单凭直觉就贸然投资于某种新的框架。
拒绝 React 并不会造成价值损失
而且通过强制政策来禁止 React(或者其他框架主义倾向)则会带来惊人的成本节约效果,让团队重新回归为用户交付优秀成果的轨道。
而从广义角度入手,这个问题的答案可以分为以下几部分:
用户关注:决策者必须直接对其工程选择负责,不可推卸责任。必须将表现不佳的系统替换为表现良好的版本。任何事物不应存在神圣光环,且必须辅以适当约束才能解决问题。
证据:管理层和工程部门间的共同承诺,是落实现实主义的必要前提。重视证据,重视客观现实。
护栏:通过政策消除出于直觉的框架主义判断。组织当然可以根据需求调整指导方针,但最重要的是先设置一项基准。
测试:没有明确的指标支撑,就不应部署任何瓣系统。这些指标体现的是用户在系统中的期望行为。只有掌握了这些定义,我们才能开展测试,探索各种系统交付情况。在这样的健康流程下,产品经理的角色才会更加鲜明。他们需要定义产品价值锚点并将其与成功融合起来,而不能只是进行无休止的实验。
现实主义和框架主义的差异
要了解现实主义和框架主义在实践中的差异,我们不妨从实例入手。回想起来,我们选择技术的标准往往围绕着对主要数据(更新)的操作次数和会话长度展开。某些类型的用例天然拥有更长的会话和对 UI 主要信息的频繁增量更新。虽然也不乏例外,但一般来说平均会话较短的站点无法承受太多的 JS 前期投入。
一般来说平均会话较短的站点无法承受太多的 JS 前期投入。只有在这些例外情况下,才应考虑 SPA 架构。
而且只有在需要 SPA 架构时,旨在支持针对本地数据模型的乐观更新工具(包括“前端框架”和“状态管理”工具)才应当被纳入架构当中。
也就是说,重点并不是要不要用 JS 框架的问题,而在于是否该选择面向 SPA 的工具。
对于大多数网站,答案显然是否定的。
信息性
用于信息目的的站点几乎总会使用语义 HTML 构建,并根据需要选择渐进式增强。
静态站点生成工具(例如 Hugo、Astro、11ty 和 Jekyll)在此类情况下效果很好,而内容变化较频繁的站点则适合使用“经典”CMS 或者 WordPress 等工具来生成 HTML 和 CSS。
博客、营销站点、公司主页、公共信息站点等应尽可能减少客户端的 JS 负载,而且绝对不该使用支持 SPA 架构的框架。
什么语义标记和可选渐进增强才是正解
信息性站点有着会话较短且应用数据存放在服务器端的特征;也就是说,页面上显示内容的真实来源始终由服务器管理和拥有。这意味着无需客户端数据模型抽象,也不必由客户端组件对数据模型更新进行定义。
注意:不少信息性网站中会包含多种子应用程序。WordPress 等 CMS 就拥有两套不同界面:其一是面向帖子作者的低流量、高交互性编辑器;其二是面向读者的高流量、低交互性查看器 UI。二者都应考虑渐进式增强,但对于会话时间较短的读者视图来说,渐进式增强则属于“刚需”。
电子商务
电子商务网站应使用服务器生成的语义 HTML 和渐进式增强来构建。
亚马逊与其他基于 React 的竞争对手间有着巨大且稳定的性能差异,这表明 SPA 架构在电商应用中表现很差。沃尔玛 70% 以上的流量来自移动设备,这也导致他们大量使用 Next.js 的决定带来不少拖累。
简言之,电子商务开发团队应当默认拒绝 JS 技术栈,并通过客户端脚本控件来保留支持,以便在出现实质性业务指标需求时仍可应对。
为什么渐进式增强才是正确选项
电子商务网站的常规形式已经稳定保持了 20 多年:
经验表明,其中 UI 元素的通用性很低,会话长度的变化幅度则很大,而内容新鲜度将直接决定用户满意度。因此降低延迟感受的最佳方法就是针对轻量级单一页面进行优化,包括采取积极的资产缓存、图像优化和服务器端页面减重策略等。
媒体网站
媒体类站点在会话长度和数据更新需求方面同样差异巨大。多数网站可以从渐进式增强起步,并随产品变化的需要不断提升复杂性。
为什么渐进式增强加孤岛分隔可能是正确选项
媒体消费网站上的许多交互元素都可以被建模面独立的交互岛(例如评论线程)。其中许多组件都拥有独立的数据模型,因此可以在更大的静态页面中被建模为 Web 组件。
何时适用 SPA
目前 web 平台的一大基础局限,就是无法在顶级导航中保留页面中的某些元素。需要支持此类功能的网站就只能考虑 SPA 技术,同时为每页能够容纳的客户端 JS 大小设置严格防护。
另外一类需求则是离线播放。在管理本地媒体缓存时,需要建立一种将应用程序逻辑同服务器信息同步的方法。
在这些场景下,轻量级 SPA 框架可能比较适合,同时辅以 Zero 或 Y.js 等具备连接状态弹性的数据系统。
社交网站
社交媒体应用的会话长度和媒体功能同样多种多样。不少应用提供无限滚动界面和复杂的帖子编辑功能,而会话深度也将直接决定客户端与服务器数据的局部模型。
渐进式增强的优势
大多数社交媒体体验都是在服务器端的数据模型之上执行少量特定操作。此模型与混合方法能够良好匹配。
可能适用 SPA 的情况
深度交互岛在社交媒体应用中往往更加常见,积极的客户端缓存(例如保留帖子草稿)可能有助于提升用户体验。这时候,就适合将其作为独立的应用组件以满足相应需求。
离线支持则是另一个需要将数据模型和用户状态快照下载至客户端的重要理由。但由于这种支持对架构有着很强的侵入性,因此必须在立项之初就提前确认。
注意:不少人认为只有支持 SPA 的工具和框架才能建立起令人眼前一亮的渐进式 Web 应用,并保证其在离线状态下良好运行。但事实并非如此,也可以使用流拼接架构构建渐进式 Web 应用,该架构能在 Service Worker 中为客户端数据实现相当于服务器端模板的功能。
多页视图转换开始让 MPA 架构的渐进式 Web 应用实现不同用户状态间的济转换,同时摆脱重量级 JS 包对主线程的阻塞。虽然框架社区还需要几年时间才能真正吃透这些技术,但其目前已经可用且效果不错。
生产力应用
以文档为中心的生产力应用程序可能是最难评判的类别,因为这类应用通常要求实现协作编辑、离线支持和具有完整文档保真度的轻量化快速“只读”模式。
此外,以分类为导向的数据存储(例如电子邮件客户端)也是 SPA 技术的潜在应用场景。但同样的,能否提供更佳体验取决于会话长度和前期有效载荷的成本,必须精心做出权衡。
以这种方式构建应用程序的团队应当建立起强大的性能护栏,提前确定关键用户旅程,并确保配合适当工具来避免不愉快的性能卡顿。
可能适用 SPA 的情况
编辑器经常会对同一数据进行多次更新(例如每次按键或鼠标拖动)。乐观应用更新并仅异步通知服务器编辑,能够在长时间编辑会话中提供更好的体验。
但团队也应当意识到,编辑者本身也可能是查看者,因此不该在前期让捆绑包过于臃肿。更糟糕的是,这样的不良设计会导致页面加载时难以将查看会话与繁重的编辑会话区分开来。
因此必须根据用户需求建立关于模块化、阶段划分和延迟包加载顺序的明确条款(例如仅在用户需要时才加载编辑器组件)。
其他应用类别
某些类型的应用程序在本质上具有交互属性,强调访问本地设备硬件或者处理 HTML 自身无法处理的媒体类型。相关案例包括 3D CAD 系统、编程编辑器、游戏流媒体服务、Web 端游戏、媒体编辑和音乐制作系统等。这些限制通常让基于客户端的复杂 JS UI 成为最佳选择,但不同 UI 也都应当接受类似生产力应用的审视与设计评估:
核心用户旅程是什么?
平均会话深度如何?
应当跟踪哪些指标来确保性能处于可接受范围内?
应如何严格控制关键路径脚本及其他资源?
这类应用同样可以推出成功的 Web 版本,只是在设计阶段需要小心谨慎。
关于企业软件:我曾接触过不少所谓“企业业务线应用”,主要就是仪表板、工作流系统和企业聊天应用等等,而这些都是严重性能问题的重灾区。
构建团队往往以为“启动性能没那么重要,因为人们一早启动应用之后就会癸天保持开启状态”。但特定情况下,这话没错,但他们忽略了性能本身也是一种文化属性。因为他们习惯于不对包括加载在内的关键用户旅程进行定义和量化,自然也不会重视其他同行甚至更加重要的交互指标。所谓以小见大,就是这个意思。
很多优势厂商之所以被打败,原因就是挑战者拿出的产品并不是更出色,而是可用性更强。 而老牌厂商之所以解决不了自己的问题,是因为问题的根源已经深深埋在其招聘和晋升流程中所鼓励的行为之内。这种管理盲点将不断自我强化,最终形成一股任何领导者都撼动不了的惯性。
这也在很大程度上解释了,为什么不尊重用户的文化会受到追捧并最终损害产品形象,但开发者们却对此熟视无睹。唯一的解决办法就是强调一切决策以用户为中心。
那些没有用的行为
然而,那帮长期执着于框架主义的管理者们,也在被过度反应者们的过激言论而反向引导。很多抗议并没有把用户体验放在首位,而是在无谓地强调某些“最佳实践”。
“我们需要快速行动”
很多人似乎对时间这项指标特别敏感。但速度快不快是相对的指标,某些臃肿的软件在高端电脑上运行得并不慢,于是很多开发团队根本意识不到自己做的东西性能有多差。而由此引发的后果,可能在未来十年内变成反复发作的顽疾。
这类团队有一个共同点,就是他们越是喊着要快速行动,实际速度反而越慢。日积月累之下,他们的性能负担成了时刻需要担心并加以应用的问题,于是开发者的精力被大量消耗来维持一套勉强可以接受的性能组合,再无力做出任何有价值的创新。
相信管理层们在高呼“我们需要快速行动!”的时候,肯定没想到会变成这样。
开发者往往有种先入为主的观念,那就是先在目标受众中尽可能占据份额,之后再想办法优化用户体验。
但实际情况恰恰相反,消除使用障碍才是开辟新市场、提升被低估利润和成果的最佳途径。
“我们测过了,没问题啊”
这完全是一句胡话,毕竟用户不是你,而且你遇到的问题在十年前跟当下也完全不同。另外别总拿互联网大厂说事,人家垄断了各种社交业务,也有烧钱做自我革新的能力。如果各位没这两下子,最好别总拿巨头的经历说事。
“我们的团队已经很熟悉 React 了”
React 开发者也是 Web 开发者中的一员,离不开 CSS、HTML、JS 和 DOM。从这个角度讲,React 其实是技术栈中最易于替代的一层。30 多年来,Web 开发者一直都在不同的模板系统(JSX)之间顺畅切换。虽然不同框架之间确实有着很多差异,但 Web 开发者天然就是掌握多门语言的通才。
何况 React 知识也不算多有价值。任何熟悉 React 的团队都能快速上手 Preact、Stencil、Svelte、Lit、FAST 乃至 Qwuik 等等客户端系统,甚至不费什么心力。
“我们得降低招聘门槛”
别闹了,我们刚刚见证过科技行业无情裁掉了大批最有才华、最有同理心、最以用户为中心的工程师。现在是人才大甩卖的时代,只要愿意拿出还算合理的薪酬,吸引优秀人才根本不是难事。
别让简历把人锁死了
哪怕面试中考察了不少 React 知识,也不代表人家入职之后就只能用 React!任何能够从容应对这些专门为了恶心人才想出来的难题的求职者,肯定也有能力适应不同的框架和系统。
考虑到 React 乃至整个框架主义思维带来的极高成本,我觉得大家真的没必要在整个生命周期中都把自己锁死在当前这股潮流身上。
编程培训出身不是原罪
很多技术经理都喜欢贬低那些编程培训班出身的开发者,这让我感到恶心。他们通过培训班学习开发,不代表他们不愿意接触和学习其他技术栈。
培训班的毕业生们可能确实对计算机科学理解不深,但他们不是傻子。他们希望把工作做好,而管理层的任务就是定义就是什么才叫“把工作做好”。人家既然能够掌握 React 这种复杂的框架,就没有理由不能适应新的开发流程。别让这种无脑污名化限制了组织的选择空间。
换言之,这种观念属于纯粹的懒政。
“现在用户的手机个顶个地快”
十多年来,框架主义的一大前提就是默认客户端设备普遍拥有强大的本地性能,因此用最终用户性能来换取开发者的便捷性完全合理。
但这帮家伙抛出的完全就是自己臆想出来的结论——他们根本不知道用户的普遍手机配置是个什么水平,也不愿意深入探究和讨论。
任何一家希望在 Web 领域取得成功的企业都不该听信他们的鬼话,而且哪怕是有冗余的性能,也不该被这样的开发思路给凭空浪费掉。
“React 已经成为行业标准”
谁说的?
这甚至就是个故意编造出来的谎言,用来歪曲 React 技术栈中的灵活空间。实际上 React 并不是单一且僵化的个体,而是一种基于理念的选择。
在超过 100 次咨询活动中,我从来没见过两套完全相同的 React 配置。唯一的例外就是那些体量特别小的案例,即开发人员直接使用了 Create React App 的默认设置。但就算是默认设置,多年来也已经历过巨大变化。
这一切并没有标准可言,始终处于变化当中。
那 React 为什么还如此强势?
答案很简单,因为它样样通也样样松。
很多自以为是的万事通就是这样。框架专家们总爱说“虚拟 DOM 就是快”这类空洞的断言结束每一次有意义的对话,但他们压根就不懂浏览器的工作原理,更遑论其他替代方案的 GC 成本了。正是这份无知让他们斩钉截铁地说出了 React“最好”这个结论,却不知道各个方面其实都有更便宜的替代方案。
对于这帮不认真讨论的人,大家也不用认真看待。但反对是必须的,而且一定要坚持以用户为先的数据驱动结构。这些错误会带来长期且高昂的成本,导致很多团队必须借助咨询服务才能让本该“高性能”的技术栈勉强维持最低限度的性能表现。
“可人家生态系统完善呀”
具体哪部分完善?请具体说说。哪些软件包这么有价值,而且跟 React 完全绑定,以至于开发团队根本没办法考虑其他方案?它们真的在 Preact 上实现不了吗?这才是重点。
而且哪怕第一时间享受到了“生态系统”的优势,那又凭什么认定这一切都将永久持续?
任何库都有被抛弃的风险,而唯一可靠的方法就是把对 NPM 的过度依赖视为一种威胁未来工程能力的高息技术债。因此,降低成本的最佳方法就是全面检查并审核构成 UI 的完整依赖链,以及参与应用程序构建的所有 build 系统。只要开发团队不愿意拥有、修复或者改进其中任何一种系统,就不该将其纳入到技术栈中来。
“Next.js 其实也可以很快”
没错,但你这是在撞大运。
使用 Next.js 构建的网站在性能上要明显落后于优先使用 HTML 的系统。
它根本无法扩展,就像是给 React 套上了手铐脚镣,更别说之后还要添加更多自定义组件和路由呢。换句话说,Next.js 就是一种快速浪费资金的方式,还会把业务锁定在一家由风险投资支持的初创公司的专有 API 身上。
Next.js 一直以来的表现都很糟糕,而且还有进一步恶化的趋势。 唯一表现良好的 Next 站点,要么是用户使用的设备确实够强劲,要么就需要通过 Vercel 进行手动调优。
“我们还有 React Native!”
React Native 确实很牛,牛就牛在它是构建不手动调优就几乎没法使用的慢速应用程序最佳选项,也是开发糟糕网站的不二之选。
任何一家希望在原有 Web 网站基础之上开发出高性能移动体验的公司,都最好认真研究一下 Trusted Web Activities 还有 PWABuilder。如果这些方法不起作用,Capacitor 和 Cordova 也有类似的功能。这些方法既能支撑起大部分本机功能,也可保证将 UI 投资集中在 Web 端,通过单一执行路径提供可见性与可控性保障。反过来大大减少重复优化和可及性等现实问题。
原文链接:
https://infrequently.org/2024/11/if-not-react-then-what/