发布于 2026-01-05 2 阅读
0

前端框架的隐性成本

前端框架的隐性成本

我们都希望自己的网站在各种设备和屏幕尺寸上都能美观、快速且响应灵敏。前端生态系统中有很多常用的工具可以帮助我们构建这样的界面。

最常见、最知名的框架是React,此外还有许多其他框架也属于这一领域,例如SvelteSolidJSAngularVueQwik等等。它们都是令人印象深刻的工程杰作,并且都伴随着雄心勃勃的愿景。

React:

用于 Web 和原生用户界面的库

坚硬的:

用于构建用户界面的简单高效的响应式设计。

Vue.js:

一个易于使用、性能卓越且用途广泛的 Web 用户界面构建框架。

无论如何,它们都有一个共同点……

Javascript

如果你打算用这些框架之一来编写整个项目,那么无论好坏,你最终都得用 JavaScript(或 TypeScript)来编写所有逻辑。毕竟,JavaScript 是 Web 的语言,对吧?它就存在于浏览器中,用同样的语言编写 Web 应用似乎再自然不过了。

只有当你要编写一个全栈应用程序并且有服务器渲染或静态渲染的页面时,你才很可能会使用像NextJsRemixSvelteKitSolidStart这样的元框架。

这意味着你需要一台服务器,而且这台服务器还要运行 JavaScript。你其实别无选择,在这种情况下,框架会强制要求你采用这种架构。

便利性重于简洁性

但这应该不是问题吧?我的意思是,入门非常简单我们来看几个例子,快速上手吧。

Next.js:

npx create-next-app@latest
cd my-project
npm run dev
Enter fullscreen mode Exit fullscreen mode

SolidStart:

npm init solid@latest
cd my-project
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

这两个例子都用一些合理的默认设置引导项目,让你在几分钟内就能启动并运行一个带有开箱即用热重载功能的开发服务器。这很棒,感觉像是很棒的开发者体验,对吧?

假设你想为正在进行的项目构建一个新的 Web 应用程序。你可以在几分钟内用 Next.js 搭建并运行一个服务器端渲染的交互式应用程序,然后开始构建。这感觉可能很棒,但你的应用程序真的需要如此复杂才能实现其功能吗?

你真的需要一个构建步骤来将你的模块和 JSX 转译成一个 JS 包,然后在服务器上运行以生成初始 HTML,并通过大量 JS 将其流式传输到客户端来填充 DOM 节点,并让客户端状态跟踪虚拟 DOM 并在每次更改时重新渲染吗?

快速启动固然方便,但别忘了方便并不等同于简单。底层需要层层抽象才能实现“神奇”的功能,随着时间的推移和项目规模的扩大,这些抽象层可能会带来麻烦。

跟上依赖关系

在上面的示例中,我们搭建了一个基本项目,以便在 Next.js 和 SolidStart 中快速上手。不过,这里有一个有趣的地方我想重点说明。安装 Next.js 后(下方会显示多个已弃用的软件包警告),npm 会输出这样一行信息:

added 361 packages, and audited 362 packages in 10s
Enter fullscreen mode Exit fullscreen mode

对于 SolidStart 来说:

added 569 packages, and audited 572 packages in 18s
Enter fullscreen mode Exit fullscreen mode

这些分别是我们在初始化项目时 NextJs 和 SolidStart 需要和下载的软件包数量。

这仅仅是框架,并不包括项目中可能需要用到的任何软件包。想要代码格式化工具来强制统一格式?下载 Prettier。想要强制执行一些代码规则?下载 ESLint。想要添加类型定义?下载 TypeScript。想要运行测试?下载 Jest(或者 Vitest),等等等等。

虽然这看似微不足道,但我认为这表明我们可能并没有按照 JavaScript 的初衷使用它,最终不得不依赖其他包来执行一些相对常见的操作。这会导致层层依赖,最终形成一个复杂的依赖关系(此处可插入 node_modules 的梗图)。

