React 很受欢迎,受欢迎到它也收到了不少批评。然而,这些针对 React 的批评并非完全没有道理:React 和 ReactDOM 总共大约有 120 KiB 的压缩 JavaScript 代码,这无疑会拖慢 启动时间。当完全依赖 React 的客户端渲染时,它会变得很卡顿。即使您在服务器端渲染组件并在客户端进行水合,它仍然会卡顿,因为 组件水合 在计算上代价很高。
在需要复杂状态管理的应用程序中,React 当然有其用武之地,但在我的职业生涯中,我发现它并不适合我所见到的大多数场景。当即使一点点的 React 都会在速度快慢不一的设备上造成问题时,使用它就成为一个需要仔细考虑的选择,它实际上排除了使用低端硬件的用户。
如果听起来我似乎对 React 有成见,那么我必须承认我真的很喜欢它的组件化模型。它使代码组织更容易。我认为 JSX 非常棒。服务器渲染也很酷——即使这只是我们如今用来表达“通过网络发送 HTML”的方式。
尽管如此,即使我愉快地在服务器端使用 React 组件(或者像我更喜欢的 Preact),但确定何时在客户端使用它却有点挑战。以下是我的 React 性能发现,我试图以对用户最友好的方式应对这一挑战。
设置场景
最近,我一直在开发一个名为 bylines.fyi 的 RSS 订阅应用程序的副项目。这个应用程序在后端和前端都使用了 JavaScript。我不认为客户端框架是可怕的东西,但我经常观察到我在日常工作和研究中遇到的客户端框架实现的两个问题。
- 框架有可能阻碍我们对它们抽象的事物(即 Web 平台)的更深入理解。如果不了解框架所依赖的一些底层 API,我们就无法知道哪些项目受益于框架,哪些项目最好不使用框架。
- 框架并不总是为良好的用户体验提供清晰的路径。
您可能可以争辩我第一点的有效性,但第二点正变得越来越难以反驳。您可能还记得不久前 Tim Kadlec 在 HTTPArchive 上对 Web 框架性能进行了一些研究,并 得出结论,React 的性能并不出色。
尽管如此,我仍然想知道是否可以在服务器端使用我认为 React 最好的部分,同时减轻它对客户端的不良影响。对我来说,同时希望使用框架来帮助组织我的代码,以及限制框架对用户体验的负面影响是有道理的。这需要进行一些实验才能找到最适合我的应用程序的方法。
实验
我确保在我的服务器上渲染我使用的每个组件,因为我相信提供标记的负担应该由 Web 应用程序的服务器承担,而不是用户的设备。但是,为了使可切换的移动导航正常工作,我的 RSS 订阅应用程序需要 一些 JavaScript 代码。

