約定
服務器操作是在服務器上執行的異步函數。它們可以在服務器組件和客戶端組件中調用,用于處理 Next.js 應用程序中的表單提交和數據修改。
服務器操作可以通過 React 的 “use server” 指令定義。你可以將該指令放在 async 函數的頂部以將該函數標記為服務器操作,或者放在單獨文件的頂部以將該文件的所有導出標記為服務器操作。
export default function Page() {// 服務器操作async function create() {'use server'// 修改數據}return '...'
}
'use server'export async function create() {}
應用
React 擴展了 HTML 元素,允許通過 action 屬性調用服務器操作。
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'),}// 修改數據// 重新驗證緩存}return <form action={createInvoice}>...</form>
}
通常,Next.js TypeScript 插件會標記updateItemAction,因為函數通常不能在客戶端-服務器邊界之間序列化,序列化是指將對象轉換為可以存儲或傳輸的格式。函數包含上下文和狀態信息,無法簡單地轉化為可以在不同環境中執行的格式。
然而,名為 action 或以 Action 結尾的 props 被假定為接收服務器操作。 這只是一個啟發式方法,因為 TypeScript 插件實際上并不知道它接收的是服務器操作還是普通函數。 運行時類型檢查仍然會確保你不會意外地將函數傳遞給客戶端組件。
處理表單時你可以將服務器操作與useActionState結合使用
export default function Page() {
const initialState: State = { message: null, errors: {} };const [state, formAction] = useActionState(createInvoice, initialState);async function createInvoice(prevState: State, formData: FormData) {'use server'const rawFormData = {customerId: formData.get('customerId'),amount: formData.get('amount'),status: formData.get('status'),}// 修改數據// 重新驗證緩存}return <form action={formAction}>...</form>
}
傳遞另外參數
服務器操作將接收 userId 參數,以及表單數據:
'use server'export async function updateUser(userId, formData) {}
程序化表單提交
你可以使用 requestSubmit() 方法以編程方式觸發表單提交。例如,當用戶使用 ? + Enter 鍵盤快捷鍵提交表單時,你可以監聽 onKeyDown 事件
'use client'export function Entry() {const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {if ((e.ctrlKey || e.metaKey) &&(e.key === 'Enter' || e.key === 'NumpadEnter')) {e.preventDefault()e.currentTarget.form?.requestSubmit()}}return (<div><textarea name="entry" rows={20} required onKeyDown={handleKeyDown} /></div>)
}
原理
每當你將 ‘use server’ 添加到服務器端函數并將其導入到客戶端組件時,它都會將其標記為對客戶端可用(服務器的入口點)。這并不意味著函數會被序列化并通過網絡發送,相反,客戶端將獲得該函數的 URL 字符串,客戶端可以使用它通過 RPC 向服務器發送請求。這是一個 POST 請求。這是自動為你處理的,你所要做的就是包含 ‘use server’,導入你的 server action 或將其作為 prop 傳遞,然后就使用它。您永遠不會看到此 URL 字符串,但這就是它在后臺的工作方式。
即使您在 server 組件中使用 server 操作,添加 “use server” 也很重要。假設您在服務器組件中有一個按鈕,并且您希望在有人單擊該按鈕時使用服務器操作。您仍然需要一個 URL 字符串,因為該按鈕最終會出現在客戶端上,并點擊該按鈕。因此,只有當你在 server 操作中包含 “use server” 指令時,它才會起作用。
此外,如果你將服務器操作導入到客戶端組件中,但忘記添加 “use server”,它會將該函數作為代碼導入到客戶端中。它將不再是服務器端功能。當你添加 “use server” 時,它會讓該函數保留在服務器上。