我们被问到的最大问题之一是
Remix 与 Next.js 有什么不同?
看来我们不得不回答这个问题!我们想直接且坦诚地解决它。如果您是 Remix 的粉丝,并想开始转发这篇博文的自鸣得意的反应,我们恳请您在点击转发按钮前先放下自满 🤗。海纳百川,有容乃大。早在 Vercel 成立之前,我们就与 Vercel 的团队成员是朋友。他们正在做伟大的工作,我们尊重他们的工作!
但毫无疑问,**我们认为 Remix 比 Next.js 拥有更好的权衡取舍**。 (否则我们也不会去构建它...)
我们鼓励您通读整篇文章。在这个话题中有很多细微差别,这些细微差别在闪亮的图表和动画中没有体现出来。希望到最后,您会考虑在您的下一个项目中使用 Remix(并非有意为之 😂)。
我们认为,比较这两个框架最公平的方式是采用 Vercel 团队自己编写的 Next.js 示例应用程序。由于是他们编写的,因此他们做出的决策应该反映了他们希望您如何构建应用程序。它还应该展示 Vercel 团队最引以为傲的功能。
我们移植了 Next.js 示例页面中的 Commerce Example。它有一些我们喜欢的现实世界中的功能,并且似乎是他们投入最多精力开发的示例。
我们实际上构建了两个版本
请注意,此应用程序无法使用我们认为 Remix 很酷的所有功能(例如嵌套路由!)。一旦我们回答了这个问题,我们就可以继续只讨论 Remix,敬请期待!
此外,我们在发布之前与 Vercel 分享了这篇文章。事实证明他们的示例运行在旧版本的 Next.js 上,他们对其进行了更新,因此我们花时间重新制作了这个示例以与他们最新的示例进行比较。
我们认为他们是朋友,甚至是合作伙伴,因为 Vercel 是 Remix 的一个极好的部署目标。我已经将 Remix 应用程序部署到我听说过的几乎所有托管服务上。Vercel 的开发者体验是我最喜欢的。 “开发、预览、发布”的理念产生了真正的效果。就在今天早上,@gt_codes 和我试图找出生产环境中的一个错误,并且能够查看每个预览部署以及每个部署的小屏幕截图,这帮助我们在几秒钟内找到了错误的提交。这很棒。
现在这种关系很有趣,因为我们不仅仅是朋友和技术合作伙伴,我们还是框架竞争对手!因此,对于我们的朋友、合作伙伴和竞争对手 Vercel,Lee 精彩地阐述了这篇文章背后的动机
当 DevTools 中存在竞争时,开发人员就会获益
— Lee Robinson (@leeerob) 2021年11月30日
◆ Svelte 正在推动 React
◆ Remix 正在推动 Next.js
◆ Prisma 正在推动 ORM
◆ Deno 正在推动 Node.js
◆ Supabase 正在推动 Firebase
◆ esbuild / SWC 正在推动 JS 工具
◆ Bun 正在推动 SWC
还有什么?
请在了解此背景信息的情况下阅读这篇文章。让我们开始推动吧!
我认为,可以通过构建者如何描述它来了解很多关于它的信息。 (如果你关注我的推特,你会知道我一直在努力改进我们的描述!)
Next.js 将自己描述为
用于生产环境的 React 框架。Next.js 为您提供了最佳的开发者体验,以及您在生产环境中所需的所有功能:混合静态和服务器渲染、TypeScript 支持、智能打包、路由预取等。无需配置。
Next.js 由 Vercel 构建。查看 Vercel 平台的 GitHub 存储库,它指出
Vercel 是一个用于静态网站和前端框架的平台,旨在与您的无头内容、电子商务或数据库集成。
我们把 Remix 描述为
Remix 是一个边缘原生、全栈 JavaScript 框架,用于构建现代、快速且强大的用户体验。它使用 Web 标准统一客户端和服务器,因此您可以减少对代码的思考,更多地关注您的产品。
我们将让您自己对比这些描述。
Remix 是否与 Next.js 一样快?
这通常是人们提出的第一个问题。Next.js 经常使用“默认性能”这个短语,并且他们确实做到了!让我们看看每个应用程序渲染“视觉完整”页面的速度有多快。
我们通过 WebPageTest 运行了这些网站。这是一个很棒的工具,可以生成本文中的比较 GIF 图。在每次比较中,我们都让每个框架运行五次,并取每次运行中的最佳结果。
每个比较的上方都有一个标题,该标题链接到生成动画的结果。您可以通过简单地点击 WebPageTest.com 上的“重新运行测试”来自行验证所有内容。
第一个测试是在弗吉尼亚州运行的,使用有线调制解调器连接到互联网。
在我们说任何话之前,让我们承认所有三个版本都非常快,以至于不值得比较谁更快。对 Next.js 来说也有些不太公平,因为 cookie 横幅的小动画会影响“视觉完整”,而 Remix 网站没有它。让我们慢动作看一下
现在我们可以看到 Next.js 实际上在 0.8 秒完成。再说一次,它们都很快。我还使用 3G 网络连接对它们进行了相同的测试,结果相同:都很快,看起来都差不多。
✅ Remix 与 Next.js 一样快
Next.js 为什么很快:首页使用 静态站点生成 (SSG) 和 getStaticProps
。在构建时,Next.js 从 Shopify 获取数据,将页面渲染到 HTML 文件并将其放入公共目录。当网站部署后,静态文件现在在边缘(Vercel 的 CDN)提供服务,而不是在单个位置的源服务器上。当请求进来时,CDN 只需提供文件即可。数据加载和渲染已提前完成,因此访问者无需支付下载和渲染的成本。此外,CDN 分布在全球各地,靠近用户(这是“边缘”),因此对静态生成文档的请求无需一直传送到单个源服务器。
Remix 移植版为什么很快:Remix 不支持 SSG,因此我们使用了 HTTP stale-while-revalidate 缓存指令(SWR,不要与 Vercel 的 swr
客户端获取包混淆)。最终结果相同:边缘上的静态文档(即使在同一个 CDN,Vercel 的)。不同之处在于文档到达那里的方式。
缓存不是在构建/部署时获取所有数据并将页面渲染到静态文档,而是在您获得流量时填充缓存。文档从缓存中提供服务,并在后台为下一个访问者重新验证。与 SSG 一样,当您获得流量时,没有访问者需要支付下载和渲染的成本。如果您想知道缓存未命中会发生什么,我们稍后会讨论。
SWR 是 SSG 的一个很好的替代方案。部署到 Vercel 的另一个好处是他们的 CDN 支持它。
您可能想知道为什么 Remix 移植版没有 Next.js 快。由于 Remix 还没有内置图像优化功能(尚未),我们只是将图像指向 Next.js 应用程序 🤫。浏览器必须打开到两个域的连接,这会导致图像加载延迟 0.3 秒(您可以在 网络瀑布图中验证这一点)。如果图像为自托管,它将与其他两个图像一样快,大约在 0.7 秒左右。
Remix 重写版为什么很快:此版本不是使用 SSG 或 SWR 在边缘缓存文档,而是在 Redis 中在边缘缓存数据。事实上,它实际上还在边缘运行应用程序,使用的是 Fly.io。最后,它有一个快速的图像优化 资源路由,它写入 持久卷。它基本上是自己的 CDN 😎。
几年前构建这可能很困难,但过去几年服务器格局发生了重大变化,而且只会变得更好。
Remix 与 Next.js 有什么不同?
这是我们收到的下一个问题。功能集有很多差异,但一个主要的架构差异是 Remix 不依赖于 SSG 来实现速度。
在几乎每个应用程序中,您最终都会遇到 SSG 无法支持的情况。对于我们正在比较的应用程序,它是搜索页面。
限制是用户可以提交无限数量的查询。在当前宇宙时空的限制下,您无法静态生成无限的页面。SSG 不适用。
由于 SSG 无法扩展到动态页面,因此 Next.js 切换到从用户浏览器进行客户端数据获取。查看网络瀑布图将告诉我们为什么它比 Remix 慢 2.3 倍。
在 Next.js 应用开始加载图片之前,Remix 应用就已经完全加载完成了。也许 Web 性能中最重要的事情就是并行化网络瀑布。在 Remix 中,我们对此非常狂热。
为什么 Next.js 比较慢:Next.js 引入了我们称为“网络瀑布请求链”的东西。由于这里无法使用 SSG,应用是从用户的浏览器中获取搜索结果。它必须先获取数据才能加载图片,而它必须先加载、解析和评估 JavaScript 才能获取数据。
在客户端获取数据也意味着需要通过网络传输更多 JavaScript,并且需要更多时间进行解析/评估。有时我们会忘记解析/评估,但您可以看到第 15 个请求上的 JS 执行时间比文档下载时间还要长!Next.js 发送的 JavaScript 比 Remix 多 1.5 倍,解压后分别为 566 kB 和 371 kB。通过网络传输时,压缩后多 50 kB(172 kB 对比 120 kB)。
在浏览器中执行更多工作会逐渐累积。查看底部显示 CPU 使用率和浏览器主线程活动的行。Next.js 应用非常繁忙,一个大的红色“长任务”正在减缓速度。
为什么 Remix 仍然像首页一样快:Remix 示例实际上都不需要在请求中与 Shopify API 交互。虽然 SSG 无法缓存搜索页面,但 Remix 版本可以:使用 SWR 或 Redis。当您拥有单一且动态的页面生成方式时,您可以调整缓存策略而无需更改应用程序代码。其结果是在常用页面上获得 SSG 速度。"/search"
页面可能会被预加载,左侧导航栏上的类别和像“tshirt”这样的常用查询也会被预加载。
是的,但是缓存未命中怎么办?
你可能不会相信我,我也没有办法证明我们的缓存是空的,但这里有一个 Remix 中的缓存未命中(我发誓,如果撒谎,愿针扎我眼)。
实际上,我撒谎了。那是 Remix 重写缓存命中。 缓存未命中更快(0.6 秒🤭)。我真的不认为你会相信我,所以我在图形中放了一个较慢的缓存命中😅
不可能!
事实证明 Shopify API 非常快。
由于 Next.js 应用是从浏览器直接获取 Shopify API 的数据,因此我们可以查看 网络图 测试并看到请求仅花费了 224 毫秒。浏览器建立与 API 的连接花费的时间比发出请求的时间还要长!(他们可以通过在初始 HTML 中使用<link rel="preconnect" />
来加快速度。)
如果用户的浏览器可以如此快速地向 Shopify 发出请求,那么 Remix 服务器肯定可以更快地发出请求。用户与云的连接始终会比您的服务器慢,最好将数据获取保留在服务器端。
底线是,使用 Shopify API 时,缓存几乎毫无意义。缓存命中或未命中在实际使用中几乎无法区分。
通过降低用户网络速度并查看发生的情况,可以最好地说明这一点。让我们再进行一次缓存未命中,这次是从香港的 3G 网络。
即使在缓存未命中时,Next.js 现在也落后了 3.5 秒。怎么回事?
你说 Shopify API 很快!
Next.js 必须先加载数据才能加载图片,必须先加载 JavaScript 才能加载数据,必须先加载文档才能加载 JavaScript。用户的网络是该链中每个步骤的倍增器😫。
在 Remix 中,唯一的链是等待文档才能加载图片。Remix 的设计始终在服务器端获取数据,从而消除了用户的网络作为其他所有地方的倍增器。
Remix 可以在收到请求时立即开始从 Shopify 获取数据。它不必等待浏览器下载文档,然后再下载 JavaScript。无论用户的网络速度有多慢,服务器上对 Shopify API 的获取都不会改变,并且可能在 200 毫秒以内。
当 Next.js 转向在客户端获取数据时,受到影响的不仅仅是用户体验。该应用现在有两个不同的抽象集用于与 Shopify 交互:一个用于 SSG,另一个用于浏览器。
这种架构差异引发了一些主要问题
process.env
吗?window.location.origin
吗?(天哪,我说同构了)(这与这篇文章无关)(啊,函子光学!)
让我们针对 Remix 回答这些问题,在 Remix 中,您只需要在服务器端抽象 Shopify API。
process.env
吗?(是)window.location.origin
吗?(否)这些问题越容易回答,您的抽象就越好,从而导致更简单的代码。
如果 Next.js 应用不再使用客户端获取数据,而是使用getServerSideProps
,它们可能会缩小差距并对这些问题给出更简单的答案。有趣的是,Next.js 文档建议您远离服务器端获取数据,并经常转向 SSG 或客户端获取数据
如果您不需要预渲染数据,则应考虑在客户端获取数据。
他们还鼓励在包含用户数据的页面上进行客户端获取数据,再次导致更多的架构差异。
[客户端获取数据]非常适合用户仪表板页面,例如。因为仪表板是私人的用户专用页面,所以 SEO 不相关
正如我们在这里看到的,服务器端渲染也与更好的性能有关,而不仅仅是 SEO。
这里的根本区别在于 Next.js 有四种获取页面数据的方式
getInitialProps
- 在服务器端和客户端调用getServerSideProps
- 在服务器端调用getStaticProps
- 在构建时调用Remix 只有一个:loader
。对一个只在一个地方运行的事物进行抽象比对四个在三个地方运行的事物进行抽象更容易。
让我们尝试量化这种架构差异的成本。也许这个应用中最困难的开发任务是抽象出电子商务后端。该应用的设计方式是您可以将任何内容插入其中:Shopify、BigCommerce、Spree、Saleor 等。
在 Next.js 应用中,Shopify 集成位于此文件夹中。今天在上面运行cloc
得到的结果是
101 text files.
93 unique files.
8 files ignored.
github.com/AlDanial/cloc v 1.92
---------------------------------------------------------------------
Language files blank comment code
---------------------------------------------------------------------
TypeScript 88 616 2256 5328
GraphQL 1 1610 5834 2258
Markdown 1 40 0 95
JSON 2 0 0 39
JavaScript 1 1 0 7
---------------------------------------------------------------------
SUM: 93 2267 8090 7727
---------------------------------------------------------------------
近 100 个文件,近 8000 行代码。我对其他集成进行了运行,结果也是如此。它们都接近 100 个文件,代码行数在 10000 行左右。几乎所有这些代码也都会发送到浏览器。
这就是架构差异的成本。Next.js 的抽象必须预料并参与构建和浏览器。Remix 的抽象只在服务器端。
您可能想知道这两个 Shopify 提供程序的功能集是否相同,也许我们在误导您。其中许多提供程序都包含一些用于身份验证和愿望清单的代码,但 Shopify 提供程序未使用任何一个(但必须为它们导出模块)。使用这两个网站,它们似乎具有相同的功能集。无论如何,如果我们遗漏了什么,很难想象需要 7000 行代码才能实现,而应用中可见的功能仅占其中的 1/10。
即使 Next.js 为搜索页面转向getServerSideProps
,它们仍然需要几乎所有这些代码来实现数据变异功能,但我现在有点跑题了!
我们经常谈论“部署到边缘”。这意味着什么?这是来自香港的另一个缓存未命中,这次是快速的用户网络
这次我们将讨论两个 Remix 应用之间的区别。我们已经知道 Next.js 版本比较慢,因为网络瀑布链的原因。
两个 Remix 应用都在服务器端获取数据,那么为什么 Remix 端口比 Remix 重写慢这么多?
答案很简单:Remix 端口运行在 Vercel 函数中,而 Vercel 的函数不会在边缘运行您的代码,它们运行在一个区域中,默认为华盛顿特区。这离香港很远!
这意味着用户必须从香港一直到达华盛顿特区,服务器才能开始从 Shopify 获取数据。服务器完成后,它必须将文档全部发送回香港。
Remix 重写也在华盛顿特区运行,但它也运行在香港!这意味着用户可以非常快速地连接到 Remix 服务器,所有操作都会更快。
这就像骑自行车去火车站进城,而不是一直骑自行车。
🚲-----------------------------------------🏢
🚲-----🚊====🏢
您可以像往常一样在网络瀑布图中看到这一点。
基础设施的差异体现在文档的第一个蓝色条中。在 Remix 端口中,它更大。那是用户在 Vercel 函数自行车道上骑自行车绕世界半圈。在 Remix 重写中,它搭上了火车,很快到达 Shopify API 并返回。
此版本运行在 Fly.io 上,它可以在全球数十个区域运行 Node.js 服务器。不过,Remix 不依赖于 Node.js。它可以在任何 JavaScript 环境中运行。事实上,它已经在 Cloudflare Workers 中运行,这意味着您的代码运行在 他们的 250 台服务器 上,这些服务器分布在世界各地。再也没有比这更靠近用户了!
这就是我们说 Remix 是“边缘原生”的原因。Next.js 依赖于 Node.js,因此它部署到边缘的能力在今天受到限制。
我们在这个领域还有很多工作要做,以改善开发者体验。目前我们只正式支持 Node.js 和 Cloudflare,但我们正在积极开发 Deno,社区成员已经在 Fastly 上运行了 Remix。
当您使用像 Remix 这样的“边缘原生”框架时,您不再需要决定哪些用户获得更快的体验。无论用户身处世界何地,您都可以为每个用户提供快速的体验。
边缘是 Remix 的构建目标。如您所见,它非常有前景。据我们了解,Vercel 团队也在努力将您的应用部署到边缘。Remix 已做好准备,我们迫不及待地想尝试一下。
这两个框架都通过链接预取启用即时过渡,但 Next.js 仅对由 SSG 创建的页面执行此操作。搜索页面再次失效了。(也许下次吧,伙计)
但是,**Remix 可以预取任何页面,因为数据加载没有架构差异。**预取不可知的、用户驱动的搜索页面 URL 与预取已知的商品 URL 没有什么不同。
事实上,Remix 的预取不仅限于链接,它可以在任何时间、任何原因预取任何页面!请查看此示例,在用户键入时预取搜索页面
没有加载动画,没有骨架屏,即使在缓慢的网络上也能提供即时的用户体验 🏎
这也很容易实现。
import { Form, PrefetchPageLinks } from "@remix-run/react";
function Search() {
let [query, setQuery] = useState("");
return (
<Form>
<input type="text" name="q" onChange={(e) => setQuery(e.target.value)} />
{query && <PrefetchPageLinks page={`/search?q=${query}`} />}
</Form>
);
}
由于 Remix 使用 HTML 的 <link rel="prefetch">
(而不是像 Next.js 这样的内存缓存),因此浏览器实际上会发出请求,而不是 Remix。观看视频,您可以看到请求如何在用户中断当前获取时被取消。Remix 不需要为异步处理的顶级处理发送任何代码。#useThePlatform ... 或者,#reuseThePlatform 😎?!
这是 Remix 和 Next.js 开始看起来完全不同的位置。您应用代码的一半与数据变异有关。现在是时候让您的 Web 框架尊重这一点。
**Next.js 中变异的工作原理:**Next.js 在这里什么也不为您做。<button onClick={itsAllUpToYou}>
。通常,您需要管理表单的状态以知道要发布什么内容,添加一个 API 路由以发布到该路由,自己跟踪加载和错误状态,重新验证数据并在整个 UI 中传播更改,最后处理错误、中断和竞争条件(但说实话,没有人真正处理这些东西)。
**Remix 中变异的工作原理:**Remix 使用 HTML 表单。我知道你在想什么。
切...我正在构建一个 Web 应用,这永远不会起作用。
您可能认为您即将看到的 API 无法处理现代 Web 应用的需求。高交互性 Web 应用一直是我的整个职业生涯,Remix 就是为此而设计的。仅仅因为它看起来像过去时代的 PHP,并不意味着它无法扩展到现代、复杂的用户体验。我们喜欢说 Remix 可以向上扩展,但它也可以向下扩展。所以让我们回到过去时代,帮助您了解 Remix。
从 Web 诞生之日起,变异就被建模为表单和处理它的服务器页面。完全忽略 Remix,它看起来像这样
<form method="post" action="/add-to-cart">
<input type="hidden" name="productId" value="123" />
<button>Add to Cart</button>
</form>
// on the server at `/add-to-cart`
export async function action(request) {
let formData = await request.formData();
return addToCart(formData);
}
浏览器使用表单序列化数据 POST 到 "/add-to-cart"
,添加待处理的 UI,并在完成后呈现一个包含数据库中所有最新数据的新页面。
Remix 与 HTML 表单执行相同的操作,只是使用大写的 F <Form>
和路由上名为 action
的函数进行了优化(想象一下您的 Next.js 页面是它们自己的 API 路由)。它使用 fetch
而不是文档重新加载进行发布,然后使用服务器重新验证页面上的所有数据,以使 UI 与后端保持同步。这与您在 SPA 中习惯的操作相同,**只是 Remix 为您管理所有这些操作**。
除了表单和服务器端操作之外,不需要任何应用程序代码来与服务器通信变异。也没有任何应用程序上下文提供程序或全局状态管理技巧来将更改传播到 UI 的其他部分。这就是为什么 Remix 包比 Next.js 包小近 30% 的原因,您不需要所有这些代码来与您的“API 路由”通信。
哎呀,我又撒谎了。该代码实际上在 Remix 中有效。如果您使用小写的 <form>
,浏览器将处理发布而不是 Remix。对于 JavaScript 无法加载的情况非常方便😅 (稍后详细介绍)
您可以通过询问 Remix 关于繁忙的加载动画和进度或正在发布的数据的过渡来扩展到花哨的 UI 以创建乐观 UI。模型是 HTML 表单,功能是您的设计师想到的任何内容。您不必完全重新设计您的实现来说“没问题,我们可以做到”。
更小的包和简单的变异 API 并不是 Remix 为您提供的唯一功能。
因为 Remix 处理您与服务器的所有交互(包括数据加载和数据变异),所以在 Web 框架领域它具有独特的能力来解决 Web 应用长期存在的问题。
当“添加到购物车”后端处理程序抛出错误时会发生什么?在这里,我们阻止对添加商品到购物车的路由的请求以查看会发生什么。
没有任何反应。错误处理很困难且令人烦恼。许多开发人员像这里一样跳过了它。我们认为这是一种糟糕的默认用户体验。
让我们看看 Remix 中会发生什么。
Remix 处理应用中围绕数据和渲染的每个错误,甚至包括服务器上的错误。
您只需在应用的根目录定义一个 错误边界 即可。您甚至可以更细化,只使出现错误的页面部分失效。
Remix 可以做到这一点而 Next.js 做不到的唯一原因是,Remix 的数据抽象不仅停止在如何将数据引入应用,还包括如何更改数据。
用户经常会意外地点击两次按钮,大多数应用都没有很好地处理这种情况。但有时您有一个按钮,您完全期望用户快速点击它,并希望 UI 立即响应。
在此应用中,用户可以更改购物车中的商品数量。他们可能会非常快速地点击它以增加数量几次。
让我们看看 Next.js 应用如何处理中断
很难确切地看到发生了什么,但如果擦洗视频控件,您可以更好地看到它。中间从 5 到 6 再到 5 有一个奇怪的东西。最后几秒钟最有趣。您可以看到发送的最后一个请求成功了(到 4),然后几帧后发送的第一个请求成功了!数量字段在没有任何用户交互的情况下从 5 跳到 4,再跳到 2。很难相信这种 UI。
此代码没有管理竞争条件、中断或重新验证,因此 UI 现在可能与服务器不同步(这取决于 2 或 4 是否是最后一个到达服务器端代码)。管理中断并在变异后重新验证数据可以防止这种情况。
我知道,处理竞争条件和中断很难!这就是为什么大多数应用都不这样做。Vercel 团队是业内最有才华的开发团队之一,即使是他们也跳过了它。
事实上,当我们在我们上一篇博文中将 React 核心团队构建的 React 服务器组件示例移植过来时,**他们也遇到了同样的错误**。
我之前说过我们对网络选项卡非常狂热。让我们看看 Remix 如何处理这个问题。
您可以看到 Remix 在中断时取消请求并在 POST 完成后重新验证数据。这确保了整个页面(不仅仅是此表单)上的 UI 与表单刚刚对服务器进行的任何更改保持同步。
您可能会认为,也许我们的应用比 Next.js 应用更注重细节。所有这些行为都不在应用代码中。它们都内置在 Remix 的数据变异 API 中。(它实际上只是在做浏览器使用 HTML 表单所做的事情...)
Remix 中客户端和服务器之间无缝的集成和过渡是前所未有的。
在我们几十年的 Web 开发生涯中,我们记得它曾经是多么简单。在表单中放置一个按钮,将其指向写入数据库的页面,重定向,获取更新的 UI。这太容易了。
在设计 Remix API 时,我们始终首先关注平台。就像变异工作流程一样。我们知道 HTML 表单 API + 服务器端处理程序是正确的,所以我们围绕它构建。这不是目标,但一个非常惊人的副作用是,规范 Remix 应用的核心功能无需 JavaScript 即可工作!
虽然完全可以使用这种方式使用 Remix,但我们并不打算让您构建没有 JavaScript 的网站。我们对构建令人惊叹的用户界面有很多雄心壮志,为此您需要 JavaScript。
与其说“Remix 无需 JavaScript 即可工作”,我们更愿意说“Remix 在 JavaScript **之前**即可工作”。也许您的用户在页面加载 JavaScript 时刚刚进入火车隧道。当他们重新出现时,页面通常仍然可以工作。我们只是追求 HTML 的简单性,但最终得到一个非常强大的框架。
我们也希望使用 Web 平台编写你的服务器端代码。Remix 没有发明新的 JavaScript 请求/响应 API,而是使用了 Web Fetch API。对于 URL 搜索参数,我们使用内置的 URLSearchParams
。对于表单数据,我们使用内置的 FormData
。
export function loader({ request }) {
// request is a standard web fetch request
let url = new URL(request.url);
// remix doesn't do non-standard search param parsing,
// you use the built in URLSearchParams object
let query = url.searchParams.get("q");
}
export function action({ request }) {
// formData is part of the web fetch api
let formData = await request.formData();
}
你会发现,当你开始学习 Remix 时,你会花同样多的时间(甚至更多)在 MDN 文档上,而不是 Remix 文档上。我们希望 Remix 能够帮助你构建更好的网站,即使你没有使用它。
学习 Remix,意外地提升你的 Web 技能。
这是我们的核心价值观。虽然 Remix 应用非常快,但我们实际上并没有过度关注性能,而是专注于提供出色的用户和开发者体验。我们寻求平台提供的解决方案,使它们更易于使用,性能通常会随之得到改善。
现在你已经了解了这两个框架的工作原理,让我们看看这些应用如何应对变化。我一直很喜欢“优化变化”这句话,在设计 Remix API 时,我们也经常提到这一点。
假设你想要更改主页上的产品,那该怎么做?在 Next.js 中你有两种选择
重建并重新部署你的应用。你的构建时间将随着商店中产品数量的线性增长(每次构建都必须从 Shopify 获取每个产品的数据)。即使只是更改页脚中的一个错别字,也需要从 Shopify 下载所有产品才能部署该更改。随着商店产品数量增长到数千个,这将成为一个问题。
使用 增量静态再生 (ISR)。Vercel 认识到 SSG 构建时间存在的问题,因此他们创建了 ISR。当请求页面时,服务器会发送缓存版本,然后在后台使用最新数据重新构建它。下一个访问者将获得新缓存的版本。
如果在部署时没有构建页面,Next.js 将服务器端渲染页面,然后将其缓存到 CDN 上。这与 HTTP stale-while-revalidate
完全相同,只是 ISR 带有一个非标准 API 和供应商锁定。
在 Remix 中,你只需使用 Shopify 更新你的产品,你的产品将在你的缓存 TTL 内更新。你也可以在半天内设置一个 webhook 来使主页查询失效。
这种基础设施比使用 SSG 更加复杂,但正如我们在本文中看到的,它可以扩展到任何规模的产品目录、任何类型的 UI(搜索页面),并且随着用户数量的增加,实际上比 SSG 速度更快(我们可以缓存常见的搜索查询)。你也不受特定主机的限制,并且几乎不受框架的限制,因为 Remix 主要使用标准的 Web API 来进行应用程序逻辑。
此外,我们认为只以一种方式(在服务器端)加载数据可以带来更清晰的抽象。
这是一个很好的问题。服务器和 HTTP 缓存仅在你的网站获得流量时才有效。事实证明,你的业务也只有在你的网站获得流量时才能运作😳。你不需要每天有两次页面浏览才能快一秒,你需要一个邮件列表。
如果缓存未命中请求占你访问量的很大一部分,那么获得 100% 的缓存命中率并不能解决你的业务问题:你遇到的不是技术问题,而是营销问题。
让我们看看另一种变化。假设产品团队找到你,说主页要更改为显示用户过去购买过的类似产品,而不是一组固定的产品列表。
就像搜索页面一样,SSG 被淘汰了,默认情况下,它的性能也随之消失了。SSG 的用例确实非常有限。
几乎每个网站都有用户。随着网站的增长,你将开始向用户展示越来越多的个性化信息。每次都变成客户端获取。在某些时候,你页面的大部分内容都是客户端获取的,性能就会下降。
对于 Remix 来说,这只是后端的一个不同的数据库查询。
想想电子商务行业的顶端:Amazon.com。整个页面都是个性化的。我们一开始就知道最终结果。投资能够让你实现目标的架构,而不是在产品团队调整主页时需要放弃的东西。
很容易忽略 Remix 中看似简单的 <Form>
+ action
+ loader
API 以及尽可能多地将内容保留在服务器端的设计所带来的强大功能。它改变了游戏规则。这些 API 是 Remix 页面加载速度更快、转换速度更快、突变(中断、竞争条件、错误)方面的 UX 更好以及开发者代码更简单的源泉。
Remix 应用的速度来自后端基础设施和预取。Next.js 的速度来自 SSG。由于 SSG 的用例有限,尤其是在功能和数据扩展时,你将失去这种速度。
SSG 和 Jamstack 是解决后端服务速度慢的绝佳变通方案。最新一代的平台和数据库速度很快,并且还在不断加快。即使是支持这些应用的 Shopify API 也能在 200 毫秒内从世界上几乎任何地方发送查询响应,我已经在除了南极洲之外的每个大陆测试过!(这个月,当 @chancethedev 在南极洲时,需要请他帮我测试一下。)
老实说,完全可以跳过本文讨论的所有缓存策略,并在服务器上的每个请求中都访问 Shopify API。加载时间将从 1.2 秒变为 1.4 秒。从 0.8 秒变为 1 秒。微不足道。如果你的后端 API 速度很慢,请投入时间让你的后端变得更快。如果你无法控制它,请部署你自己的服务器并在那里缓存,这样你就可以为所有用户加快任何页面的速度。
投资后端将产生与 SSG 相同的性能结果,但它可以扩展到任何类型的页面。它需要比 SSG 更多的初始工作,但我们认为从长远来看,这对于你的用户和代码来说是值得的。
数据加载也只是一半的故事。在 Remix 中,你的数据抽象还可以封装数据突变。所有代码都保留在服务器端,从而带来更好的应用程序代码和更小的浏览器包。
使用 Next.js,你必须将自己的数据突变代码发送到浏览器才能与 API 路由交互并将更新传播到 UI 的其他部分。正如我们在本文中看到的,即使是顶尖的团队也会在错误、中断和竞争条件方面搞砸。
你是否忽略了
getServerSideProps
?
有些人说你可以使用 getServerSideProps
做 Remix 所做的一切。这个问题源于我们还没有机会很好地解释 Remix!
如前所述,这肯定会加快搜索页面的速度。但是,你仍然需要处理数据突变。你需要结合使用 getServerSideProps
、API 路由以及你自己的与它们进行突变通信的浏览器代码(包括错误处理、中断、竞争条件、重定向和重新验证)。我们在这里真正想说的是“你可以构建你自己的 Remix”。确实,你可以。我们已经做到了😇。
呼!
现在我们已经回答了每个人都在问我们的那个大问题,我们未来的文章将真正开始展示 Remix 的强大功能!