这种情况恰如其分地描述了我所说的 简单状态。以我的经验,简单状态的一个典型例子是线性的 A 到 B 交互。我们打开一个东西,然后我们关闭它。是有状态的,但简单。
不幸的是,我经常看到使用有状态的 React 组件来管理简单状态,这对于性能来说是一个有问题的权衡。虽然目前这可能是一个模糊的表述,但随着您继续阅读,您会逐渐明白。也就是说,需要强调的是,这是一个简单的例子,但它也是一个警示。大多数开发人员——我希望如此——不会仅仅依靠 React 来驱动其网站上的一件事的如此简单的行为。因此,了解您将要看到的这些结果的目的是为了告知您如何构建您的应用程序,以及当涉及到运行时性能时,您的框架选择的影响如何扩展。
条件
我的 RSS 订阅应用程序仍在开发中。它不包含任何第三方代码,这使得在安静的环境中进行测试变得很容易。我进行的实验比较了三种实现中的移动导航切换行为。
- 在服务器端渲染并在客户端水合的有状态 React 组件 (
React.Component
)。 - 一个有状态的 Preact 组件,也在服务器端渲染并在客户端水合。
- 一个服务器端渲染的无状态 Preact 组件,未进行水合。相反,普通的 事件监听器 在客户端提供移动导航功能。
这三种场景都在四种不同的环境中进行了测量。
- 在 Chrome 83 上运行的 诺基亚 2 Android 手机。
- 在 Chrome 83 上运行的 Windows 10 上的 2013 年 华硕 X550CC 笔记本电脑。
- 在 Safari 13 上运行的旧款 第一代 iPhone SE。
- 在 Safari 13 上运行的新款 第二代 iPhone SE。
我相信这一系列的移动硬件将说明各种设备功能上的性能,即使它在 Apple 方面略微偏重。
测量内容
我想测量每个环境中每种实现的四件事。
- 启动时间。对于 React 和 Preact,这包括加载框架代码以及在客户端水合组件所需的时间。对于事件监听器场景,这仅包括事件监听器代码本身。
- 水合时间。对于 React 和 Preact 场景,这是启动时间的一个子集。由于 macOS 上 Safari 中远程调试崩溃的问题,我无法单独测量 iOS 设备上的水合时间。事件监听器实现没有产生任何水合成本。
- 移动导航打开时间。这让我们深入了解框架在其事件处理程序的抽象中引入了多少开销,以及这与无框架方法相比如何。
- 移动导航关闭时间。事实证明,这比打开菜单的成本要低得多。我最终决定不在本文中包含这些数字。
需要注意的是,这些行为的测量仅包括脚本时间。任何布局、绘制和合成成本都将是这些测量的补充和外部。应该注意的是,这些活动与触发它们的脚本争夺主线程时间。
流程
为了在每个设备上测试这三种移动导航实现,我遵循了以下流程。
- 我在 macOS 上使用 Chrome 中的远程调试 来调试诺基亚 2。对于 iPhone,我使用了 Safari 中等效的远程调试功能。
- 我在每个设备上访问了在我的本地网络上运行的 RSS 订阅应用程序上的同一页面,该页面可以运行移动导航切换代码。因此,网络性能不是我测量中的一个因素。
- 在没有应用 CPU 或网络限流的情况下,我开始在分析器中录制,并重新加载页面。
- 页面加载后,我打开移动导航,然后关闭它。
- 我停止了分析器,并记录了每个四种行为中涉及的 CPU 时间。
- 我清除了性能时间轴。在 Chrome 中,我还点击了 垃圾回收 按钮,以释放可能由我应用程序的代码从上一个会话录制中占用 的任何内存。
我针对每个设备的每种场景重复此过程十次。十次迭代似乎可以获得足够的数据来查看一些异常值,同时获得相当准确的画面,但随着我们回顾结果,我会让您自己决定。如果您不想逐一了解我的发现,您可以查看 此电子表格 中的结果并得出您自己的结论,以及 每种实现的移动导航代码。
结果
我最初想用图表来呈现这些信息,但由于我所测量内容的复杂性,我不确定如何在不使可视化效果混乱的情况下呈现结果。因此,我将在一系列表格中呈现 CPU 时间的最小值、最大值、中位数和平均值,所有这些表格都能有效地说明我在每次测试中遇到的结果范围。
诺基亚 2 上的 Google Chrome
诺基亚 2 是一款低成本的 Android 设备,配备了 ARM Cortex-A7 处理器。它不是性能强大的设备,而是一款价格低廉且易于获得的设备。目前全球 Android 的使用率约为 40%,尽管 Android 设备的规格差异很大,但低端 Android 设备并不少见。这是一个我们必须认识到的问题,它与 财富和靠近高速网络基础设施 都有关。
让我们看看启动成本的数据是什么样的。
启动时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 137.21 | 31.23 | 4.69 |
中位数 | 147.76 | 42.06 | 5.99 |
平均值 | 162.73 | 43.16 | 6.81 |
最大值 | 280.81 | 62.03 | 12.06 |
我认为,平均需要超过 160 毫秒来解析和编译 React,以及水化一个组件,这说明了一些问题。提醒一下,在这种情况下,启动成本包括浏览器评估移动导航工作所需脚本所需的时间。对于 React 和 Preact,它还包括水化时间,这两种情况下都可能导致我们在启动时偶尔遇到的 诡异谷效应。
Preact 的表现要好得多,花费的时间比 React 少约 73%,考虑到 Preact 在未压缩的情况下只有 10 KiB,这是有道理的。尽管如此,重要的是要注意 Chrome 的帧预算约为 10 毫秒,以避免在 60 fps 时出现卡顿。启动时的卡顿与其他任何卡顿一样糟糕,并且是计算 首次输入延迟 的一个因素。不过,总的来说,Preact 的性能相对较好。
至于 addEventListener
实现,事实证明,对于没有开销的小脚本,解析和编译时间非常低,这并不奇怪。即使在采样的最大时间 12 毫秒时,您也几乎没有进入 Janksburg 大都市区的边缘区域。现在让我们看看仅水化成本。
水化时间
React 组件 | Preact 组件 | |
---|---|---|
最小值 | 67.04 | 19.17 |
中位数 | 70.33 | 26.91 |
平均值 | 74.87 | 26.77 |
最大值 | 117.86 | 44.62 |
对于 React,这仍然在Yikes Peak附近。当然,一个组件的中位水化时间为 70 毫秒并不是什么大问题,但想想 当同一页面上有很多组件时,水化成本是如何扩展的。我测试的 React 网站在此设备上感觉更像是耐力测试而不是用户体验,这并不奇怪。
Preact 的水化时间要少得多,这是有道理的,因为 Preact 的 hydrate 方法文档 指出它“跳过大多数差异化,同时仍然附加事件监听器并设置组件树”。没有报告 addEventListener
场景的水化时间,因为水化不是 VDOM 框架之外的事物。接下来,让我们看看打开移动导航所需的时间。
移动导航打开时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 30.89 | 11.94 | 3.94 |
中位数 | 43.62 | 14.29 | 6.14 |
平均值 | 43.16 | 14.66 | 6.12 |
最大值 | 53.19 | 20.46 | 8.60 |
我发现这些数据有点令人惊讶,因为 React 执行事件监听器回调的 CPU 时间几乎是您自己注册的事件监听器的七倍。这是有道理的,因为 React 的状态管理逻辑是必要的开销,但人们不得不怀疑对于简单的线性交互来说,这是否值得。
另一方面,Preact 设法将其在事件监听器上的开销限制在执行事件监听器回调只需要“仅”两倍的 CPU 时间。
关闭移动导航所涉及的 CPU 时间要少得多,React 的平均大约时间约为 16.5 毫秒,Preact 和裸事件监听器分别约为 11 毫秒和 6 毫秒。我会发布关闭移动导航测量的完整表格,但我们还有很多东西需要筛选。此外,您可以在我之前提到的 电子表格 中自己查看这些数据。
关于 JavaScript 样本的简要说明
在继续 iOS 结果之前,我想解决的一个潜在问题是 在 Chrome DevTools 中禁用 JavaScript 样本 在远程设备上录制会话时的影响。在编译我的初始结果后,我想知道捕获整个调用栈的开销是否扭曲了我的结果,因此我重新测试了禁用样本的 React 场景。事实证明,此设置对结果没有重大影响。
此外,由于调用栈被截断,我无法测量组件的水化时间。禁用样本与启用样本的平均启动成本分别为 160.74 毫秒和 162.73 毫秒。相应的中位数分别为 157.81 毫秒和 147.76 毫秒。我认为这完全属于“噪声范围”。
第一代 iPhone SE 上的 Safari
初代 iPhone SE 是一款很棒的手机。尽管它已经有些老旧,但由于其更舒适的物理尺寸,它仍然受到忠实用户的喜爱。它搭载了 Apple A9 处理器,这仍然是一款强大的竞争者。让我们看看它在启动时间上的表现。
启动时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 32.06 | 7.63 | 0.81 |
中位数 | 35.60 | 9.42 | 1.02 |
平均值 | 35.76 | 10.15 | 1.07 |
最大值 | 39.18 | 16.94 | 1.56 |
这比诺基亚 2 有了很大的改进,也说明了低端 Android 设备与即使是使用时间较长的旧款 Apple 设备之间的差距。
React 的性能仍然不佳,但 Preact 使我们能够达到 Chrome 的典型帧预算。当然,仅事件监听器速度极快,在帧预算中为其他活动留出了大量空间。
不幸的是,我无法在 iPhone 上测量水化时间,因为每次我在 Safari 的 DevTools 中遍历调用栈时,远程调试会话都会崩溃。考虑到水化时间是整体启动成本的一部分,如果诺基亚 2 测试的结果有任何指示,您可以预计它可能至少占启动时间的一半。
移动导航打开时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 16.91 | 5.45 | 0.48 |
中位数 | 21.11 | 8.62 | 0.50 |
平均值 | 21.09 | 11.07 | 0.56 |
最大值 | 24.20 | 19.79 | 1.00 |
React 在这里表现不错,但 Preact 似乎更有效地处理事件监听器。裸事件监听器速度极快,即使在这款旧款 iPhone 上也是如此。
第二代 iPhone SE 上的 Safari
在 2020 年年中,我入手了新款 iPhone SE。它与 iPhone 8 和类似手机具有相同的物理尺寸,但处理器与 iPhone 11 上使用的 Apple A13 相同。对于其相对较低的 400 美元的零售价来说,它非常快。鉴于如此强大的处理器,它的处理能力如何呢?
启动时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 20.26 | 5.19 | 0.53 |
中位数 | 22.20 | 6.48 | 0.69 |
平均值 | 22.02 | 6.36 | 0.68 |
最大值 | 23.67 | 7.18 | 0.88 |
我想在某种程度上,当加载单个框架并水化一个组件的相对较小的工作负载时,会存在收益递减的情况。在某些情况下,第二代 iPhone SE 比其第一代版本的速度略快,但差别不大。我想这款手机处理更大且持续的工作负载的能力会比其前代产品更好。
移动导航打开时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 13.15 | 12.06 | 0.49 |
中位数 | 16.41 | 12.57 | 0.53 |
平均值 | 16.11 | 12.63 | 0.56 |
最大值 | 17.51 | 13.26 | 0.78 |
React 的性能略有改善,但其他方面变化不大。奇怪的是,Preact 在此设备上打开移动导航的平均时间似乎比其第一代对应产品更长,但我将其归因于异常值扭曲了相对较小的数据集。我当然不会根据此假设第一代 iPhone SE 是一款更快的设备。
老旧 Windows 10 笔记本电脑上的 Chrome
诚然,这些是我最期待看到的结果:2013 年的华硕笔记本电脑,搭载 Windows 10 和当时的 Ivy Bridge i5 处理器,如何处理这些事情?
启动时间
React 组件 | Preact 组件 | addEventListener 代码 | |
---|---|---|---|
最小值 | 43.15 | 13.11 | 1.81 |
中位数 | 45.95 | 14.54 | 2.03 |
平均值 | 45.92 | 14.47 | 2.39 |
最大值 | 48.98 | 16.49 | 3.61 |
考虑到设备已有七年历史,这些数据还不错。Ivy Bridge i5 在当时是一款优秀的处理器,再加上它 采用主动冷却(而不是像移动设备处理器那样 被动冷却),因此它可能不像移动设备那样经常遇到 热节流 场景。
水化时间
React 组件 | Preact 组件 | |
---|---|---|
最小值 | 17.75 | 7.64 |
中位数 | 23.55 | 8.73 |
平均值 | 23.12 | 8.72 |
最大值 | 26.25 | 9.55 |
Preact 在这里表现良好,并且设法保持在 Chrome 的帧预算内,速度几乎是 React 的三倍。如果你在页面启动时要渲染十个组件,情况可能会有很大不同,甚至可能在 Preact 中也是如此。
移动导航打开时间
Preact 组件 | addEventListener 代码 | ||
---|---|---|---|
最小值 | 6.06 | 2.50 | 0.88 |
中位数 | 10.43 | 3.09 | 0.97 |
平均值 | 11.24 | 3.21 | 1.02 |
最大值 | 14.44 | 4.34 | 1.49 |
对于这种隔离的交互,我们看到的性能与高端移动设备类似。看到这样一台老旧的笔记本电脑仍然能够保持相当不错的性能,这令人鼓舞。也就是说,这台笔记本电脑在浏览网页时风扇经常旋转,所以主动散热可能是这款设备的救星。如果这款设备的 i5 是被动散热的,我怀疑它的性能可能会下降。
浅调用栈获胜
React 和 Preact 启动时间比完全摒弃框架的解决方案更长,这并不奇怪。工作量越少,处理时间就越少。
虽然我认为启动时间至关重要,但不可避免地,你将以牺牲一些速度为代价来换取更好的开发者体验。尽管我强烈认为我们往往过于偏向开发者体验而不是用户体验。
问题也出在框架加载**之后**我们做了什么。客户端水合(hydration)我认为被滥用了太多,有时完全没有必要。每次在 React 中水合一个组件时,你就是在向主线程抛出这些东西

