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