Next.js 14 初學(xué)者指南
隨著現(xiàn)代Web應(yīng)用的發(fā)展,用戶界面變得越來越復(fù)雜,同時用戶對應(yīng)用的響應(yīng)速度和互動性有著更高的期待。在這樣的背景下,Next.js 作為一個前沿的React框架,提供了一系列高級功能來滿足開發(fā)者的需求。
Metadata:在Next.js項目中優(yōu)化SEO
在當(dāng)今這個信息爆炸的時代,擁有一個高可見度的網(wǎng)站已成為許多企業(yè)和個人的追求。搜索引擎優(yōu)化(SEO)是實現(xiàn)這一目標(biāo)的重要手段。為了讓你的Next.js應(yīng)用更好地被搜索引擎發(fā)現(xiàn),Next.js引入了一個非常實用的功能——元數(shù)據(jù)API。通過這個API,你可以為每個頁面定義元數(shù)據(jù),確保當(dāng)你的頁面被分享或索引時顯示準確、相關(guān)的信息。
1. 靜態(tài)元數(shù)據(jù)的配置
靜態(tài)元數(shù)據(jù)是指在構(gòu)建時確定的有關(guān)頁面的信息,并且在運行時不會改變。這些元數(shù)據(jù)與特定頁面相關(guān)聯(lián),可以包括標(biāo)題和描述等數(shù)據(jù)。例如,如果你有一個關(guān)于頁面,你可以這樣配置它的靜態(tài)元數(shù)據(jù):
//src/app/about/page.tsx
export const metadata = {
title: "關(guān)于我",
};
export default function About() {
return <h1>關(guān)于我</h1>;
}
通過這種方式,當(dāng)你的“關(guān)于我”頁面被搜索引擎索引或被分享到社交媒體時,其標(biāo)題會正確地顯示為“關(guān)于我”。
2. 動態(tài)生成的元數(shù)據(jù)
與靜態(tài)元數(shù)據(jù)不同,動態(tài)元數(shù)據(jù)允許你根據(jù)運行時的動態(tài)數(shù)據(jù)或條件生成頁面的元數(shù)據(jù)。這對于那些內(nèi)容經(jīng)常變化或依賴于用戶輸入的頁面非常有用。比如,你有一個展示產(chǎn)品詳情的頁面,可以這樣配置其元數(shù)據(jù):
import { Metadata } from "next";
type Props = {
params: {
productId: string;
};
};
export const generateMetadata = ({ params }: Props ): Metadata => {
return {
title: `產(chǎn)品 ${params.productId} 的詳情`,
};
};
export default function ProductDetails({ params }: Props) {
return <h1>產(chǎn)品 {params.productId} 的詳情</h1>;
}
甚至,你可以使用異步函數(shù)來生成元數(shù)據(jù),這在你需要從數(shù)據(jù)庫或API獲取數(shù)據(jù)時特別有用:
export const generateMetadata = async ({ params }: Props): Promise<Metadata> => {
const title = await new Promise((resolve) => {
setTimeout(() => {
resolve(`產(chǎn)品 ${params.productId}`);
}, 100);
});
return { title: `產(chǎn)品 ${title} 的詳情`, };
};
3. 元數(shù)據(jù)規(guī)則
- layout.tsx 和 page.tsx 文件都可以導(dǎo)出元數(shù)據(jù)。如果在布局中定義,則適用于該布局中的所有頁面;如果在頁面中定義,則僅適用于該頁面。
- 元數(shù)據(jù)按順序讀取,從根級別到最終頁面級別。
- 如果同一路由的多個位置有元數(shù)據(jù),它們會被合并,但頁面元數(shù)據(jù)會替換布局元數(shù)據(jù)(如果它們有相同的屬性)。
4. title metadata
關(guān)于元數(shù)據(jù)中的title屬性,這是一個非常關(guān)鍵的部分,它直接影響到你的頁面在搜索引擎中的顯示標(biāo)題以及用戶在瀏覽器標(biāo)簽頁中看到的內(nèi)容。title可以是一個字符串或者是一個對象,這取決于你想如何控制標(biāo)題的顯示。
當(dāng)你在layout.tsx文件中定義元數(shù)據(jù)時,title字段提供了幾個有趣的選項來增加靈活性:
//layout.tsx
export const metadata: Metadata = {
title: {
absolute: "",
default: "",
template: "%s | 網(wǎng)站名稱",
},
description: "",
};
- absolute:這是一個備選項,用于設(shè)置一個絕對的標(biāo)題,通常在這里不會設(shè)置值。
- default:這個值將作為默認標(biāo)題,即如果子路由沒有指定自己的標(biāo)題,那么就會使用這個默認值。
- template:這是一個模板字符串,子路由的標(biāo)題名將替換%s。這樣,你可以很容易地為站點中的每個頁面添加一個統(tǒng)一的后綴或前綴,比如網(wǎng)站名稱或者是一個關(guān)鍵的標(biāo)識符。
這個功能特別適合那些頁面結(jié)構(gòu)復(fù)雜、需要精細控制每個頁面標(biāo)題的網(wǎng)站。通過在不同級別(全局布局、頁面布局、單獨頁面)精心設(shè)計title的設(shè)置,可以確保無論用戶進入網(wǎng)站的哪個部分,都能通過標(biāo)題快速了解內(nèi)容,并通過模板確保網(wǎng)站的整體品牌一致性得到維護。
舉個例子,如果你的一個頁面沒有指定特定的標(biāo)題,那么它就會使用default中的值。而當(dāng)頁面指定了自己的標(biāo)題時,template中定義的模式就會發(fā)揮作用,自動將頁面的標(biāo)題和網(wǎng)站名稱進行組合,形成一個既清晰又具有品牌特色的標(biāo)題展示。
這種靈活性和自動化的結(jié)合,不僅使得SEO優(yōu)化變得簡單,而且還能在提升用戶體驗的同時,加強網(wǎng)站品牌的影響力。
通過精心設(shè)計每個頁面的元數(shù)據(jù),不僅可以提高網(wǎng)站的搜索引擎排名,還能提升用戶體驗,增加點擊率。在社交媒體時代,一個吸引人的頁面標(biāo)題和描述可以大大增加內(nèi)容的分享率。而Next.js提供的元數(shù)據(jù)API,讓這一切變得簡單而直接。
Navigation:使用 Link 組件進行導(dǎo)航
在構(gòu)建一個動態(tài)且互動性強的網(wǎng)站時,頁面間的導(dǎo)航是不可或缺的一環(huán)。Next.js 為此提供了非常便利的解決方案——Link 組件和 useRouter 鉤子,讓客戶端導(dǎo)航變得既簡單又高效。
Link 組件是 Next.js 中用于實現(xiàn)路由跳轉(zhuǎn)的主要方式,它基于 HTML 的 <a> 元素進行了擴展,使得在 Next.js 應(yīng)用中的路由之間進行導(dǎo)航變得非常簡便。使用 Link 組件時,你只需要導(dǎo)入它并指定 href 屬性為目標(biāo)路徑即可:
import Link from "next/link";
<Link href="/blog">博客</Link>
Navigation:程序化導(dǎo)航
有時候,我們需要在代碼中根據(jù)某些條件或邏輯來動態(tài)導(dǎo)航到不同的頁面,這時就可以使用 Next.js 提供的 useRouter 鉤子。useRouter 允許你訪問路由對象,通過這個對象,你可以控制應(yīng)用的路由行為,例如進行頁面跳轉(zhuǎn)。
以下是一個使用 useRouter 進行程序化導(dǎo)航的示例:
import { useRouter } from "next/router";
const MyComponent = () => {
const router = useRouter();
const handleClick = () => {
console.log("下單");
router.push("/"); // 使用 router.push 方法跳轉(zhuǎn)到首頁
};
return (
<button onClick={handleClick}>下單</button>
);
}
在這個例子中,當(dāng)用戶點擊“下單”按鈕時,handleClick 函數(shù)會被觸發(fā),然后應(yīng)用會使用 router.push("/") 代碼來跳轉(zhuǎn)到首頁。這種方式非常適合在用戶完成某些操作后需要自動跳轉(zhuǎn)頁面的場景。
無論是通過 Link 組件還是 useRouter 鉤子進行導(dǎo)航,Next.js 都為開發(fā)者提供了極大的便利和靈活性。通過這些工具,你可以輕松地在你的應(yīng)用中實現(xiàn)復(fù)雜的導(dǎo)航邏輯,為用戶提供流暢且富有互動性的網(wǎng)頁體驗。
Templates(模板)
在構(gòu)建現(xiàn)代Web應(yīng)用時,開發(fā)者常常需要在多個頁面之間共享某些布局或樣式。Next.js的模板(Templates)功能就是為此而生。模板類似于布局(Layouts),它們都可以包裹每個子布局或頁面。但模板和布局在功能上有一定的差異,特別是在處理頁面導(dǎo)航時。
模板的特性
當(dāng)用戶在共享同一模板的不同路由之間導(dǎo)航時,模板會呈現(xiàn)一些獨特的行為:
- 重新掛載組件:每次導(dǎo)航到新路由時,即使新舊路由共享相同的模板,該模板的一個新實例也會被掛載。
- DOM元素重建:模板中的DOM元素會在每次導(dǎo)航時被重新創(chuàng)建,而不是復(fù)用。
- 狀態(tài)不保留:由于模板重新掛載,任何在模板中維持的狀態(tài)都將丟失,每次導(dǎo)航都是從新的狀態(tài)開始。
- 效果重新同步:React的效果(effects)會在每次導(dǎo)航時重新同步,意味著例如useEffect中的代碼會在每次模板掛載時執(zhí)行。
定義模板
定義模板非常簡單,你只需要創(chuàng)建一個默認導(dǎo)出的React組件,這個組件可以從template.js或template.tsx文件中導(dǎo)出。這個組件通常會接受children作為其屬性,并在其內(nèi)部渲染這些子元素:
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
模板使用場景
模板特別適合于那些需要在多個頁面之間共享相同布局,但又希望在每次頁面跳轉(zhuǎn)時能夠完全重置狀態(tài)和DOM的場景。這可以確保用戶在不同頁面間導(dǎo)航時,能夠獲得一致且干凈的體驗,而不必擔(dān)心前一個頁面的狀態(tài)影響到當(dāng)前頁面。
通過明智地使用模板,你可以在保持代碼組織和復(fù)用性的同時,為用戶提供流暢且一致的瀏覽體驗。
loading.tsx
loading.tsx 文件在 Next.js 應(yīng)用中扮演著特別的角色,它允許開發(fā)者為特定路由段創(chuàng)建加載狀態(tài),這些加載狀態(tài)在內(nèi)容加載時展示給用戶。使用 loading.tsx 可以有效地提升用戶體驗,特別是在網(wǎng)絡(luò)環(huán)境較差或內(nèi)容較多需要較長時間加載的場景下。
創(chuàng)建加載狀態(tài)
在 loading.tsx 文件中,你可以定義一個或多個加載狀態(tài)的 React 組件。這些組件可以是簡單的動畫,如旋轉(zhuǎn)的加載指示器,或者更復(fù)雜的占位符布局,如骨架屏。
// loading.tsx
export default function Loading() {
return (
<div className="loading-container">
<p>內(nèi)容加載中,請稍候...</p>
{/* 這里可以添加加載動畫或圖標(biāo) */}
</div>
);
}
使用加載狀態(tài)
當(dāng)用戶導(dǎo)航到一個新的路由段,而這個路由段的內(nèi)容還在加載時,你定義的加載狀態(tài)會立即顯示給用戶。這提供了一個視覺反饋,讓用戶知道應(yīng)用正在響應(yīng)其操作,并且內(nèi)容正在積極加載中。這樣可以避免用戶在看到空白頁面時感到困惑或者認為應(yīng)用出現(xiàn)了問題。
提升用戶體驗
利用 loading.tsx 實現(xiàn)的加載狀態(tài)可以大大提升應(yīng)用的用戶體驗:
- 減少等待感:通過立即提供反饋,用戶感知到的等待時間會減少,即使實際加載時間沒有變短。
- 增強應(yīng)用感知速度:快速響應(yīng)用戶操作的應(yīng)用給人的感覺更快,即使是在加載較重的內(nèi)容時也不例外。
- 保持用戶參與:加載狀態(tài)可以是創(chuàng)造性的,提供額外的視覺元素或信息,保持用戶的參與度,避免他們在加載過程中離開。
在設(shè)計加載狀態(tài)時,重要的是要保持它的簡潔和與應(yīng)用整體風(fēng)格的一致性。加載狀態(tài)不僅是一種功能性需求,也是提升品牌體驗和應(yīng)用專業(yè)度的機會。
error.tsx
在構(gòu)建現(xiàn)代web應(yīng)用時,有效地管理和響應(yīng)錯誤是至關(guān)重要的。Next.js 通過文件系統(tǒng)層次結(jié)構(gòu)中的 error.tsx 文件,為開發(fā)者提供了一種靈活而強大的方式來創(chuàng)建和管理錯誤UI,以及處理特定路由段的錯誤。
創(chuàng)建針對性的錯誤UI
通過在應(yīng)用的不同路由級別添加 error.tsx 文件,你可以為這些路由定制特定的錯誤處理UI。這種方法使得在用戶遇到錯誤時,能夠展示更具體、更友好的錯誤消息和恢復(fù)選項,而不是一個通用的錯誤頁面。
// 使用 'use client' 來指明這些錯誤組件必須是客戶端組件
'use client';
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// 將錯誤記錄到錯誤報告服務(wù)中
console.error(error);
}, [error]);
return (
<div>
<h2>出錯了!</h2>
<button onClick={() => reset()}>
重試
</button>
</div>
);
}
隔離錯誤影響
將 error.tsx 文件放置于應(yīng)用的不同級別,能夠幫助你更精確地控制錯誤的影響范圍。例如,在特定的路由段出現(xiàn)錯誤時,只有那部分內(nèi)容會展示錯誤信息,應(yīng)用的其他部分仍然可以正常工作。這樣既提高了應(yīng)用的魯棒性,也優(yōu)化了用戶體驗。
錯誤恢復(fù)功能
在 error.tsx 中,你可以提供恢復(fù)功能,如重試按鈕,允許用戶嘗試從錯誤中恢復(fù),而無需重新加載整個頁面。這種快速響應(yīng)錯誤并嘗試恢復(fù)的能力,對于保持應(yīng)用的交互性和用戶滿意度至關(guān)重要。
嵌套路由中的錯誤處理
通過在嵌套的文件夾結(jié)構(gòu)中不同級別放置 error.tsx 文件,你可以實現(xiàn)更細粒度的錯誤處理。這意味著,你可以為應(yīng)用中的不同部分定制不同的錯誤處理策略和UI,使錯誤處理更加靈活和用戶友好。
這種方法利用了Next.js的文件系統(tǒng)路由和組件模型,提供了一種既簡潔又強大的錯誤處理機制,幫助開發(fā)者構(gòu)建更加可靠和用戶友好的應(yīng)用。
Parallel Routes(插槽)
Next.js 的并行路由是一種高級路由機制,允許在同一布局中同時渲染多個頁面,極大地增強了頁面布局和內(nèi)容管理的靈活性。通過使用名為“插槽(slots)”的功能,開發(fā)者可以以模塊化的方式組織內(nèi)容。
定義插槽
要定義一個插槽,我們使用 @folder 命名約定。然后,每個插槽作為屬性傳遞給其對應(yīng)的 layout.tsx 文件。
以儀表盤為例,你可以使用并行路由同時渲染用戶、收入和通知頁面:
// dashboard/layout.tsx
export default function DashboardLayout({
children,
users,
revenue,
notifications
}: {
children: React.ReactNode;
users: React.ReactNode;
revenue: React.ReactNode;
notifications: React.ReactNode;
}) {
return (
<>
<div>{children}</div>
<div>{users}</div>
<div>{revenue}</div>
<div>{notifications}</div>
</>
);
}
并行路由的一個優(yōu)勢是它們能夠?qū)蝹€布局劃分為各種插槽,使代碼更易于管理。
獨立的路由處理
布局的每個插槽,例如用戶分析或收入指標(biāo),都可以有自己的加載和錯誤狀態(tài)。在不同頁面部分以不同速度加載或遇到獨特錯誤的場景中,這種細粒度的控制尤其有益。
路由內(nèi)的子導(dǎo)航
你的儀表盤的每個插槽都可以實質(zhì)上作為一個小應(yīng)用程序運行,完備自己的導(dǎo)航和狀態(tài)管理。這在諸如儀表盤這樣的復(fù)雜應(yīng)用中特別有用,不同部分服務(wù)于不同的目的。
//dashboard/@notifications/page.tsx
export default function Notifications() {
return (
<div>通知</div>
<Link href="/dashboard/">歸檔</Link>
);
};
//dashboard/@notifications/archieved/page.tsx
export default function ArchivedNotifications() {
return (
<div>歸檔通知</div>
<Link href="/dashboard/">默認</Link>
);
};
這種結(jié)構(gòu)不僅提升了代碼的模塊化和可讀性,而且還增強了用戶界面的交互性,使用戶能夠在儀表盤的不同部分之間流暢地導(dǎo)航,同時各部分能夠獨立地加載和處理數(shù)據(jù)。這樣的設(shè)計思想,為構(gòu)建復(fù)雜且高效的Web應(yīng)用提供了新的可能性。
結(jié)束
通過今天的分享,我們了解了Next.js并行路由的強大之處,以及它如何使我們能夠構(gòu)建更加動態(tài)和響應(yīng)式的Web應(yīng)用。這項技術(shù)不僅提高了應(yīng)用的性能和用戶體驗,還讓代碼的組織和維護變得更加高效。