回想一下,在诺基亚 2 上,我测得的水合移动导航组件的**最短**时间约为 67 毫秒。在 Preact 中(你将在下面看到水合调用栈)大约需要 20 毫秒。

这两个调用栈的比例尺并不相同,但 Preact 的水合逻辑更简单,可能是因为正如 Preact 的文档所说,“大多数差异比较被跳过了”。这里发生的事情要少得多。当你使用 `addEventListener` 而不是框架更接近底层时,你可以获得更快的速度。

并非所有情况都需要这种方法,但你会惊讶于你可以完成什么,当你的工具是addEventListener
、querySelector
、classList
、setAttribute
/getAttribute
等等。
这些方法——以及许多类似的方法——正是框架本身所依赖的。诀窍在于评估哪些功能可以安全地在框架提供的功能之外交付,并在有意义时依赖框架。

如果这是例如在客户端请求 API 数据并在这种情况下管理 UI 的复杂状态的调用栈,我会认为这种成本更可以接受。然而,事实并非如此。我们只是在用户点击按钮时让导航出现在屏幕上。这就像用推土机来做铲子更适合的工作。
Preact 至少找到了中间地带

Preact 完成 React 所做的相同工作所需的时间大约是它的三分之一,但在该预算设备上,它经常超出帧预算。这意味着在某些设备上打开该导航会动画缓慢,因为布局和绘制工作可能没有足够的时间完成而不会进入长时间任务区域。