如此多的依赖项本身就会让你的项目变得“温血”。如果你六个月不碰这个项目,当你再回来时,可能会发现它已经过时了。

别担心!你不需要时刻关注依赖项的更新,对吧?嗯,这要看情况。假设你很注重安全,并使用像Dependabot这样的工具来扫描依赖项是否存在漏洞。你可能会发现每周都会出现一些漏洞,尤其是在你的项目拥有成千上万个依赖项的情况下,这对于中大型项目来说并不罕见。

假设你完全不在乎更新。项目越老,就越难避免依赖瀑布效应。最终,当需要更新 Node、使用新发布的功能或添加与旧依赖项不兼容的新包时,你都不得不打开依赖瀑布的闸门。

另一方面,我也见过一些项目想要与所有依赖项保持同步,这可能导致每天多次软件包更新,有时还会引入一系列有趣的错误和怪癖,这些错误和怪癖通过了测试并进入了生产环境。

如果把这种情况推广到多个项目上,就会突然感觉像是一种持续的背景压力,每周都要测试和更新项目以保持其相关性,防止它们被束之高阁,这就会变成一项繁琐的工作。

快速演变的生态系统

你可能见过“又一个JS框架”之类的梗图。从积极的角度来看,我们可以说这个生态系统中不断涌现出创新、演进和变化。这些项目所投入的精力和热情着实令人印象深刻。然而,在生产环境中开发Web应用程序时,这也会带来一些负面影响。

如果你已经在这个领域摸爬滚打了一段时间,你可能用 Vite 搭建了你的第一个 React 项目,create-react-app以便快速上手。可惜的是,Vite 现在已经被弃用了,或许你应该迁移到 Vite。也许你用 Gatsby 搭建了你的第一个静态 React 项目。嗯,Gatsby 现在也差不多过时了,你应该迁移到 Next.js 或 Remix。已经在使用 Next.js 了?那你应该迁移到 app router。也许你一开始是用类来编写 React 组件,并添加逻辑生命周期钩子。嗯,那是老方法了,你现在应该编写函数式组件,并祈祷你能将正确的依赖项传递给钩子useEffect。但是等等!别担心,React 服务器组件已经出现,而且一个智能编译器也即将问世。

这种不断变化和力求保持领先地位的做法会增加技术债务,并加重项目维护的负担。想想看,如今使用 React 需要掌握多少概念,而 2016 年刚接触 React 的人又需要了解多少?值得称赞的是,React 一直保持着向后兼容性,但我们不能忽视为了跟上当今最佳实践所需的认知负担。

开发者体验谬误

这就引出了开发者体验的问题。我们目前讨论的所有内容,难道不都可以看作是这些框架所提供的出色开发者体验所必须付出的代价吗?

虽然我认为开发者的即时体验很棒,但长期体验却常常被忽视。当然,能在几秒钟内启动一个新的开发服务器,并在代码更改后几乎立即实现热重载,这种感觉确实很棒——但如果你记录下多年来每次重构、升级或维护项目的情况,就会发现体验远没有那么好,这一点不容忽视。

那么,我们能从中得到哪些启示呢?

选择合适的工具来完成工作

不要想当然地认为 Web 框架是构建 Web 应用的默认最佳选择。要考虑你的实际应用场景。它必须是单页应用吗?你需要如此丰富的客户端交互吗?你是否可以使用HTML之类的框架,并结合你选择的任何编程语言来生成页面,从而实现类似的功能?

保持简单

技术固然很酷,但有时我们需要的只是足够好用、简单易用的解决方案。这有助于我们保持理智,避免倦怠。避免过早抽象,尽量遵循网络标准,不要追求过高的扩展性。

作为开发者,我们有时会过于焦虑,执着于追求完美和最佳的开发方式。但别忘了享受过程,探索学习。即使最终失败了,也意味着你学到了东西,以后总有机会修改。

文章来源:https://dev.to/manonbox/hidden-cost-of-frontend-frameworks-5pi