并不是只有你这么觉得,Next.js 变得越来越难用了

原文信息: 查看原文查看原文

It’s not just you, Next.js is getting harder to use

- Andrew Israel

并不是只有你这么觉得,Next.js 变得越来越难用了

前几天我写了一篇博客文章,讲述 Next.js 中间件如何有助于解决服务器组件强加的一些限制。这引发了一些有趣的讨论,关于这是否是一种合理的途径,或者 Next.js 的开发体验(DX)是否只是… 糟糕

从我的角度来看,Next.js 的 App Router 有两个主要问题使其难以采用:

  • 你需要了解很多内部原理才能完成看似基本的任务。
  • 有很多方式会让你无意中给自己带来麻烦,这些是选择性退出而不是选择性加入的。

为了更好地理解这一点,让我们看看它的前身,页面路由器。

快速回顾页面路由器

当我第一次了解 Next.js 时,主要的“竞争对手”是 Create React App (CRA)。我所有的项目都在使用 CRA,但我因为两个原因转而使用 Next.js:

  • 我喜欢基于文件的路由,因为它允许我写更少的样板代码。
  • 每次我运行开发服务器时,CRA 会打开 http://localhost:3000(这很快就变得很烦人),而 Next.js 不会。

第二个原因可能有点傻,但对我来说,Next.js 是:

带有更好默认设置的 React。

这就是我想要的全部。直到后来我才发现 Next.js 有其他特性。API 路由非常令人兴奋,因为它们让我拥有了一个无服务器函数,而无需设置任何额外的基础设施 - 对于营销网站上的“联系我们”表单等事情来说非常方便。getServerSideProps 允许我在页面加载之前在服务器上运行基本功能。

这些概念很强大,但它们也很简单。

API 路由看起来和行为上与任何其他路由处理器非常相似。如果你使用过 Express 或 Cloudflare Workers,你可以眯起眼睛看一个路由处理器,所有你已经知道的概念都翻译过来了。getServerSideProps 有点不同,但一旦你理解了如何获取 request 以及响应的格式,它实际上也相当简单。

App Router 的发布

Next 13 版本引入了 App Router,增加了许多新特性。你可以使用 服务器组件 在服务器上渲染你的 React 组件,并减少你需要发送到客户端的数据量。

你可以使用 布局,它们允许你定义多个路由共享的 UI 部分,并且不需要在每次导航时重新渲染。

缓存变得更加复杂。

虽然这些特性很有趣,但最大的损失是简单性

当一个框架没有按照你认为的方式工作时

作为一个开发者,一个相当普遍的经历是,你会感到沮丧地大喊,“为什么这不起作用?”

每个人都经历过这种情况,这总是令人不快。对我来说,如果感觉不是我的代码中的一个 bug,而是对事物应该如何工作的误解,那就更加痛苦了。

你不再喊,“为什么这不起作用?”而是,“为什么这样工作… 那样?”

不幸的是,App Router 充满了这些微妙之处。

让我们回顾一下我的原始问题:我只想在服务器组件中获取 URL。这是对这个话题的一个流行 Github 问题的回答,我会在这里发布一部分:

如果我们退后一步,问题“为什么我不能访问 pathname 或当前 URL?”是更大的问题的一部分:“为什么我不能访问完整的 请求和响应对象?”

Next.js 是一个静态动态渲染框架,它将工作分拆成路由段。虽然公开请求/响应是非常强大的,但这些对象本质上是动态的,并且影响整个路由。这限制了框架实现当前(缓存和流式传输)和未来(部分预渲染)优化的能力。

为了解决这个挑战,我们考虑过公开请求对象,并跟踪它的访问位置(例如使用代理)。但这将使得跟踪代码库中方法的使用方式变得更加困难,并可能导致开发者无意中选择动态渲染。

相反,我们公开了 Web 请求 API 的特定方法,统一并优化了每种方法以适应不同的使用环境:组件、服务器操作、路由处理器和中间件。这些 API 允许开发者明确选择框架启发式,如动态渲染,并使 Next.js 更容易跟踪使用情况,打破工作,并尽可能地优化。