在这种情况下,事件监听器正是我需要的。在该预算设备上,它的速度是 React 的七倍。
结论
这不是一篇针对 React 的攻击文章,而是一封呼吁大家思考我们工作方式的信。如果我们注意评估哪些工具适合这项工作,即使对于具有大量复杂交互性的应用程序,也可以避免其中一些性能陷阱。公平地说,这些陷阱可能存在于许多 VDOM 框架中,因为它们的本质增加了必要的开销来为我们管理各种事情。
即使你正在处理不需要 React 或 Preact 的项目,但想要利用组件化,可以考虑一开始就将其全部保留在服务器端。这种方法意味着你可以决定何时以及是否适合将功能扩展到客户端——以及**如何**这样做。
在我的 RSS 订阅应用程序中,我可以通过在该应用程序页面的入口点中放置轻量级的事件监听器代码,并使用资源清单来放置每个页面工作所需的最小脚本量来管理这一点。
现在假设你有一个真正需要 React 提供的功能的应用程序。你拥有具有大量状态的复杂交互。以下是一些可以尝试使事情运行得更快的方法。
- 检查所有有状态组件——即任何扩展 `React.Component` 的组件——并查看它们是否可以重构为无状态组件。如果组件不使用生命周期方法或状态,你可以将其重构为无状态组件。
- 然后,如果可能,避免向客户端发送这些无状态组件的 JavaScript,以及避免对其进行水合。如果一个组件是无状态的,只在服务器端渲染它。尽可能预渲染组件以最大程度地减少服务器响应时间,因为服务器端渲染也有其自身的性能陷阱。
- 如果你有一个具有简单交互的有状态组件,可以考虑预渲染/服务器端渲染该组件,并将其交互性替换为独立于框架的事件监听器。这完全避免了水合,用户交互不必经过框架的状态管理逻辑。
- 如果必须在客户端水合有状态组件,请考虑延迟水合页面顶部附近的组件。一个交叉观察器触发回调非常适合此目的,并且会为页面上的关键组件提供更多主线程时间。
- 对于延迟水合的组件,评估是否可以在主线程空闲时间使用
requestIdleCallback
安排其水合。 - 如果可能,考虑从 React 切换到 Preact。鉴于它在客户端上的运行速度比 React 快得多,值得与你的团队讨论一下这是否可行。最新版本的 Preact 在大多数情况下与 React 几乎 1:1,并且 `preact/compat` 在简化此转换方面做得非常好。我不认为 Preact 是性能的灵丹妙药,但它可以让你更接近你需要达到的目标。
- 考虑将你的体验适应低设备内存的用户。
navigator.deviceMemory
(在 Chrome 和衍生浏览器中可用)使你能够为低内存设备上的用户更改用户体验。如果有人拥有这样的设备,那么它的处理器可能也不快。
无论你决定如何处理这些信息,我的论点的核心是:如果你使用 React 或任何 VDOM 库,都应该花一些时间调查它对一系列设备的影响。获得一台廉价的 Android 设备,看看你的应用程序使用起来感觉如何。将这种体验与你的高端设备进行对比。
最重要的是,如果结果是你的应用程序实际上排除了无法负担高端设备的部分受众,则不要遵循“最佳实践”。继续努力让一切变得更快。如果我们的日常工作有任何迹象,这是一项会让你忙碌一段时间的工作,但这没关系。使网络更快使网络在更多地方更易访问。使网络更易访问使网络更具**包容性**。这是我们都应该尽力去做的好事。
我要感谢Eric Bailey对本文提供的编辑反馈,以及 CSS-Tricks 团队愿意发布它。
这是一篇很棒的文章!
我完全同意优化应该成为 2020 年的优先事项。
但我们(开发者)应该问自己的问题是:**谁应该做什么?**
如今你阅读的几乎每一篇社论都会鼓励你优化你的代码并大量投资于可访问性。它们写作的方式将这种负担强加给了普通开发者。
根据我的职业经验,我很抱歉在这里直白地说,大多数普通开发者都很糟糕。他们难以编写易于阅读和优化的代码。他们根本缺乏对整个系统的认识,他们只是按照指示去做,仅此而已。这很遗憾,但这是现实……
别误会我的意思,我希望我的网站/应用程序尽可能快且易于访问,但**将大部分繁重的工作交给浏览器、工具和框架制造商是否公平?**
我们可用的工具在过去几年中取得了令人难以置信的进步,但它们还有很长的路要走。
浏览器是第一道防线,而那里的情况远非一片光明。
你上次使用基本日期选择器以实现其可访问性是什么时候?如果有过一个好的可访问的默认日期选择器,我对此表示怀疑,它是否看起来足够好以至于可以被认真对待?
是否可以说基本元素的 CSS 自定义缺乏是当今网络上大约 25% 的可访问性问题的直接原因?开发人员被迫使用各种自定义解决方案,而这些解决方案或多或少都存在缺陷。给我一个合适的,我就会使用它,但我不会抱太大希望……
多年来,开发人员一直需要很多东西,这是一个很长的清单。老实说,我们可以说我们刚刚摆脱了浏览器在功能方面大多处于同一水平的黑暗时代。现在是时候让浏览器制造商以及负责 HTML、JS、规范等的人员真正浏览该清单了。
框架是一个有点争议的话题。
就我个人而言,在两方面都经历过(有和没有框架),要回到不使用框架的情况将非常非常非常……非常困难。组件、组合和状态已经变得太有用,无法舍弃。
在企业环境中,框架几乎是必需的。不是因为没有它们就无法完成,而是因为自己动手需要时间,而时间就是金钱。不管你喜不喜欢,金钱是大多数企业决策的主要因素,而大多数普通开发人员所做的事情就是:在企业工作……不仅如此,框架还提供了指导方针,并在一定程度上限制了开发人员可能造成的潜在损害。事情完成得更快,错误更少。金钱,这就是全部。
框架是否经过优化,这是极少数开发人员的责任。是否使用某个框架而不是另一个框架,是在组织更高层做出的决定,通常是由称职的人员做出的,其余的开发人员只需使用他们获得的框架。
工具也发挥着作用。
最近,开发人员被各种选择宠坏了。但仍有很长的路要走。
在几乎 99.9% 的工具中,可访问性通常只是事后才考虑的事情,这给普通开发人员带来了自己完成繁重工作的负担。
我个人喜欢 NextJs,也喜欢 Preact 如何让我从 js 包中剔除许多 KB。我喜欢许多框架(React、Vue、Svelte、Eleventy、Gatsby 等)的很多方面,但我也会看到它们有时优化得非常糟糕而感到难过。
我们在优化方面缺乏圣杯。我应该能够使用我选择的框架编写代码,而不用担心它将如何被转换为 HTML、CSS 和 JS。
我们今天使用的框架在某种程度上做到了这一点,但我们还没有一个从上到下覆盖 HTML + CSS + JS 堆栈的框架。
如果我使用 CSS-In-JS,我希望该工具使生成的代码仅加载我需要的 CSS,而没有 JS 开销。我希望默认情况下将封装、延迟加载和关键 CSS 从我的工作中移除。
如果我正在编写 JS 代码,我不希望担心包的大小或如何拆分它。我不应该担心不对折叠以下组件应用水合。我们不要谈论包含数十亿个文件的 node_modules……
HTML 中的可访问性应该默认存在,而不是像今天这样以非常笨拙的方式硬塞进去。应该能够轻松地在第一个请求中将所有文件推送到浏览器,打开管道,我们现在是 2020 年,可以多线程处理了。
我很好,至少我认为我很好;),但我不想独自完成所有事情。我还有生活,一天只有 24 小时,我喜欢每天晚上至少睡 7 个小时。食物链上层的人能否离开他们的宝座,帮助我们这里的人?
我的评论绝不意味着贬低你的文章。我同意每个开发人员都应该重视优化他们的代码并根据自己的需求做出最佳决策。我每天都在努力做到这一点。
我只是想表达许多开发人员正在感受到的一种情绪。我们肩上的压力一直在增加,而**其中大部分可以通过浏览器、工具和框架制造商来缓解**。
我的论点到此结束;)
我想说的是,相反,我们才刚刚进入浏览器的黑暗时代,Firefox 实际上已经死了,可用性很差。即使 Web 平台似乎在向前发展,整体用户体验也在不断下降。
感谢你的文章!
您是否考虑过使用一些无 vdom 的框架(如 Svelte 或 viperHyper/hyperHtml)运行相同的测试?(还有更多,但不是如果其中一些是“低级”的,即 lit-html 本身是否支持添加事件处理程序。)
嘿,Jonny,如果 Web 世界中的一切都是由 Google 和 Facebook 完成的,为什么有人会聘用你?如果一切都很顺利,你如何期望 Web 开发人员获得更高的薪酬?如果你对工具和框架有疑问,那就构建一个,让我们的生活更轻松。
此外,您可以使用 HTTP2 并行推送文件。
我认为 Web 社区也指出了“诱导需求”。工具越强大,开发人员就越有可能滥用它们。因此,我相信教育开发人员了解性能和可访问性以及各种设备和计算能力非常重要。感谢你的观点!
问题是,最需要这种观点的受众,一切看起来都很复杂,一切都是状态,因为他们的 fn(x) 等于状态。试图用推土机穿过门把地方毁了,现在你需要它来清理烂摊子。
我只会问一个挑衅性的问题:你认为基于 WordPress 的博客的读者端前端是否适合使用 React?因为最近有一个科技博客切换了,并且认为它很棒!即使使用 SSR,加载时间也比旧布局剥离垃圾后的加载时间更差……
我认为 React——或任何主要关注 JavaScript 驱动内容客户端渲染的框架——都不适合博客。博客是“文档 Web”的完美示例,其主要目的是访问阅读内容。React 的优势在于管理复杂的状态,而我们通常体验到的博客并不真正需要这样的工具。如果有人坚持要在博客中使用它,我会尝试说服他们改用 Preact。
哇。我们可以在下一篇文章中添加 Svelte 吗?
很棒的文章 Jeremy。我喜欢人们抽出时间来衡量和比较此类结果。
我的问题是,您将在“简单状态”和“复杂状态管理”之间划清哪条界限?我假设这条线将是灰色且模糊的:) 但即使有一些关于哪些因素有帮助的指导方针也很有帮助。我从这场讨论的另一端而来——我的工作几乎完全依赖于简单状态,并且不确定在引入 VDOM 解决方案之前我应该让它变得多么复杂,尤其是我喜欢组件带来的好处。我猜那些首先学习 React 的人,尤其是在类似训练营的环境中学习的人,也会遇到我同样的问题。
嘿,Julie。感谢你的提问。
我想这条线取决于你愿意自己处理多少状态。正如我在文章中所说,“简单状态”最适合线性、可切换的事物。我个人愿意更进一步来自己管理一些事情——比如我的 RSS 订阅应用程序的“订阅”按钮——以及更复杂的事情。是的,这条线非常模糊,并且可能取决于你是与团队合作还是独自工作。
但是,你不需要现成的状态管理解决方案(但它很有帮助)。你可以始终使用
data-
属性用初始状态填充元素,然后用这些值填充一个状态对象,并在不同的事件处理程序中管理该状态对象。这基本上是一个状态机,David Khourshid谈论了很多。他在他的xstate 项目上做了大量工作,但他还提供了一个关于 3 KiB 未压缩的 xstate 超最小实现,称为@xstate/fsm,我刚刚开始在我的项目中探索它。无论如何,希望这有帮助。