Next.js vs Remix - 開(kāi)發(fā)者的困境
React 生態(tài)系統(tǒng)是一個(gè)繁榮的景觀,充滿(mǎn)了承諾革新網(wǎng)絡(luò)開(kāi)發(fā)的框架。今天,我們將深入探討兩個(gè)流行的競(jìng)爭(zhēng)者:Next.js 和 Remix。
Next.js 是最流行的用于服務(wù)器端渲染的 React 框架之一。它已經(jīng)存在相當(dāng)長(zhǎng)的時(shí)間了,并且提供了開(kāi)發(fā)者所需的所有功能,提供了出色的開(kāi)發(fā)體驗(yàn)。
Remix 是一個(gè)較新的參與者,由 React Router 的創(chuàng)始人創(chuàng)建。它倡導(dǎo)全棧開(kāi)發(fā)方法,并引入了幾個(gè)創(chuàng)新特性。隨著 Remix 在 2022 年的開(kāi)源推出,開(kāi)發(fā)者開(kāi)始思考哪個(gè)框架更適合他們的應(yīng)用。兩者都擁有令人印象深刻的特性和充滿(mǎn)激情的社區(qū),但哪一個(gè)應(yīng)該成為我們下一個(gè)項(xiàng)目的首選呢?
讓我們分析它們的優(yōu)勢(shì)和劣勢(shì),以幫助我們選擇優(yōu)勝者。
1. 路由
Next.js
Next.js 有兩種不同的路由器:App Router 和 Pages Router。App Router 是一個(gè)較新的路由器,允許我們使用 React 的最新功能,比如 Server Components 和 Streaming。Pages Router 是原始的 Next.js 路由器,它允許我們構(gòu)建服務(wù)器端渲染的 React 應(yīng)用程序,并繼續(xù)支持用于較舊的 Next.js 應(yīng)用程序。
對(duì)于應(yīng)用程序路由,Next.js 13 使用基于目錄的路由,其中任何在 /app 下的文件稱(chēng)為 page.tsx 的文件都會(huì)被構(gòu)建為路由。應(yīng)用目錄中的文件夾可以包含用于布局的 layout.tsx,用于公開(kāi)訪(fǎng)問(wèn)該路由的 page.tsx,用于定義加載狀態(tài)的 loading.tsx,以及用于錯(cuò)誤處理的 error.tsx。要?jiǎng)?chuàng)建嵌套路由,我們可以相互嵌套文件夾。
圖片
路由
來(lái)源:Next.js 文檔
文件夾結(jié)構(gòu)
來(lái)源:Next.js 文檔
Remix
Remix v2 使用基于平面文件的路由系統(tǒng)。在我們的 /app/routes 文件夾中,我們可以通過(guò)添加新組件來(lái)創(chuàng)建新路由。使用文件名中的句點(diǎn)分隔符(.)來(lái)創(chuàng)建嵌套路由。例如,如果我們想在 Remix 應(yīng)用中創(chuàng)建一個(gè) /concerts/trending 路由,我們會(huì)添加一個(gè)名為 concerts.trending.tsx 的新文件。
來(lái)源:Next.js 文檔
視角
現(xiàn)在,如果我們比較這兩個(gè)框架的路由機(jī)制,它們都選擇了基于文件系統(tǒng)的路由幾乎相同的方向,感覺(jué)這是正確的方式前進(jìn)。
Remix 似乎更直觀,我們可以通過(guò)查看文件/布局來(lái)了解它表示的路由。但根據(jù) Next.js,將相關(guān)的路由文件放在一個(gè)文件夾中也是有道理的,這有助于為每個(gè)路由段定義我們的加載/錯(cuò)誤狀態(tài)。
2.數(shù)據(jù)獲取
Next.js
Next.js 提供了幾種數(shù)據(jù)獲取方法:
- getServerSideProps:在每個(gè)請(qǐng)求期間在服務(wù)器上獲取數(shù)據(jù)。這用于服務(wù)器端渲染(SSR),在客戶(hù)端請(qǐng)求頁(yè)面時(shí)獲取數(shù)據(jù)。
- getStaticProps:在構(gòu)建時(shí)獲取數(shù)據(jù),生成帶有預(yù)渲染內(nèi)容的靜態(tài) HTML 頁(yè)面。
- getInitialProps:在服務(wù)器和客戶(hù)端上都運(yùn)行,用于初始渲染和客戶(hù)端填充數(shù)據(jù)。這是一個(gè)遺留的 API。
- fetch:Next.js 擴(kuò)展了本地的 fetch Web API,允許我們配置每個(gè)在服務(wù)器上的 fetch 請(qǐng)求的緩存和重新驗(yàn)證行為。fetch 與 async/await 可以在 Server Component* Route Handlers 和 Server Actions 中使用。
async function getUsers() {
const res = await fetch('https://jsonplaceholder.typicode.com/users')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
export default async function Page() {
const users = await getUsers()
return (
<div>
<h1>Users</h1>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
Remix
在 Remix 中,數(shù)據(jù)是在加載器中獲取的。每個(gè)路由可以定義一個(gè)加載器函數(shù),在渲染時(shí)為路由提供相關(guān)數(shù)據(jù)。useLoaderData 將加載器的數(shù)據(jù)提供給組件。加載器僅在服務(wù)器上運(yùn)行。
import { useLoaderData } from "@remix-run/react";
export const loader = async () => {
const users = await getUsers();
return json({ users });
};
export default function Page() {
const users = useLoaderData<typeof loader>();
return (
<div>
<h1>Users</h1>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
視角
Next.js 似乎非常適合具有靜態(tài)和動(dòng)態(tài)內(nèi)容混合的應(yīng)用程序,其中靈活性和定制性被優(yōu)先考慮。Remix 的數(shù)據(jù)獲取方法允許更精細(xì)地控制數(shù)據(jù)加載和依賴(lài)關(guān)系。
3.數(shù)據(jù)變更
處理變更時(shí),我們通常通過(guò)向后端服務(wù)器發(fā)送 API 請(qǐng)求,然后更新本地狀態(tài)以反映更改。
這兩個(gè)框架的目標(biāo)是通過(guò)將變更處理直接集成到其核心功能中來(lái)徹底改變變更處理方式。
Next.js
在 Next.js 13.4 之前,創(chuàng)建并在服務(wù)器上執(zhí)行操作的唯一方法是創(chuàng)建 API 路由并更新?tīng)顟B(tài)。
Next.js 13.4 引入了服務(wù)器動(dòng)作以處理數(shù)據(jù)變更,以簡(jiǎn)化開(kāi)發(fā)者體驗(yàn)并改善用戶(hù)體驗(yàn)。
使用 API 路由
export default function Page() {
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
});
// Handle response if necessary
const data = await response.json();
// ...
}
return (
<form onSubmit={onSubmit}>
<input type="text" name="name" />
<button type="submit">Submit</button>
</form>
);
}
使用 server actions
export default function Page() {
async function create(formData: FormData) {
'use server';
const id = await createItem(formData);
}
return (
<form action={create}>
<input type="text" name="name" />
<button type="submit">Submit</button>
</form>
);
}
來(lái)自 Next.js 14 表單和變更的示例
Remix
Remix 自動(dòng)將 UI 與持久服務(wù)器狀態(tài)同步。這發(fā)生在三個(gè)步驟中:
- 路由加載器向 UI 提供數(shù)據(jù)
- 表單提交數(shù)據(jù)到路由動(dòng)作,更新持久狀態(tài)
- 頁(yè)面上的加載器數(shù)據(jù)自動(dòng)重新驗(yàn)證
圖片
Remix 鼓勵(lì)將用戶(hù)采取行動(dòng)的每個(gè)部分都保持為 HTML 表單。每當(dāng)用戶(hù)觸發(fā)表單提交時(shí),它調(diào)用動(dòng)作。一旦動(dòng)作執(zhí)行完畢,Remix 通過(guò)瀏覽器的 fetch 請(qǐng)求重新獲取該路由的所有加載器,并刷新 UI,確保 UI 始終與數(shù)據(jù)庫(kù)同步。這被稱(chēng)為 Remix 的“全棧數(shù)據(jù)流”。
export async function loader({
request,
}: LoaderFunctionArgs) {
const user = await getUser(request);
return json({
displayName: user.displayName,
email: user.email,
});
}
export default function Component() {
const user = useLoaderData<typeof loader>();
return (
<Form method="post" action="/account">
<h1>Settings for {user.displayName}</h1>
<input
name="displayName"
defaultValue={user.displayName}
/>
<input name="email" defaultValue={user.email} />
<button type="submit">Save</button>
</Form>
);
}
export async function action({
request,
}: ActionFunctionArgs) {
const formData = await request.formData();
const user = await getUser(request);
await updateUser(user.id, {
email: formData.get("email"),
displayName: formData.get("displayName"),
});
return json({ ok: true });
}
這個(gè)示例來(lái)自 Remix 路由動(dòng)作文檔。
視角
- Next.js server actions 與 React 生態(tài)系統(tǒng)和 React 的 API 相關(guān)聯(lián)。而 Remix 是基于 Web 平臺(tái)的功能實(shí)現(xiàn)的,并且與 Web 的工作方式密切相關(guān)。
- Next.js 的動(dòng)作是以組件為中心的。而 Remix 的動(dòng)作是以路由為中心的,因此不像組件那樣易于組合。
- 在 Next.js 中,我們需要手動(dòng)告訴路徑重新驗(yàn)證,而 Remix 則進(jìn)行自動(dòng)重新驗(yàn)證。這些是 Next.js 和 Remix 的權(quán)衡,我們可以決定我們可以接受哪些,我們需要哪些,并相應(yīng)地做出決定。
4.錯(cuò)誤處理
Next.js and Remix 提供了在我們的 Web 應(yīng)用程序中優(yōu)雅處理錯(cuò)誤的機(jī)制。
Next.js
每個(gè)路由段中都有一個(gè)獨(dú)立的 error.js 文件,用于渲染該路由段的錯(cuò)誤狀態(tài)。error.js 文件約定允許我們通過(guò)自動(dòng)將路由段及其嵌套子元素包裝在 React 錯(cuò)誤邊界中,優(yōu)雅地處理嵌套路由中的意外運(yùn)行時(shí)錯(cuò)誤。它處理在服務(wù)器端或?yàn)g覽器中可能發(fā)生的意外錯(cuò)誤以及如 404 等預(yù)期錯(cuò)誤。
Remix
要渲染路由段的錯(cuò)誤狀態(tài),我們可以導(dǎo)出 ErrorBoundary。它處理在服務(wù)器端或?yàn)g覽器中可能發(fā)生的意外錯(cuò)誤以及如 404 等預(yù)期錯(cuò)誤。
5.社區(qū)支持
Next.js
Next.js 是一個(gè)經(jīng)過(guò)良好建立的框架,擁有 11.8 萬(wàn)顆 GitHub stars(撰寫(xiě)時(shí))。它擁有龐大的社區(qū)和生態(tài)系統(tǒng),在尋找解決問(wèn)題、插件或集成時(shí)具有重大優(yōu)勢(shì)。
Remix
Remix 在撰寫(xiě)時(shí)擁有約 2.66 萬(wàn)顆 GitHub stars ,并且社區(qū)正在不斷壯大。
觀點(diǎn)
如果應(yīng)用程序不太復(fù)雜且不需要社區(qū)的太多幫助,則更喜歡 Remix。如果一個(gè)應(yīng)用程序需要一個(gè)擁有更廣泛功能范圍和龐大用戶(hù)社區(qū)的框架,那么 Next.js 是一個(gè)不錯(cuò)的選擇。
6.學(xué)習(xí)曲線(xiàn)
Next.js
相對(duì)較難學(xué)習(xí)。它提供了很多選擇,如果開(kāi)發(fā)者沒(méi)有正確使用,低級(jí)別控制可能會(huì)顯得過(guò)度。
Remix
相對(duì)較簡(jiǎn)單。它提供了一種做事情的方式,并將很多內(nèi)容抽象出來(lái)。
7.部署
Next.js
在 Vercel 之外部署 Next.js 可能會(huì)有挑戰(zhàn),Vercel 是一個(gè)出色的平臺(tái),但如果我們的基礎(chǔ)設(shè)施在 AWS 上,則可能并不理想。將 Next.js 托管在我們的 AWS 賬戶(hù)中可以更輕松地與我們的后端集成,并且通常比在 Vercel 上更具成本效益。雖然 Next.js 沒(méi)有原生支持使用無(wú)服務(wù)器方式自托管,但我們可以將其作為 Node 應(yīng)用程序運(yùn)行。但是,這種方法可能無(wú)法提供與使用 Vercel 相同的好處。
幸運(yùn)的是,有一個(gè)新的開(kāi)源 Next.js 無(wú)服務(wù)器適配器 - OpenNext。該適配器接收 Next.js 構(gòu)建輸出并將其轉(zhuǎn)換為可部署到任何函數(shù)即服務(wù)(FaaS)平臺(tái)的包,使部署更加靈活。
Kent Dodds 在他的博客中表達(dá)了對(duì)部署的擔(dān)憂(yōu)。
Remix
Remix 被設(shè)計(jì)用于部署在支持 JavaScript 執(zhí)行的任何平臺(tái)上。這在很大程度上是由于它專(zhuān)注于標(biāo)準(zhǔn)。
8.價(jià)格
Next.js
對(duì)許多人來(lái)說(shuō),Vercel 的定價(jià)似乎是一個(gè)大問(wèn)題。這可能是一個(gè)重要的考慮因素。
Remix
由于 Remix 可以在支持 JavaScript 執(zhí)行的任何平臺(tái)上部署,因此我們可以根據(jù)自己的選擇自由選擇平臺(tái)。
9. 與大品牌的合作
Next.js
Next.js 由 Vercel 維護(hù)。React 團(tuán)隊(duì)與 Next.js 團(tuán)隊(duì)密切合作,推出新功能,如 React Server Components。
Remix
Remix 在 2022 年與 Shopify 合作!在 Shopify 的支持下,Remix 獲得了來(lái)自一個(gè)成熟的商業(yè)領(lǐng)導(dǎo)者的長(zhǎng)期支持和支持。
10. 公司
Next.js
- Netflix Jobs
- TikTok
- Notion
- Loom
詳細(xì)列表可見(jiàn) https://nextjs.org/showcase。
Remix
- NASA
- Docker - Docker Scout 是一個(gè)統(tǒng)一的容器安全解決方案,旨在幫助開(kāi)發(fā)人員快速識(shí)別并修復(fù)所有存儲(chǔ)庫(kù)中的漏洞。
- Shopify
- react-admin - 用于提供私有 npm 注冊(cè)表和企業(yè)用戶(hù)儀表板。
詳細(xì)列表可見(jiàn) https://remix.run/showcase。
那么,誰(shuí)會(huì)獲得冠軍呢?
獲勝者是…
平局!Next.js 和 Remix 在不同領(lǐng)域都表現(xiàn)出色。
然而,“最佳”框架取決于項(xiàng)目的獨(dú)特需求:
對(duì)于:大型項(xiàng)目、功能豐富的框架、以及擁有廣泛支持的快速勝利 - Next.js 可能是冠軍。
對(duì)于:性能關(guān)鍵項(xiàng)目、流暢的用戶(hù)體驗(yàn)、解決較不復(fù)雜的問(wèn)題以及愿意探索現(xiàn)代方法 - Remix 可能是冠軍。
記?。?/p>
這兩個(gè)框架都擁有活躍的社區(qū)和不斷增長(zhǎng)的資源池。親身實(shí)驗(yàn)至關(guān)重要。使用每個(gè)框架構(gòu)建小型項(xiàng)目,以發(fā)現(xiàn)個(gè)人適合性。團(tuán)隊(duì)的技能和偏好很重要。選擇與團(tuán)隊(duì)開(kāi)發(fā)風(fēng)格相符的框架。
本文翻譯自 https://blog.saeloun.com/2024/02/21/next.js-vs-remix。