本文中,我們將深入研究其中一些對用戶和開發(fā)人員體驗(yàn)都產(chǎn)生影響的更新。讓我們開始吧!
就其核心而言,一致性是一組原則或編碼指南,它將一些決策責(zé)任從開發(fā)人員手中奪走。通過設(shè)置默認(rèn)值和提供指導(dǎo)方針,更容易實(shí)現(xiàn)預(yù)期結(jié)果并防止常見錯(cuò)誤。
根據(jù)Next.js 團(tuán)隊(duì)的說法,“一致性是一個(gè)系統(tǒng),它提供精心設(shè)計(jì)的解決方案和規(guī)則以支持最佳加載和 Core Web Vitals”。
Next.js 11 中的新一致性原則可以分為三個(gè)方面:
框架必須在其核心建立某些設(shè)計(jì)模式,使開發(fā)人員難以做錯(cuò)事。
盡管有很強(qiáng)的默認(rèn)設(shè)置,但有時(shí)開發(fā)人員有責(zé)任在兩條潛在路徑之間進(jìn)行選擇。一組可操作的規(guī)則將使應(yīng)用程序更容易遵守強(qiáng)大的性能標(biāo)準(zhǔn),同時(shí)仍然允許足夠的自定義級別。
創(chuàng)作時(shí)間更重視功能開發(fā)周期而非生產(chǎn)后的性能。在代碼提交之前必須考慮性能,而不是在產(chǎn)品發(fā)布后將其視為基于分析的指標(biāo)。
強(qiáng)制執(zhí)行創(chuàng)作時(shí)間原則的最簡單方法之一是通過 linting 規(guī)則。因此,Next.js 11 開箱即用的支持 ESLint。
要獲得這些功能的好處,您必須通過運(yùn)行以下代碼升級到 Next.js 的最新版本:
npm i next@latest react@latest react-dom@latest
通過運(yùn)行以下代碼啟用 linting:
npx next lint
一旦我們升級我們的版本并啟用 ESLint,我們將開始收到警告,這些警告會(huì)促使我們朝著行業(yè)最佳實(shí)踐的方向發(fā)展,幫助我們的應(yīng)用程序遵守一致性準(zhǔn)則。
許多網(wǎng)站都包含第三方腳本以啟用分析和廣告。
加載腳本的順序?qū)撁嫘阅苡芯薮笥绊懀绕涫窃谔幚砭W(wǎng)頁上的多個(gè)外部腳本時(shí)。如果管理不當(dāng),腳本會(huì)嚴(yán)重降低用戶體驗(yàn)。
Next.js 引入了一個(gè)腳本組件,它的strategy屬性可以處理很多這些問題。
讓我們來測試一下!一個(gè)明顯的變化是我們不再需要在標(biāo)簽中包裝原生 HTML 腳本next/head標(biāo)簽:
import Head from 'next/head'
function Home() {
return (
<>
<Head>
<script async src="https://polyfill.io/v3/polyfill.min.js?features=WebAnimations" />
</Head>
</>
)
}
相反,我們可以直接使用原生 HTML 腳本標(biāo)簽,如下所示:
import Script from 'next/script'
function Home() {
return (
<>
<Script src="https://polyfill.io/v3/polyfill.min.js?features=WebAnimations" />
</>
)
}
在這個(gè)例子中,我們建議使用該beforeInteractive策略加載我們的 polyfill 。讓我們改變策略,看看它如何影響加載腳本!
腳本在服務(wù)器端被注入到 HTML 中,并在捆綁的 JavaScript 運(yùn)行之前在瀏覽器上運(yùn)行。使用下面的代碼塊,我們看到獲取 polyfill 的網(wǎng)絡(luò)調(diào)用是在網(wǎng)絡(luò)選項(xiàng)卡中的其他所有內(nèi)容之前進(jìn)行的:
<Script src="https://polyfill.io/v3/polyfill.min.js?features=WebAnimations"
`strategy="beforeInteractive" />
如果我們將策略更改為afterInteractive,腳本將在頁面變?yōu)榻换ナ胶髨?zhí)行。網(wǎng)絡(luò)請求位于網(wǎng)絡(luò)選項(xiàng)卡中的頁面底部,它優(yōu)先考慮更重要的任務(wù)。
將策略更改為lazyOnload告訴 Next.js 在空閑時(shí)間加載腳本。獲取 polyfill 的網(wǎng)絡(luò)請求進(jìn)一步向下移動(dòng)到頁面底部;沒有其他延遲加載的腳本,如下面的屏幕截圖所示。
除了上面的自定義之外,如果我們想在腳本加載后執(zhí)行一些代碼,我們可以使用onLoad腳本標(biāo)簽的prop。該onLoad屬性可確保上面的腳本加載,使功能使用腳本的功能和變量沒有崩潰:
<Script
id="my-script"
src="https://polyfill.io/v3/polyfill.min.js?features=WebAnimations"
onLoad={() => {
// this executes after the script is loaded
}}
/>
Next.js 11 包括對next/image組件的增強(qiáng),例如減少布局偏移,為最終用戶提供更流暢的體驗(yàn)。
以前,要使用Image組件渲染圖像,必須使用height和width道具。這些道具允許 Next.js 識別圖像的大小并渲染占位符,防止布局偏移和混亂的用戶界面:
<Image
alt="Fixing"
src="/fixing.png"
layout="intrinsic"
width={700}
height={475}
/>
Next.js 11 提供了對srcprop 的支持,可以使用import關(guān)鍵字來應(yīng)用。如果以這種方式導(dǎo)入源,則不需要單獨(dú)指定height和width道具:
import source from '../public/fixing.png'
<Image
alt="Fixing"
src={source}
layout="intrinsic"
/>
在 Next.js 11 中,該next/image組件支持一個(gè)新的占位符屬性,該屬性設(shè)置value為blur較慢的連接。該next/image組件將在加載原始圖像時(shí)顯示模糊的低分辨率圖像:
<Image
alt="Fixing"
src={source}
layout="intrinsic"
placeholder="blur"
/>
在最終加載原始圖像之前,模糊圖像將顯示兩到三秒鐘。
此外,Next.js 11 提供了通過Image標(biāo)簽提供自定義占位符的選項(xiàng),可以使用blurDataURLprop 顯示。提供給此道具的值可以使用諸如blurha.sh 之類的應(yīng)用程序生成。
Webpack 5 支持在 10.2 版中宣布,適用于所有文件中沒有自定義 webpack 配置的 Next.js 項(xiàng)目next.config.js。自定義 webpack 配置如下所示:
module.exports = {
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
return config; // return the modified config
},
}
從版本 11 開始,無論自定義配置如何,webpack 5 都是所有 Next.js 應(yīng)用程序的默認(rèn)構(gòu)建工具。webpack 5 附帶的所有以下優(yōu)化將可用于新建項(xiàng)目。
我們知道我們可以使用命令觸發(fā)構(gòu)建next build。但是,如果我們?yōu)轫?xiàng)目觸發(fā)的兩個(gè)連續(xù)構(gòu)建之間幾乎沒有變化怎么辦?
Webpack 5 只允許有條件地重新編譯已更改的文件。性能得到了提高,因?yàn)槲覀儾粫?huì)冗余處理尚未更改的塊。
有了 webpack 5,Next.js 11 將快速刷新識別為一項(xiàng)特殊任務(wù),并以更高的優(yōu)先級執(zhí)行它,從而在每次保存任何代碼更改時(shí)都能更快地刷新。
使用 webpack 5,build命令的構(gòu)建過程是確定性的。如果代碼段沒有更改,則后續(xù)構(gòu)建時(shí)生成的哈希不會(huì)更改,這意味著在瀏覽器中散列的文件可以在更長的時(shí)間內(nèi)重復(fù)使用。
Webpack 5 提供了tree shaking commonJS模塊的功能,從包中刪除未使用的代碼。
由于社區(qū)的要求,Vercel 團(tuán)隊(duì)推出了該@next/codemod工具,該工具支持 React 和 Next.js 項(xiàng)目之間的兼容性。
我們可以運(yùn)行該工具,為它提供一個(gè)使用 Create React App 腳本生成的項(xiàng)目,并將其轉(zhuǎn)換為 Next.js 項(xiàng)目。讓我們試試吧!
首先,使用以下命令創(chuàng)建一個(gè) React 應(yīng)用程序:
npx create-react-app cra-demo
現(xiàn)在,我們已經(jīng)初始化了一個(gè)新的 React 項(xiàng)目,我們可以使用以下命令運(yùn)行我們的項(xiàng)目:
npm run start
你會(huì)看到熟悉的 React 屏幕:
接下來,運(yùn)行腳本以從 Create React App 遷移到 Next.js 項(xiàng)目:
npx @next/codemod cra-to-next cra-demo
我們可以看到腳本對存儲庫進(jìn)行了一些更改以將其移植到 Next.js。這些變化包括:
Next.js 11 為 webpack 提供了全新的Babel 加載器實(shí)現(xiàn),其中包括一個(gè)內(nèi)存配置緩存層,通過 10.1 和 10.2 版本的改進(jìn)進(jìn)一步縮短了啟動(dòng)時(shí)間。
Vercel 的團(tuán)隊(duì)使用了多種下一代前端技術(shù),如 serviceWorker、WebAssembly 和 ES 模塊,以在瀏覽器內(nèi)部提供協(xié)作環(huán)境。使用 Next.js Live 功能,開發(fā)人員和設(shè)計(jì)人員只需共享 URL 即可協(xié)作。Live 功能目前正在搶先體驗(yàn)下運(yùn)行。
很明顯,這些升級構(gòu)成了有史以來最突出的 Next.js 版本之一!
對內(nèi)核進(jìn)行了一些更改以減少啟動(dòng)時(shí)間,例如 Babel 優(yōu)化,以及促使開發(fā)人員采用經(jīng)過驗(yàn)證的解決方案的一致性建議。其他改進(jìn)是加載圖像和腳本,使用戶體驗(yàn)無縫。此外,我們現(xiàn)在可以使用 Live 功能改進(jìn)協(xié)作。
由于這些以及更多原因,Next.js 11 版本看起來很有希望并且值得升級。試一試,讓我們知道你在評論中的想法!
HTML5 History API無疑是現(xiàn)代網(wǎng)站的發(fā)展方向,因?yàn)樗瓿闪耸诸^的任務(wù),同時(shí)還提供了額外的功能。您可以根據(jù)需求使用history.pushState()或history.replaceState()在瀏覽器中修改URL:
// 當(dāng)前 URL: https://my-website.com/page_a
// 修改后的URL
const nextURL = 'https://my-website.com/page_b';
const nextTitle = 'My new page title';
const nextState = { additionalInformation: 'Updated the URL with JS' };
// 這將在瀏覽器的歷史記錄中創(chuàng)建一個(gè)新條目,而無需重新加載
window.history.pushState(nextState, nextTitle, nextURL);
// 這將替換瀏覽器歷史記錄中的當(dāng)前條目,而無需重新加載
window.history.replaceState(nextState, nextTitle, nextURL);
兩個(gè)方法的參數(shù)相同,允許您傳遞自定義的可序列化狀態(tài)對象作為第一個(gè)參數(shù)、自定義標(biāo)題(盡管大多數(shù)瀏覽器會(huì)忽略此參數(shù))以及要在瀏覽器歷史記錄中添加/替換的URL。請記住,History API只允許相同的源URL,因此您無法導(dǎo)航到完全不同的網(wǎng)站。
舊的Location API不是該作業(yè)的最佳方案,因?yàn)樗鼤?huì)重新加載頁面,但是它仍然允許您修改當(dāng)前URL,并且在使用舊瀏覽器時(shí)可能會(huì)很有用。您可以使用window.location.href、location.assign()或location.replace()修改URL:
// 當(dāng)前 URL: https://my-website.com/page_a
// 修改后的URL
const nextURL = 'https://my-website.com/page_b';
// 這將在瀏覽器的歷史記錄中創(chuàng)建一個(gè)新條目,然后重新加載
window.location.href = nextURL;
// 這將替換瀏覽器歷史記錄中的當(dāng)前條目,然后重新加載
window.location.assign(nextURL);
// 這將替換瀏覽器歷史記錄中的當(dāng)前條目,然后重新加載
window.location.replace(nextURL);
如上所述,這三個(gè)選項(xiàng)都將導(dǎo)致頁面重新加載,這可能是不希望的。此外,與使用History API不同,您只能設(shè)置URL,而不需要任何附加參數(shù)。最后,Location API不會(huì)限制您使用相同的源URL,但使用不同的源URL,這可能會(huì)導(dǎo)致安全問題。
如今,當(dāng)我們想要基于 React 創(chuàng)建一個(gè)新的 Web 項(xiàng)目時(shí),我們有許多不同的框架選擇。作為一名開發(fā)人員,您會(huì)發(fā)現(xiàn)自己很難知道應(yīng)該選擇哪一種,或者哪一種最適合您的需求。
您可能知道最常用的框架之一是Next.js,Netflix、Twitch 或 Uber 等公司通常使用它。它被認(rèn)為是增長最快的 React 框架之一。
其他人很難與 Next.js 競爭,因?yàn)樗w了三種不同的頁面渲染策略,但自 2021 年 11 月以來,我們似乎有了一個(gè)新的、新鮮的、強(qiáng)大的框架,稱為Remix。
對不起蓋茨比,我沒有忘記你,我喜歡你在生成靜態(tài)站點(diǎn)時(shí)的工作方式。
為什么需要 Next.js 或 Remix 而不是普通的 React
也許你沒有;這取決于。重點(diǎn)是了解 React 應(yīng)用程序如何工作,您可能會(huì)發(fā)現(xiàn)哪些問題以及 Next.js 或 Remix 等框架如何解決這些問題。
關(guān)于 React 的要點(diǎn)是您可以制作單頁應(yīng)用程序 (SPA),其中僅使用一個(gè) HTML 文件來呈現(xiàn)所有網(wǎng)頁,并且路由保留在客戶端。但這到底是什么意思呢?
當(dāng)發(fā)出初始請求時(shí),瀏覽器會(huì)立即收到一個(gè)包含所有應(yīng)用程序的 HTML 框頁面。
沒有預(yù)渲染的內(nèi)容。
當(dāng)用戶導(dǎo)航觸發(fā)時(shí),JavaScript 僅替換那些與請求的路由相關(guān)的組件和內(nèi)容,但 HTML 文件保持不變。
簡而言之,瀏覽器負(fù)責(zé)管理應(yīng)加載哪個(gè) JavaScript 文件以呈現(xiàn)當(dāng)前頁面。換句話說,客戶端呈現(xiàn) (CSR)。
CSR 可以非常花哨并且有助于創(chuàng)建不需要關(guān)心 SEO、緩存或緩慢的初始渲染的應(yīng)用程序。
SSlow Initial Render——當(dāng)用戶第一次登陸時(shí),可能需要很長時(shí)間才能加載第一個(gè)內(nèi)容頁面。這意味著讓他們得到一個(gè)空白頁面,直到 JavaScript 加載并呈現(xiàn)所有內(nèi)容。
SEO——因?yàn)閮?nèi)容只有一個(gè) HTML 頁面,所以很難讓搜索引擎和社交媒體機(jī)器人對內(nèi)容進(jìn)行索引。
緩存問題——HTML 結(jié)構(gòu)無法緩存,因?yàn)樗诘谝淮武秩緯r(shí)不可用。
在這一點(diǎn)上,您可能認(rèn)為 SPA 是魔鬼,但想想這么多公司使用的所有內(nèi)部應(yīng)用程序或他們銷售的應(yīng)用程序產(chǎn)品。
當(dāng)您想利用 SPA 的工作方式而又不失去我之前提到的三點(diǎn)時(shí),Next.js或Remix就會(huì)出現(xiàn)。優(yōu)點(diǎn)是網(wǎng)站可以在發(fā)送到客戶端之前在服務(wù)器端呈現(xiàn)。
SSR vs SSG vs ISR
我已經(jīng)解釋了 CSR 的含義和工作原理,現(xiàn)在輪到我來談?wù)勂渌ㄉ诘捻撁娉尸F(xiàn)策略。
Next.js是一個(gè)強(qiáng)大的選項(xiàng),因?yàn)樗峁┝巳齻€(gè)開箱即用的不同選項(xiàng),而Remix完全依賴于 SSR(目前)。但這些策略究竟是如何運(yùn)作的呢?
服務(wù)器端渲染 (SSR)
當(dāng)一個(gè)頁面被請求時(shí),它在發(fā)送到客戶端之前在服務(wù)器上完全呈現(xiàn)。對于那些擁有大量動(dòng)態(tài)數(shù)據(jù)和內(nèi)容的網(wǎng)站來說,這是一個(gè)很好的選擇。
靜態(tài)站點(diǎn)生成 (SSG)
該策略在構(gòu)建期間按路由生成所有頁面。出現(xiàn)提示時(shí),只會(huì)將請求的頁面發(fā)送給客戶端。也就是說,這構(gòu)建了一個(gè)典型的靜態(tài)網(wǎng)站。
對于那些幾乎沒有或沒有動(dòng)態(tài)數(shù)據(jù)且頁面不多的靜態(tài)網(wǎng)站來說,它可能是一個(gè)合適的選擇。數(shù)據(jù)中的每個(gè)更改都會(huì)觸發(fā)所有頁面的重新生成。
增量靜態(tài)再生 (ISR)
Next.js 提供了這個(gè)概念,是 SSR 和 SSG 的混合體。
它類似于 SSG,但您可以設(shè)置一個(gè)間隔時(shí)間,讓您知道何時(shí)應(yīng)該重新生成您的頁面。適用于那些具有動(dòng)態(tài)內(nèi)容的靜態(tài)站點(diǎn)。
Next.js 的強(qiáng)項(xiàng)在于它允許您在每個(gè)頁面上的這三個(gè)選項(xiàng)之間切換,因此您可以讓每個(gè)選項(xiàng)遵循不同的策略。
所以也許你會(huì)問自己,“如果 Next.js 只適用于 SSR,我為什么要用 Remix 替換它”。答案很簡單,如今,應(yīng)用程序和網(wǎng)站往往具有動(dòng)態(tài)數(shù)據(jù),并且很少有適合 SSG 或 ISR 場景的示例。
考慮到這一點(diǎn),Remix 提供了一個(gè)較低級別的 API。例如,它公開了 Request 對象,因此您可以在呈現(xiàn)頁面之前輕松修改標(biāo)頭,而在 Next.js 中,您需要中間件來實(shí)現(xiàn)它。
路由
這兩個(gè)框架都使用基于文件的路由系統(tǒng)來呈現(xiàn)頁面。
每個(gè)頁面都保留在不同的文件中。
每個(gè)頁面都與基于其文件名的路由相關(guān)聯(lián)。
最后,每個(gè)頁面都會(huì)呈現(xiàn)一個(gè) React 組件。
混音中的路由
您將在 中添加每個(gè)路由/app/routes,并且在其中創(chuàng)建的每個(gè)文件或文件夾都將被視為一個(gè)路由。
混音路由 1
此外,您會(huì)發(fā)現(xiàn)一個(gè)文件,/app/root.jsx,其中是“根路由”,是整個(gè)應(yīng)用程序的布局。它包含 <html>、<head> 和 <body> 標(biāo)簽,以及其他與 Remix 相關(guān)的內(nèi)容。
Next.js 中的路由
您將在 中添加每條路線/pages,它的工作方式與 Remix 完全相同。
nextjs 路由 1
在這種情況下,您可能有也可能沒有pages/_app.jsand pages/_document.js。其中App用于初始化頁面,文檔包含標(biāo)簽。
Next.js 默認(rèn)使用它們,無需額外配置。但是,如果您需要自定義它們,您可以創(chuàng)建這兩個(gè)文件,以這種方式覆蓋默認(rèn)文件。
索引路線
他們都使用相同的系統(tǒng)來呈現(xiàn)容器路線。
Next.js 示例:
pages/index.js=> /(根頁面)
pages/posts/index.js或者pages/posts.js=>/posts
混音示例:
routes/index.js=> /(根頁面)
routes/posts/index.js或者routes/posts.js=>/posts
嵌套路由
Remix 建立在 React Router 之上,由同一個(gè)團(tuán)隊(duì)開發(fā),所以你可以想象在嵌套路由方面誰是贏家。
其背后的想法是創(chuàng)建一個(gè)活動(dòng)路由來掛載包含多個(gè)嵌套路由的布局,以便每個(gè)路由管理自己的內(nèi)容。
如果我必須用 React 語言來解釋它,我會(huì)告訴你想象每個(gè)嵌套路由都是一個(gè)組件,并且根據(jù) URL 路徑,它們可以被掛載和卸載。
為實(shí)現(xiàn)這一點(diǎn),Remix 使用了<Outlet />React-router 組件。它用在父路由上,告訴它們只要被觸發(fā)就渲染子路由元素。
請記住,所有這些嵌套路由都已預(yù)加載到服務(wù)器上,因此幾乎每個(gè)加載狀態(tài)都可以刪除。聽起來不像是 SSR 和 SPA 的混合體?
另一方面,Next.js 確實(shí)支持嵌套路由,但它們作為單獨(dú)的頁面工作。如果您想實(shí)現(xiàn)類似于 Remix 所做的事情,您可能應(yīng)該自定義您的_app.js.
兩者的示例:
routes/posts/news/index.js=>/posts/news
動(dòng)態(tài)路線
它們通過基于動(dòng)態(tài)的 URL 參數(shù)呈現(xiàn)不同的內(nèi)容來工作,但它使用單個(gè)路由文件。
在這種情況下,兩個(gè)框架都支持此功能,但使用不同的命名約定。
Next.js 示例:
pages/posts/[postId].js=>/posts/some-post-slug
pages/products/[category]/[productId].js=>/products/keyboards/some-keyboard-slug或/products/headphones/some-headphone-slug
它有自己的鉤子,useRouter將為您提供那些動(dòng)態(tài)參數(shù)(postId、類別、productId)的當(dāng)前值。
混音示例:
/app/routes/posts/$postId.js=>/posts/some-post-slug
/app/routes/products/$category/$productId.js=>/products/keyboards/some-keyboard-slug或/products/headphones/some-headphone-slug
您可以使用useParamReact Router hook 來訪問這些動(dòng)態(tài)參數(shù)。
需要加載文件的路由怎么辦?
想象一下,您需要您的網(wǎng)站包含典型文件robots.txt和sitemap.xml文件。
在 Next.js 和 Remix 中,您都可以將它們添加到 /public 目錄。但是 Remix 可以讓你通過路由系統(tǒng)實(shí)現(xiàn)它,只需創(chuàng)建一個(gè)/app/routes/[sitemap.xml].js或/app/routes/[robots.txt].js
數(shù)據(jù)加載
這兩個(gè)框架都是服務(wù)器端的,因此每個(gè)框架都有不同的系統(tǒng)來獲取數(shù)據(jù)和手動(dòng)調(diào)用 API。
對于 Next.js,您可以在兩個(gè)選項(xiàng)中進(jìn)行選擇,具體取決于您要構(gòu)建的頁面類型。
getStaticProps(SSG,如果設(shè)置了重新驗(yàn)證間隔,則為 ISR)——它在構(gòu)建時(shí)獲取數(shù)據(jù)并將其數(shù)據(jù)作為頁面的組件屬性提供。
export async function getStaticProps( {params} ) {
const productList = await getProductList()
return {
props: {
productList,
slug: params.slug
}
}
}
getServerSideProps (SSR) — 它在運(yùn)行時(shí)在服務(wù)器端獲取數(shù)據(jù),并將返回的數(shù)據(jù)作為頁面的組件道具提供。
export async function getStaticProps( {params} ) {
const productList = await getProductList()
return {
props: {
productList,
slug: params.slug
}
}
}
/**
* Here you would have your own getStaticProps/getServerSideProps functions
* depending on what system you choose.
**/
const PageName = (props)=> {
const { productList } = props
// Here you could render the requested data
return (<div>
<pre> {JSON.stringify(productList, null, 4)} </pre>
</div>)
}
Remix 使用不同的方式加載數(shù)據(jù);
它為您提供了一個(gè)loader在每個(gè)路由文件上使用的函數(shù),該文件將在運(yùn)行時(shí)在服務(wù)器端運(yùn)行。
在頁面組件本身上,您將能夠使用useLoaderData掛鉤來收集這些數(shù)據(jù)。
/**
* Here you would have your own getStaticProps/getServerSideProps functions
* depending on what system you choose.
**/
export const async loader = ( {params} ) => {
const productList = await getProductList()
return {
productList,
slug: params.slug
}
};
export default function PageName() {
const { productList } = useLoaderData();
// Here you could render the requested data
return (<div>
<pre> {JSON.stringify(productList, null, 4)} </pre>
</div>)
}
數(shù)據(jù)突變
Next.js 沒有內(nèi)置的方式來執(zhí)行突變,你必須手動(dòng)進(jìn)行。
另一方面,Remix 創(chuàng)建了一個(gè)表單組件,并像使用瀏覽器的本機(jī) HTML 表單元素一樣使用它。如果您不輸入任何操作 URL,它將對表單使用相同的路由。
如果方法是GET,loader將觸發(fā)導(dǎo)出函數(shù),如果方法是POST,action將觸發(fā)定義在組件中的導(dǎo)出函數(shù)。
此外,您可以使用提供的 useTransition 掛鉤根據(jù)請求狀態(tài)管理應(yīng)用程序的行為,而無需像往常一樣手動(dòng)管理它。
export const loader = async ({ params }) => {
// Each time this page receive a GET Request, this loader will be executed
const userData = await getSomeUserData(params.slug)
return {
userData // it contains the User Name
}
}
export const action = async ({ request }) => {
// Each time this page receive a POST Request, this loader will be executed
const form = await request.formData()
const content = form.get('content')
return redirect('/')
}
export default function App() {
const { name } = useLoaderData();
return (
<div>
<div>{`Hello there ${name}`}</div>
<Form method="POST">
<label htmlFor="content">
Content: <textarea name="content" />
</label>
<input type="submit" value="Add New" />
</Form>
</div>
)
}
造型
Next.js 帶有開箱即用的內(nèi)置 CSS 支持,因此您可以直接將其導(dǎo)入到 /pages/_app.js 所需的任何 style.css 文件中。
您還可以添加其他 CSS 框架,使用一些配置或插件很容易實(shí)現(xiàn)。
linksRemix 更喜歡通過提供組件和導(dǎo)出功能來專注于對 Web 標(biāo)準(zhǔn)更加友好的解決方案。
import styles from "./styles/app.css";
export function links() {
return [
{ rel: "stylesheet", href: styles },
{ rel: "stylesheet", href: 'https://.....' },
];
}
export default function App() {
return (
<html lang="en">
<head>
<Links />
</head>
<body>
<Outlet />
</body>
</html>
);
}
您可能認(rèn)為僅僅加載一個(gè)樣式表有點(diǎn)過分了,但是如果我告訴您可以在每個(gè)頁面上單獨(dú)使用它,讓您僅在需要時(shí)加載特定樣式/字體怎么辦?
另外,在這里你可以使用其他的 CSS 框架,只需很少的配置,比如 Tailwindcss。
圖像優(yōu)化
Next.js 在這里獲勝,因?yàn)樗凶约旱膬?nèi)置組件next/image,可以實(shí)現(xiàn)圖像優(yōu)化、延遲加載、大小控制和集成加載器。不幸的是,目前 Remix 沒有任何圖像優(yōu)化支持。
搜索引擎優(yōu)化
在本文的開頭,我告訴過您這些框架背后的要點(diǎn)之一是解決 SPA 的 SEO 問題。
它們都有自己的內(nèi)置機(jī)制來動(dòng)態(tài)管理每個(gè)頁面上應(yīng)該使用哪些元數(shù)據(jù),例如關(guān)鍵字、標(biāo)題、描述等。
next/head在 Next.js 中,您可以通過將其導(dǎo)入頁面/路由來使用其內(nèi)置組件。
import Head from 'next/head'
export default function PostPage() {
return (
<div>
<Head>
<title>Post Page Title</title>
<meta name="description" content="Post Page Description" />
</Head>
<p>
All the metadata inside Head component will be injected into the
documents head
</p>
</div>
)
}
另一方面,Remix 為我們提供了一個(gè)meta導(dǎo)出函數(shù),可以接收頁面的請求數(shù)據(jù)以及 URL 參數(shù)。
export const meta = ({ data, params }) => {
charset: "utf-8",
viewport: "width=device-width,initial-scale=1",
title: `Post | ${data.title}`,
description: data.description,
};
export default function PostPage() {
return (
<div>
<p>
All the metadata returned on meta function will be injected into the
documents head
</p>
</div>
)
}
錯(cuò)誤處理
Next.js 使您可以在捕獲某些錯(cuò)誤時(shí)定義自定義頁面,例如 400 或 500 錯(cuò)誤。但是除了路由或狀態(tài)錯(cuò)誤之外的任何其他錯(cuò)誤都會(huì)導(dǎo)致頁面中斷。
Remix 讓您盡情發(fā)揮想象力,這樣您就可以覆蓋和自定義您想要的每個(gè)錯(cuò)誤。他們添加了錯(cuò)誤邊界,這是一種處理錯(cuò)誤的獨(dú)特概念——在 React v16 中引入。
您可以通過在您的甚至每個(gè)頁面上使用CatchBoundary, 和ErrorBoundary導(dǎo)出的函數(shù)輕松地覆蓋這些錯(cuò)誤。root.tsx讓我們添加一些視覺示例。
// Here you will catch any controlled error and will tell the framework what information should be shown
const CatchBoundary = () => {
let caught = useCatch();
switch (caught.status) {
case 401:
// Document is a custom React Component which contains <html>, <head>,<body>, etc
return (
<Document title={`${caught.status} ${caught.statusText}`}>
<h1>
{caught.status} {caught.statusText}
</h1>
</Document>
);
case 404:
return <PageNotFoundComponent />; // Yes, you can also use a React Component
default:
throw new Error(
`Unexpected caught response with status: ${caught.status}`
);
}
};
// Here you will have a more generic advise for when an uncontrolled error has been triggered
const ErrorBoundary = ( { error } ) => {
return (
<Document title="Uh-oh!">
<h1>App Error</h1>
<pre>{error.message}</pre>
<p>
Replace this UI with what you want users to see when your app throws
uncaught errors.
</p>
</Document>
);
};
export const loader: LoaderFunction = async ({ params }) => {
const post = await getPost(params.slug);
if (!post) {
throw new Response("Not Found", {
status: 404,
});
}
return json({ post });
};
export default function PostPage() {
const { post } = useLoaderData();
return (
<div>
<pre>
{JSON.stringify(post, null, 4)}
</pre>
</div>
)
}
部署
Next.js 很容易部署在任何支持 Node.js 的服務(wù)器上。它還集成了無服務(wù)器部署到 Vercel 的功能,Netlify 有自己的適配器,因此您可以輕松部署應(yīng)用程序。
Remix 基于 Web Fetch API 而不是 Node.js 構(gòu)建,因此它可以在 Vercel、Netlify、Architect(Node.js 服務(wù)器)以及 Cloudflare 等非 Node.js 環(huán)境中運(yùn)行。
它還會(huì)在您開始項(xiàng)目時(shí)為您提供開箱即用的適配器,因此如果您知道要將項(xiàng)目部署到哪里,就可以直奔主題。
Remix 最重要的事情之一是它不再使用 Webpack。相反,它使用 Esbuild,它在捆綁和部署方面速度極快。
其他提及
每當(dāng)您想選擇 Next.js 或 Remix 時(shí),還有其他要點(diǎn)需要考慮,但我想收集那些在我看來作為開發(fā)人員在開發(fā)項(xiàng)目時(shí)具有更大影響的因素。
但如果您想了解更多,這里有一些我在本文中沒有談到的事情的回顧。
實(shí)時(shí)重新加載。Next.js 使用熱模塊重新加載 (HMR) 并默認(rèn)啟用,而在 Remix 上,您必須在 root.tsx 上添加一個(gè)組件以強(qiáng)制您的應(yīng)用程序在代碼更改時(shí)重新加載。
Remix 支持 Cookie、會(huì)話和身份驗(yàn)證,而 Next.js 不支持——您必須使用外部庫。
Next.js 內(nèi)置了對字體和腳本優(yōu)化的支持,而 Remix 則沒有。
兩者都讓您開箱即用地使用 Typescript。
Remix 無需執(zhí)行 JavaScript 即可完成其大部分功能,而 Next.js 則不會(huì)。
Remix 可以在嵌套路由中包含獨(dú)立路由
兩者都可以使用 Tailwindcss 快速工作,并有自己的指南來實(shí)現(xiàn)它。
Next.js 內(nèi)置了對國際化路由的支持。
Next.js 對 AMP 具有開箱即用的支持,而 Remix 則沒有。
*請認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。