例如,当使用 headers 时,框架知道要选择动态渲染来处理请求。或者,在 cookies 的情况下,你可以在 React 渲染上下文中读取 cookie,但只能在变异上下文中(例如服务器操作和路由处理器)设置 cookie,因为一旦开始流式传输就不能设置 cookie。

值得称赞的是,这个回答非常棒。它写得很好,帮助我理解了很多潜在问题,并让我深入了解了与不同方法相关的权衡,这些我绝对没有考虑过。

话虽如此,如果你是一名开发者,而你所做的一切就是试图在服务器组件中获取 URL,你可能会在阅读这个回答后,留下更多需要 Google 的事情,然后意识到你可能必须重构你的代码。

这篇文章总结了我对它的感受:

并不是说它一定是错误的 - 这是意料之外的。

那篇原创文章还提到了一些其他的微妙之处。一个常见的陷阱是 cookie 的处理方式。你可以在任何地方调用 cookies().set("key", "value"),它将类型检查,但在某些情况下它会在运行时失败。

将这些与“旧”方式进行比较,你得到了一个大型的 request 对象,可以在服务器上做任何你想做的事情,可以说复杂性有了跳跃性的提升。

我还需要指出,“默认开启”的积极缓存是一个糟糕的体验。我认为更多的人期望选择性加入缓存,而不是挖掘大量文档以弄清楚如何选择性退出。

我相信其他公司也有类似的问题,但在 PropelAuth,我们经常收到错误报告,这些不是错误,而是“你认为你发起了一个 API 调用,但你没有,你只是读取了一个缓存的结果。”

所有这些都引出了一个问题,这些特性和优化是为了谁?

很难构建一个通用产品

我所说的所有这些特性,被我描绘为过于复杂,确实对某些人很重要。例如,如果你正在构建一个电子商务平台,这里有一些很棒的特性。

你的页面加载更快,因为你发送到客户端的数据更少。你的页面加载更快,因为一切都被积极地缓存了。你的页面加载更快,因为只有页面的一部分需要在用户导航到新页面时重新渲染。在电子商务领域,更快的页面加载意味着更多的钱,所以你绝对会选择接受一个更复杂的框架来换取它们。

但如果我在为我的 SaaS 应用程序构建一个仪表板… 我真的不在乎那些。我更关心的是我能以多快的速度发布功能,所有这些复杂性都成为了开发团队的负担。

我个人对 App Router 的经历和挫折会与另一个人的不同,因为我们有不同的产品,不同的用例和不同的资源。具体来说,作为一个花费大量时间编写并帮助其他人编写 B2B SaaS 应用程序的人,App Router 的开发体验与页面路由器相比是一个巨大的退步。

对于框架来说,随着它们的成长,这是否是不可避免的?

随着产品/框架的成长,它们往往会变得更加复杂。客户要求更多东西。大客户要求更具体的东西。大客户付得更多,所以你优先考虑并构建那些更具体的东西。

之前喜欢它的简单性的客户对事情感觉复杂而感到恼火… 哦,看看,一个新的框架出现了,它更简单。我们都应该转而使用那个!

很难避免这种情况,但一种缓解方法是不要让每个人都必须处理只有一些人需要的复杂性。

只因为推荐,并不意味着它适合你

我对 App Router 的最大问题之一就是这一点:

Next.js 官方推荐你使用 App Router 是在它真正准备好用于生产环境之前。Next.js 没有对 TypeScript、ESLint 或 Tailwind 是否适合你的项目提出建议(尽管它默认提供 TS/ESLint 是 Yes,Tailwind 是 No - 对不起 Tailwind 粉丝),但它绝对认为你应该使用 App Router。

官方 React 文档 没有同样的感受。它们目前推荐页面路由器,并将 App Router 描述为“前沿的 React 框架。”

通过这个视角看待 App Router,它更有意义。与其将其视为 React 的推荐默认值,你可以更多地将其视为 像一个 beta 版本。体验更加复杂,一些过去容易的事情现在变得困难/不可能,但你会从一些仍在“前沿”的东西中期待什么呢?

所以当你为下一个项目选择框架时,值得认识到 App Router 仍然有很多粗糙的边缘。你可能会有更好的运气选择一个更适合你用例的不同工具。

分享于 2024-06-01

访问量 64

预览图片