
Next.js 提供了多種網(wǎng)頁渲染方式,這取決于你的服務(wù)應(yīng)用的場景,包括服務(wù)端渲染、客戶端渲染、靜態(tài)生成、增量靜態(tài)生成等。
SSR (Server-side Rendering)
在 Next.js 中使用 getServerSideProps 來實現(xiàn)服務(wù)端渲染,該動作在用戶發(fā)起頁面請求時執(zhí)行,示例代碼如下:
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
兩種模式:
- 用戶直接請求:服務(wù)端請求數(shù)據(jù) -> 服務(wù)端渲染頁面 -> 用戶
- 用戶通過
next/link或next/router跳轉(zhuǎn):客戶端請求數(shù)據(jù) -> 服務(wù)端執(zhí)行getServerSideProps并返回數(shù)據(jù) -> 客戶端渲染
SSG (Static-side Generation)
在 Next.js 中使用 getStaticProps 來實現(xiàn)靜態(tài)頁面生成,該動作在 next build 時執(zhí)行,示例代碼如下:
// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getStaticProps() {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch('https://.../posts')
const posts = await res.json()
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
export default Blog
CSR (Client-side Rendering)
客戶端渲染,一般的做法是在 useEffect 中請求服務(wù)端數(shù)據(jù)再渲染組件,該動作在頁面基本靜態(tài)文件加載完畢后執(zhí)行,示例代碼如下:
function Profile() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
fetch('/api/profile-data')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])
if (isLoading) return <p>Loading...</p>
if (!data) return <p>No profile data</p>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
Next.js 背后的技術(shù)團隊開發(fā)了名為 SWR 的 React Hook 包,如果使用客戶端渲染的話,強烈推薦使用 SWR,它可以處理緩存、重新驗證、焦點跟蹤、間隔重新獲取等。示例代碼如下:
import useSWR from 'swr'
const fetcher = (...args) => fetch(...args).then((res) => res.json())
function Profile() {
const { data, error } = useSWR('/api/profile-data', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
想了解更多,可以查看 SWR 文檔 。
ISR (Incremental Static Regeneration)
在 Next.js 中使用增量靜態(tài)生成,只需在 getStaticProps 中添加屬性 revalidate,該動作在用戶發(fā)起頁面請求時執(zhí)行,示例代碼如下:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}
export default Blog
如上示例,用戶發(fā)起請求后,服務(wù)端會渲染并緩存該頁面,再將預(yù)渲染的頁面返回給用戶。在緩存期間內(nèi),用戶的請求會直接返回緩存;緩存時間結(jié)束后,用戶再次請求,會重新渲染并生成新的緩存。
使用 ISR,就可以在運行時實現(xiàn)靜態(tài)頁面生成,而無需重新構(gòu)建整個網(wǎng)站。
Dynamic Routing
在 Next.js 中配合使用 getStaticPaths + getStaticProps 即可實現(xiàn)動態(tài)路由的預(yù)渲染,該動作在 next build 時執(zhí)行,示例代碼:
/**
* CodePath: ./pages/user/[uid].tsx
*/
import { GetStaticPropsContext } from "next/types"
const Page = ({ name }) => {
return (
<div> {name} </div>
)
}
export async function getStaticPaths() {
const users = ['Anoyi', 'Jack', 'Marry']
return {
paths: users.map(user => ({ params: { user } })),
fallback: true
};
}
export async function getStaticProps(context: GetStaticPropsContext) {
const { user } = context.params
return {
props: {
user
},
}
}
export default Page
如上示例,項目構(gòu)建完畢后,會生成三個靜態(tài)文件 /user/Anoyi & /user/Jack & /user/Marry,其它路由會執(zhí)行動態(tài)路由的邏輯。