Nextjs 构建应用基础:数据获取一、数据获取和缓存 1.服务端 fetchAPI获取数据: 使用上面的方式获取数据数
一、数据获取和缓存
1.服务端
-
fetchAPI获取数据:
export default async function Page() { let data = await fetch('https://api.vercel.app/blog') let posts = await data.json() return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ) }
使用上面的方式获取数据数据将被缓存,关闭缓存的方式如下:
let data = await fetch('https://api.vercel.app/blog', { cache: 'no-store' })
在同一个page中多次使用
fetch
请求相同路径,其结果会被记录 -
使用ORM等方式获取:
import { db, posts } from '@/lib/db' export default async function Page() { let allPosts = await db.select().from(posts) return ( <ul> {allPosts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ) }
使用上面的方式获取数据数据将被缓存,关闭缓存的方式如下:
export const dynamic = 'force-dynamic'
当路由中不存在动态函数时,上述两种请求方式获得的数据会在项目构建时进行预渲染。然后通过增量静态生成的方式来更新数据。
2.客户端
- 类似服务端可使用fetchAPI请求数据
'use client'
import { useState, useEffect } from 'react'
export function Posts() {
const [posts, setPosts] = useState(null)
useEffect(() => {
async function fetchPosts() {
let res = await fetch('https://api.vercel.app/blog')
let data = await res.json()
setPosts(data)
}
fetchPosts()
}, [])
if (!posts) return <div>Loading...</div>
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
- 使用ORM和数据库获取:
可以通过使用
unstable_cache
API去缓存响应,允许页面在构建时进行预渲染,同时设置了缓存时间以及标签,可以通过增量静态生成使其失效。
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
const getPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
export default async function Page() {
const allPosts = await getPosts()
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
二、数据获取模式: 并行和顺序
1.顺序获取:
如下,由于Playlists
组件依赖于artistID
参数,Playlists
组件只有在Artist
组件请求结束之后再开始请求数据此时可以使用loadng.js
路由或者Suspense
组件包裹处理。
export default async function Page({
params: { username },
}: {
params: { username: string }
}) {
// Get artist information
const artist = await getArtist(username)
return (
<>
<h1>{artist.name}</h1>
{/* Show fallback UI while the Playlists component is loading */}
<Suspense fallback={<div>Loading...</div>}>
{/* Pass the artist ID to the Playlists component */}
<Playlists artistID={artist.id} />
</Suspense>
</>
)
}
async function Playlists({ artistID }: { artistID: string }) {
// Use the artist ID to fetch playlists
const playlists = await getArtistPlaylists(artistID)
return (
<ul>
{playlists.map((playlist) => (
<li key={playlist.id}>{playlist.name}</li>
))}
</ul>
)
}
2.并行获取:
通过在组件外部发起请求,避免async/await
对请求的阻塞;
import Albums from './albums'
async function getArtist(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}`)
return res.json()
}
async function getAlbums(username: string) {
const res = await fetch(`https://api.example.com/artist/${username}/albums`)
return res.json()
}
export default async function Page({
params: { username },
}: {
params: { username: string }
}) {
const artistData = getArtist(username)
const albumsData = getAlbums(username)
// Initiate both requests in parallel
const [artist, albums] = await Promise.all([artistData, albumsData])
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums} />
</>
)
}
三、数据预加载
可以通过创建一个函数,在阻塞请求之前立即调用该函数。
如下:checkIsAvailable()
会阻止 <Item/>
渲染,因此可以在它之前调用 preload()
获取<Item/>
组件依赖数据。在渲染<Item/>
时,其数据已被提取。
import { getItem } from '@/utils/get-item'
export const preload = (id: string) => {
// void evaluates the given expression and returns undefined
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
void getItem(id)
}
export default async function Item({ id }: { id: string }) {
const result = await getItem(id)
// ...
}
import Item, { preload, checkIsAvailable } from '@/components/Item'
export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
// starting loading item data
preload(id)
// perform another asynchronous task
const isAvailable = await checkIsAvailable()
return isAvailable ? <Item id={id} /> : null
}
二、 Server Actions
Server Actions
: 在服务端执行的异步函数,可以在服务端和客户端组件中使用,以处理 Next.js 应用中的表单数据提交和更改。
通过在页面中添加标记"use server"
:
- 添加在异步函数顶部:将函数作为Server Action;
- 添加在页面顶部: 将页面导出的函数都变为Server Action;
通过使用Server Action
简化了以往需要处理HTTP进行客户端和服务端的交互过程(nextjs.org/docs/app/bu…)
export default function Page() {
async function createInvoice(formData: FormData) {
'use server'
const rawFormData = {
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
}
// mutate data
// revalidate cache
}
return <form action={createInvoice}>...</form>
}
具体使用实例参照文档;
总结:
- 服务端数据获取与缓存:使用
fetch
API 或 ORM 在服务端获取数据,支持数据缓存和预渲染,可通过设置禁用缓存。 - 客户端数据获取:客户端同样可以使用
fetch
API 或unstable_cache
API 进行数据获取,并支持数据缓存。 - 数据获取模式:支持顺序获取和并行获取数据,以适应不同的组件数据依赖关系。
- 数据预加载:通过预加载函数提前获取数据,减少组件渲染时的数据获取延迟。
- Server Actions:在服务器端执行的异步函数,用于处理表单提交和数据变更,简化了客户端与服务器的交互过程。
转载自:https://juejin.cn/post/7415662205616619571