很容易,我们被问到的最大问题是:
Remix与Next.js有何不同?
看来我们必须回答这个问题!我们想直接而不带戏剧性地解决这个问题。如果你是Remix的粉丝,并且想开始在推特上对这篇文章做出沾沾自喜的反应,我们恳请你在点击推特按钮之前不要沾沾自自喜🤗. 涨潮使所有的船都浮起来。早在Vercel成立之前,我们就和Vercel的人是朋友。他们做得很好,我们尊重他们所做的工作!
但毫无疑问,我们认为Remix比Next.js有更好的折衷方案。(否则我们就不会构建它…)
我们鼓励您阅读整篇文章。在这段对话中,有很多细微之处没有在闪亮的图表和动画中捕捉到。最后,希望你能考虑在下一个项目中使用Remix(并非双关语😂).
tl;dr
- Remix在提供静态内容方面与Next.js一样快或更快
- Remix在提供动态内容方面比Next.js更快
- Remix即使在慢速网络上也能提供快速的用户体验
- Remix可以自动处理错误、中断和比赛情况,Next.js则不会
- Next.js鼓励客户端JavaScript提供动态内容,Remix没有
- Next.js需要客户端JavaScript进行数据突变,Remix不需要
- Next.js构建时间随着数据线性增加,Remix构建时间几乎是即时的,并且与数据解耦
- Next.js要求您更改应用程序架构,并在数据扩展时牺牲性能
- 我们认为Remix的抽象会带来更好的应用程序代码
背景
我们认为比较这些框架的最公平的方法是以Vercel团队自己编写的Next.js应用程序为例。既然他们写了,他们所做的决定应该反映出他们打算如何构建你的应用程序。它还应该展示Vercel团队最引以为豪的功能。
我们从Next.js示例页面移植了Commerce示例。它有一些我们喜欢的现实世界功能,似乎是他们投入最多精力的功能。
- 初始页面加载对电子商务至关重要
- 搜索页面上的动态数据
- 购物车的数据突变
- 能够与多个提供者集成,说明框架如何帮助您抽象
我们实际上构建了两个版本:
- 最小端口-我们只是复制/粘贴/调整Next.js代码,使其在Remix上运行,而不是在Next.js.这与Next.jsDemo一样部署到Vercel。这是一个很好的框架比较,因为除了框架之外的所有东西都是一样的。
- 重写-这两个框架实际上没有太多API重叠,Remix可以在不同于Next.js的基础设施上运行。为了真正实践Remix的设计,我们将示例重写为惯用的Remix,甚至在应用程序中建立了一个快速的图像优化路线,使其成为100%的Remix。
请注意,这个应用程序并不能锻炼我们认为Remix很酷的一切(比如嵌套路线!)。一旦我们回答了这个问题,我们就可以继续谈论remix了,所以请继续关注!
此外,我们在发布之前与Vercel分享了这篇文章。事实证明,他们的例子是在旧版本的Next.js上运行的,他们对其进行了更新,所以我们花时间对其进行返工,以与他们的最新例子进行比较。
说真的,我们喜欢Vercel
我们认为他们是朋友,甚至是合作伙伴,因为Vercel是Remix的优秀部署目标。我已经将Remix应用程序部署到了几乎所有我听说过的托管服务中。Vercel的开发经验无疑是我的最爱。“开发、预览、发货”的咒语具有实际效果。就在今天早上,@gt_codes和我正试图找出一个生产错误,让每个预览部署都有一个小屏幕截图,可以帮助我们在几秒钟内找到错误的提交。这是好东西。
现在这是一种有趣的关系,因为我们不仅仅是朋友和技术合作伙伴,我们还是框架竞争对手!因此,对于我们的朋友、合作伙伴和竞争对手Vercel,Lee精彩地阐述了这篇文章背后的动机:
当DevTools存在竞争时,开发人员会胜出:
- ◆ Svelte正在推动React
- ◆ Remix正在推Next.js
- ◆ Prisma正在推动ORM
- ◆ Deno正在推Node.js
- ◆ Supadase正在推动Firebase
- ◆ esbuild/SWC正在推动JS工具
- ◆ Bun正在推动SWC
- 还有什么?
- --Lee Robinson(@leeerob)2021年11月30日
请在阅读这篇文章的背景下阅读。让我们加油!
自我描述
我想你可以通过建造它的人对它的描述来了解很多。(如果你在推特上关注我,你就会知道我一直在努力迭代我们的!)
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网站没有它。让我们用慢镜头来看它:
主页,弗吉尼亚州,Cable,Slow Mo
现在我们可以看到Next.js实际上完成了0.8s。同样,它们都很快。我还用3G网络连接对它们进行了同样的测试,结果是一样的:速度都很快,看起来都差不多。
✅ Remix和Next.js一样快
为什么应用程序很快
为什么Next.js很快:主页使用带有getStaticProps的静态站点生成(SSG)。在构建时,Next.js从Shopify中提取数据,将页面呈现为HTML文件,并将其放在公共目录中。当部署站点时,静态文件现在在边缘(Vercel的CDN之外)提供服务,而不是在单个位置访问原始服务器。当收到请求时,CDN只是为文件提供服务。数据加载和渲染已经提前完成,因此访问者不需要支付下载+渲染的费用。此外,CDN在全球范围内分布,靠近用户(这是“边缘”),因此对静态生成的文档的请求不必一直传递到单个源服务器。
为什么Remix端口很快:Remix不支持SSG,所以我们在重新验证缓存指令时使用了HTTP过时指令(SWR,不能与Vercel的SWR客户端获取包融合)。最终的结果是一样的:边缘的静态文档(即使在相同的CDN上,Vercel的)。不同的是文件是如何到达那里的。
不是在构建/部署时获取所有数据并将页面呈现为静态文档,而是在获取流量时启动缓存。文档从缓存中提供,并在后台为下一个访问者重新验证。与SSG一样,当你获得流量时,没有访问者支付下载+渲染成本。如果您想知道缓存未命中,我们稍后会讨论。
SWR是SSG的一个很好的替代方案。部署到Vercel的另一个好处是他们的CDN支持它。
你可能想知道为什么Remix端口没有Next.js那么快。由于Remix还没有内置图像优化功能,我们只是将图像指向Next.jss应用程序🤫. 浏览器必须打开与两个域的连接,这会将图像加载延迟0.3秒(您可以在网络瀑布上验证这一点)。如果这些图像是自托管的,那么它就在那里,其他两个大约在0.7秒。
为什么Remix重写很快:这个版本不是用SSG或SWR在边缘缓存文档,而是用Redis在边缘缓存数据。事实上,它实际上也使用Fly.io在边缘运行应用程序。最后,它有一个快速的图像优化资源路由,可以写入持久卷。它基本上是自己的CDN😎.
这在几年前可能很难构建,但在过去几年中,服务器环境发生了重大变化,而且只会变得更好。
加载动态页面
Remix与Next.js有何不同?
这是我们的下一个问题。功能集有很多不同,但一个主要的架构差异是Remix不依赖SSG来提高速度。
在几乎每一个应用程序中,你最终都会遇到SSG无法支持的情况。对于我们在这里比较的应用程序,它是搜索页面。
限制条件是用户可以提交无限数量的查询。由于宇宙当前对空间和时间的限制,您无法静态生成无限页面。SSG不在谈判桌上。
Search Page, Cached, Virginia, Cable
由于SSG不能扩展到动态页面,Next.js切换到从用户浏览器获取客户端数据。在网络瀑布上达到峰值会告诉我们为什么它比Remix慢2.3倍。
在Next.js应用程序开始加载图像之前,Remix应用程序就已经完全完成了。也许在web性能方面最重要的事情是并行化网络瀑布。在Remix,我们对此非常狂热。
为什么Next.js较慢:Next.js引入了我们所说的“网络瀑布请求链”。因为这里不能使用SSG,所以该应用程序从用户的浏览器中获取搜索结果。在获取数据之前,它无法加载图像,在加载、解析和评估JavaScript之前,它也无法获取数据。
在客户端获取还意味着在网络上有更多的JavaScript,以及更多的解析/评估时间。有时我们忘记了parse/eval,但您可以看到JS在第15个请求上的执行时间比文档下载的时间长!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,我们可以查看测试的网络图,发现请求只花了224ms。浏览器与API建立连接的时间比发出请求的时间长!(他们可以在最初的HTML中使用<link rel=“preconnect”/>来加快速度。)
如果用户的浏览器能够如此迅速地向Shopify发出请求,那么Remix服务器肯定可以更快地完成。用户到云的连接总是比服务器慢,最好是保持数据获取。
底线是,当使用Shopify API时,缓存几乎是毫无意义的。缓存命中或未命中将几乎无法区分。
这可以通过减慢用户的网络速度并查看发生了什么来最好地说明。让我们再做一次缓存丢失,这次是从香港通过3G连接。
搜索页面,空缓存,香港,3G
Next.js现在落后3.5秒,即使是缓存未命中。怎么了?
你说Shopify API很快!
Next.js在加载数据之前不能加载图像,在加载JavaScript之前不能加载数据,在加载文档之前不能加载JavaScript。用户的网络是该链中每一步的乘数😫.
在Remix中,唯一的环节是等待文档能够加载图像。总是在服务器上获取的Remix设计消除了用户的网络在其他地方的乘数。
收到请求后,Remix可以立即开始从Shopify获取。它不必等待浏览器下载文档,然后再下载JavaScript。无论用户的网络速度有多慢,服务器上Shopify API的获取不会改变,可能低于200ms。
架构差异
当Next.js转向客户端抓取时,用户体验并不是唯一受到影响的东西。该应用程序现在有两组不同的抽象用于与Shopify对话:一组用于SSG,另一组用于浏览器。
像这样的架构分歧带来了一些主要问题:
- 你必须在浏览器中进行身份验证吗?
- API是否支持CORS?
- API SDK甚至可以在浏览器中工作吗?
- 我们如何在构建代码和浏览器代码之间共享代码?
- 在浏览器中公开API令牌可以吗?
- 我们刚刚发给每位访客的代币有什么权限?
- 这个函数可以使用process.env吗?
- 这个函数能读取window.location.origin吗?
- 如何发出在两个地方都有效的网络请求?
- 我们可以将这些响应缓存在某个地方吗?
- 我们应该制作一个在两个地方都能工作的同构缓存对象,并将其传递给不同的数据获取函数吗?
(我说的是同构的)(这与这个帖子正交)(gah PROFUNCTOR OPTICS!)
让我们为Remix回答以下问题,您只需在服务器上抽象Shopify API:
- 你必须在浏览器中进行身份验证吗?(否)
- API是否支持CORS?(没关系)
- API SDK甚至可以在浏览器中工作吗?(不需要)
- 我们如何在构建代码和浏览器代码之间共享代码?(你不必)
- 在浏览器中公开API令牌可以吗?(不需要)
- 我们刚刚发给每位访客的代币有什么权限?(你没有!)
- 这个函数可以使用process.env吗?(是)
- 这个函数能读取window.location.origin吗?(否)
- 如何发出在两个地方都有效的网络请求?(无论您想要什么,它都不在浏览器中)
- 我们可以将这些响应缓存在某个地方吗?(当然,HTTP、redis、lru缓存、持久卷、sqlite…)
- 我们应该制作一个在两个地方都能工作的同构缓存对象,并将其传递给不同的数据获取函数吗?(不需要!)
这些问题回答得越简单,抽象就越好,从而产生更简单的代码。
如果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行代码上徘徊。几乎所有的代码都能进入浏览器。
以下是与Shopify的remix集成。
- 1个文件
- 608行代码
- 这些都没有进入浏览器
这就是架构差异的代价。Next.js抽象必须预测并参与构建和浏览器。Remix抽象仅在服务器上。
你可能想知道这两家Shopify提供商是否有相同的功能集,也许我们是在欺骗。其中许多代码用于身份验证和愿望清单,但Shopify提供商没有使用任何一个(但必须为它们导出模块)。使用这两个网站,它们似乎具有相同的功能集。无论如何,如果我们确实错过了什么,很难想象当应用程序中的可见功能只占其中的十分之一时,需要7000行代码才能到达那里。
即使Next.js移到了搜索页面的getServerSideProps,他们仍然需要几乎所有的代码来实现数据突变功能,但我现在已经超越了自己!
边缘本机
我们经常谈论“部署到边缘”。这是什么意思?以下是另一个来自香港的缓存未命中,这一次使用快速用户网络:
搜索页面,空缓存,香港,有线
这一次我们将讨论两个Remix应用程序之间的区别。我们已经知道Next.js版本由于网络瀑布链的原因速度较慢。
两个Remix应用程序都在服务器上获取,那么为什么Remix端口远远落后于Remix Rewrite呢?
答案很简单:Remix端口在Vercel函数中运行,而Vercel的函数不会在边缘运行您的代码,它们在一个地区运行,默认为华盛顿特区。那离香港很远!
这意味着用户必须从香港一路到达华盛顿特区,然后服务器才能开始从Shopify获取数据。当服务器完成后,它必须将文档一直发送回。
Remix Rewrite也在华盛顿特区运行,但它也在香港运行!这意味着用户可以非常快速地跳转到Remix服务器,在那里一切都会更快。
这就像骑自行车去火车进城,而不是全程骑自行车。
🚲-----------------------------------------🏢
🚲-----🚊====🏢
你可以在网络瀑布中看到这一幕(和往常一样):
基础结构的差异体现在文档的第一个蓝色条中。在remix港,它要大得多。这是用户在Vercel功能自行车道上骑自行车环游半个世界。在Remix Rewrite中,它上了火车,进入了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预取不仅仅限于链接,它可以在任何时间、任何原因预取任何页面!请检查此项,在用户键入时预取搜索页面:
搜索输入预取,快速3G
0:00
/ 0:07
没有微调器,没有骨架,即时用户体验,即使在慢速网络上也是如此🏎
这也非常容易做到。
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不需要发送一个字符的代码就可以实现一流的异步处理#使用平台。。。或者,呃,#reuseThePlatform😎?!
数据突变
这就是Remix和Next.js看起来完全不同的地方。你的应用程序代码有一半与数据突变有关。是时候让你的网络框架尊重这一点了。
突变是如何在Next.js中工作的:Next.js在这里对你没有任何帮助<按钮单击={itsAllUpToYou}>。通常,您将管理表单的状态以了解发布内容,添加要发布到的API路由,自己跟踪加载和错误状态,重新验证数据并在整个UI中传播更改,最后处理错误、中断和竞争条件(但老实说,实际上没有人处理这些事情)。
突变在remix中的作用:remix使用HTML形式。我知道你在想什么。
pffft。。。我正在构建一个网络应用程序,这永远不会奏效。
你可能会认为你即将看到的API看起来无法满足现代网络应用程序的需求。高度互动的网络应用程序一直是我的整个职业生涯,Remix的设计就是考虑到了它们。仅仅因为它看起来像过去的PHP,并不意味着它不能扩展到现代、复杂的用户体验。我们喜欢说remix是放大的,但它也缩小了。让我们回到过去,帮助你理解remix。
自从网络出现以来,一个突变被建模为一个表单和一个服务器页面来处理它。完全忽略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表单相同,只是使用了capital-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应用程序的长期问题。
未处理的错误
当“添加到购物车”后端处理程序抛出错误时会发生什么?在这里,我们阻止对将项目添加到购物车的路线的请求,看看会发生什么。
Next.js POST失败
Next.js Failed POST
什么也没发生。错误处理既困难又烦人。许多开发人员只是跳过它,就像他们在这里做的那样。我们认为这是一个糟糕的默认用户体验。
让我们看看remix会发生什么。
Remix Failed POST
Remix处理应用程序中数据和渲染的每一个错误,甚至服务器上的错误。
你所要做的就是在应用程序的根目录下定义一个错误边界。您甚至可以变得更精细,只删除页面中出现错误的部分。
Remix能做到这一点而Next.js不能做到的唯一原因是,Remix的数据抽象并没有停留在如何将数据导入应用程序,以及如何更改数据上。
中断
用户经常在意外事件中点击两次按钮,大多数应用程序处理得不太好。但有时你有一个按钮,你完全希望用户点击得很快,并希望UI立即做出响应。
在此应用程序中,用户可以更改购物车中的商品数量。他们很可能会很快点击它,将数字增加几次。
让我们看看Next.js应用程序如何处理中断
Next.js Interruption
要准确地看到发生了什么有点困难,但如果你擦洗视频控件,你可以更好地看到它。有一个奇怪的东西从5到6到5在中间。不过最后几秒是最有趣的。你可以看到,最后一个请求发送了着陆(转到4),然后几帧后,第一个请求发送着陆!数量字段从5跳到4,再跳到2,没有任何用户交互。有点难以信任的UI。
此代码没有管理竞争条件、中断或重新验证,因此UI现在可能与服务器不同步(这取决于2或4是最后一个到达服务器端代码)。管理中断并在突变后重新验证数据本可以防止这种情况的发生。
我明白了,处理比赛条件和干扰很难!这就是为什么大多数应用程序都不这么做的原因。Vercel团队是业内最有天赋的开发团队之一,甚至他们都跳过了。
事实上,当我们在上一篇博客文章中移植React Core团队构建的React Server Components示例时,他们也有同样的错误。
我之前说过,我们对网络标签非常狂热。让我们看看Remix是如何处理这一问题的。
Remix Interruption
您可以看到Remix在中断时取消请求,并在POST完成后重新验证数据。这样可以确保整个页面上的UI(而不仅仅是这个表单)与表单在服务器上所做的任何更改同步。
你可能会认为,也许我们的应用程序比Next.js应用程序更注重细节。这些行为都不在应用程序代码中。这一切都内置于Remix的数据突变API中。(它实际上只是在做浏览器对HTML表单所做的事情…)
Remix中客户端和服务器之间的无缝集成和过渡是前所未有的。
Remix❤️ Web
在我们几十年的网络开发生涯中,我们记得它曾经是多么简单。在表格中放一个按钮,指向写入数据库的页面,重定向,获得更新的UI。这太容易了。
在设计Remix API时,我们总是首先关注平台。就像突变工作流程一样。我们知道HTML表单API+服务器端处理程序是正确的,所以我们围绕这一点进行构建。这不是目标,但一个非常惊人的副作用是,一个惯用的Remix应用程序的核心功能在没有JavaScript的情况下工作!
Remix Without JavaScript
虽然以这种方式使用Remix是完全有效的,但我们并不希望您在没有JavaScript的情况下构建网站。我们有很大的雄心来构建令人惊叹的用户界面,你需要JavaScript来实现这一点。
与其说“Remix在没有JavaScript的情况下工作”,我们更愿意说“RemixforJavascript”。也许你的用户只是在页面加载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方面做得更好,在网络方面意外地做得更好。
这是我们的核心价值。虽然Remix应用程序的速度非常快,但我们实际上并没有过度关注性能,只是关注出色的用户和开发人员体验。我们向平台寻求问题的答案,使其更易于使用,并且性能通常会自行处理。
针对变化进行优化
既然你知道了这两个框架是如何运作的,那么让我们看看应用程序是如何应对变化的。我一直很喜欢“优化以适应变化”这句话,当我们设计Remix API时,我们经常谈到这一点。
更改主页
让我们考虑一下你想更改主页上的产品,那是什么样子的?Next.js中有两个选项:
- 重建并重新部署您的应用程序。您的构建时间将随着商店中产品的数量线性增长(每个构建都必须从Shopify中提取每个产品的数据)。只需更改页脚中的拼写错误,就需要从Shopify下载每一款产品来部署更改。随着你的商店发展到成千上万的产品,这将成为一个问题。
- 使用增量静态再生。Vercel认识到SSG中构建时间的问题,因此他们创建了ISR。当请求页面时,服务器会发送缓存的版本,然后在后台使用新数据重新构建它。下一个访问者得到的是新缓存的版本。
如果在部署时没有构建页面,Next.js将服务器呈现页面,然后将其缓存在CDN上。这正是HTTP stale-wile-revalidate所做的,除了ISR带有非标准的API和供应商锁定。
在Remix中,您只需使用Shopify更新您的产品,您的产品就会在缓存TTL内更新。您还可以在下午设置一个webhook来使主页查询无效。
这个基础设施比使用SSG需要更多的工作,但正如我们在本文中所看到的,它可以扩展到任何大小的产品目录、任何类型的UI(搜索页面),而且实际上比SSG更快,用户更多(我们可以缓存常见的搜索查询)。您也没有耦合到特定的主机,也几乎没有耦合到框架,因为Remix主要使用标准的web API来实现应用程序逻辑。
此外,我们认为在服务器上仅以一种方式加载数据会导致更干净的抽象。
缓存未命中怎么办?
这是一个很好的问题。服务器和HTTP缓存只有在您的网站获得流量时才能工作。事实证明,只有当你的网站也获得流量时,你的业务才能运作😳. 你不需要一天两次浏览页面就能快一秒,你需要一个邮件列表。
- Remix中产品页面的空缓存命中率并不比Next.js网站中的搜索页面慢(在那里它不能使用SSG)。你上一次不搜索就在网上购物是什么时候?当缓存中充满了常见查询时,速度会更快。
- 常见的登录页通常都会被准备好,然后Remix的预取会使下一个过渡瞬间发生。Remix可以预取任何页面,无论是动态页面还是其他页面,Next.js都不能。
- 在使用SSG达到一定规模时,您需要切换到ISR。现在,在不属于上次部署的页面上,您也遇到了同样的缓存未命中问题
如果缓存未命中请求在你的访问中占很大一部分,那么获得100%的缓存命中率并不能解决你的业务:你没有技术问题,你有营销问题。
个性化
让我们看看另一个变化。想象一下,产品团队来找你,说主页正在更改,以显示与用户过去购买的产品类似的产品,而不是一组产品列表。
和搜索页面一样,SSG是不存在的,默认情况下你的性能也是如此。SSG的用例集确实有限。
几乎每个网站都有用户。随着网站的发展,您将开始向用户显示越来越多的个性化信息。每一次,它都会变成客户端获取。在某种程度上,您的页面的大部分都是客户端获取的,您的性能也随之下降。
对于Remix来说,这只是后端的一个不同的数据库查询。
想想电子商务食物链的顶端:亚马逊。整个页面都是个性化的。我们从一开始就知道结局。投资于能让你达到目标的架构,而不是产品团队调整主页时需要放弃的东西。
要旨
很容易错过Remix看似简单的<Form>+操作+加载程序API的强大功能,以及尽可能多地保留在服务器上的设计。它改变了游戏。这些API是Remix更快的页面加载、更快的转换、更好的用户体验(中断、竞争条件、错误)以及更简单的开发代码的来源。
Remix应用程序的速度来自后端基础设施和预取。Next.js的速度来自SSG。由于SSG的用例有限,特别是在功能和数据扩展方面,您将失去这种速度。
SSG和Jamstack是解决后端服务速度慢的好办法。最新一代的平台和数据库速度很快,而且只会越来越快。即使是支持这些应用程序的Shopify API也可以在200毫秒内从世界上几乎任何地方发送对查询的响应,我在除了南极洲之外的所有大陆都进行了测试!(这个月他来的时候,需要@chancedev帮我试试。)
老实说,跳过本文讨论的所有缓存策略并在服务器上的每个请求中使用Shopify API是完全可以接受的。而不是1.2秒的负载,它将是1.4。它将是1,而不是0.8秒。布普基斯。如果你有一个缓慢的后端API,投入你的时间使你的后端快速。如果你无法控制它,可以部署你自己的服务器并在那里缓存,这样你就可以为所有用户加速任何页面。
投资于后端将产生与SSG相同的性能结果,但它可以扩展到任何类型的页面。它将需要比SSG更多的初始工作,但从长远来看,我们认为这对您的用户和代码来说是值得的。
数据加载也只是故事的一半。在Remix中,您的数据抽象还可以封装数据突变。所有代码都留在服务器上,从而在浏览器中生成更好的应用程序代码和更小的捆绑包。
使用Next.js,您必须将自己的数据变异代码发送到浏览器,以便与API路由交互,并将更新传播到UI的其余部分。正如我们在这篇文章中看到的,即使是顶级球队也会因为失误、中断和比赛条件而陷入混乱。
你不是在忽略getServerSideProps吗?
有些人说你可以用getServerSideProps完成Remix所做的所有事情。这个问题来自于我们还没有机会很好地解释Remix!
如前所述,这肯定会加快搜索页面的速度。然而,您仍然需要处理数据突变。您将需要getServerSideProps、API路由和您自己的浏览器代码的组合,以便与它们进行通信以进行突变(包括错误处理、中断、竞争条件、重定向和重新验证)。我们在这里真正要说的是“你可以建立自己的remix”。的确,你可以。我们已经做到了
- 登录 发表评论