伺服器動作與資料變更
伺服器動作 (Server Actions) 是在伺服器端執行的非同步函式,可用於伺服器與客戶端元件中,處理 Next.js 應用程式中的表單提交與資料變更。
🎥 觀看影片: 深入了解使用伺服器動作處理表單與資料變更 → YouTube (10 分鐘)。
慣例
伺服器動作可透過 React 的 "use server"
指令定義。您可將指令置於 async
函式頂端標記該函式為伺服器動作,或置於獨立檔案頂端標記該檔案所有匯出為伺服器動作。
伺服器元件
伺服器元件可使用行內函式層級或模組層級的 "use server"
指令。若要行內定義伺服器動作,請在函式主體頂端加入 "use server"
:
客戶端元件
客戶端元件僅能匯入使用模組層級 "use server"
指令的動作。
要在客戶端元件呼叫伺服器動作,請建立新檔案並在頂端加入 "use server"
指令。檔案內所有函式將被標記為可重複用於客戶端與伺服器元件的伺服器動作:
您也可將伺服器動作作為 prop 傳遞給客戶端元件:
行為
- 伺服器動作可透過
<form>
元素的action
屬性呼叫:- 伺服器元件預設支援漸進增強,意味即使 JavaScript 尚未載入或停用,表單仍可提交。
- 在客戶端元件中,呼叫伺服器動作的表單會在 JavaScript 未載入時排隊提交,優先處理客戶端水合。
- 水合完成後,瀏覽器不會在表單提交時重新整理。
- 伺服器動作不限於
<form>
,可從事件處理器、useEffect
、第三方函式庫及其他表單元素如<button>
呼叫。 - 伺服器動作與 Next.js 快取與重新驗證架構整合。當動作呼叫時,Next.js 可在單次伺服器往返中同時回傳更新的 UI 與新資料。
- 底層實作上,動作使用
POST
方法,且僅此 HTTP 方法可呼叫它們。 - 伺服器動作的參數與回傳值必須可被 React 序列化。請參閱 React 文件了解可序列化參數與值清單。
- 伺服器動作是函式,意味可在應用程式中任何地方重複使用。
- 伺服器動作繼承使用它們的頁面或佈局的執行環境。
- 伺服器動作繼承使用它們的頁面或佈局的路由區段設定,包含如
maxDuration
等欄位。
範例
表單
React 擴充了 HTML <form>
元素,允許透過 action
prop 呼叫伺服器動作。
在表單中呼叫時,動作會自動接收 FormData
物件。您無需使用 React useState
管理欄位,而是可使用原生 FormData
方法提取資料:
須知事項:
- 範例:含載入與錯誤狀態的表單
- 處理含多欄位的表單時,可考慮使用
entries()
方法搭配 JavaScript 的Object.fromEntries()
。例如:const rawFormData = Object.fromEntries(formData)
。需注意formData
會包含額外的$ACTION_
屬性。- 參閱 React
<form>
文件了解更多。
傳遞額外參數
您可使用 JavaScript bind
方法傳遞額外參數給伺服器動作。
伺服器動作將接收 userId
參數,以及表單資料:
須知事項:
- 替代方案是將參數作為隱藏輸入欄位傳遞(例如
<input type="hidden" name="userId" value={userId} />
)。然而,該值會成為渲染 HTML 的一部分且不會被編碼。.bind
在伺服器與客戶端元件中皆可使用,也支援漸進增強。
等待狀態
您可使用 React useFormStatus
鉤子在表單提交時顯示等待狀態。
useFormStatus
回傳特定<form>
的狀態,因此必須定義為<form>
元素的子項。useFormStatus
是 React 鉤子,因此必須在客戶端元件中使用。
<SubmitButton />
可嵌套於任何表單中:
伺服器端驗證與錯誤處理
我們建議使用如 required
和 type="email"
等 HTML 驗證進行基本客戶端表單驗證。
對於進階伺服器端驗證,您可使用如 zod 的函式庫在變更資料前驗證表單欄位:
在伺服器端驗證欄位後,您可在動作中回傳可序列化物件,並使用 React useFormState
鉤子向使用者顯示訊息。
- 透過將動作傳遞給
useFormState
,動作的函式簽名會變更為接收新的prevState
或initialState
參數作為第一個引數。 useFormState
是 React 鉤子,因此必須在客戶端元件中使用。
接著,您可將動作傳遞給 useFormState
鉤子,並使用回傳的 state
顯示錯誤訊息。
須知事項:
- 在變更資料前,您應始終確保使用者也有權限執行該動作。參閱驗證與授權。
樂觀更新
您可使用 React useOptimistic
鉤子在伺服器動作完成前樂觀更新 UI,而非等待回應:
嵌套元素
您可在 <form>
內嵌套的元素如 <button>
、<input type="submit">
和 <input type="image">
中呼叫伺服器動作。這些元素接受 formAction
prop 或事件處理器。
這在需要於單一表單中呼叫多個伺服器動作時很有用。例如,除了發布按鈕外,您可建立特定 <button>
元素儲存文章草稿。參閱 React <form>
文件了解更多。
程式化表單提交
您可以使用 requestSubmit()
方法觸發表單提交。例如,當使用者按下 ⌘
+ Enter
時,您可以監聽 onKeyDown
事件:
這將觸發最近的 <form>
祖先元素的提交,從而呼叫伺服器動作 (Server Action)。
非表單元素
雖然在 <form>
元素中使用伺服器動作很常見,但它們也可以從程式碼的其他部分呼叫,例如事件處理器和 useEffect
。
事件處理器
您可以從事件處理器(如 onClick
)呼叫伺服器動作。例如,增加按讚計數:
為了提升使用者體驗,我們建議使用其他 React API,如 useOptimistic
和 useTransition
,在伺服器動作執行完成前更新 UI,或顯示待處理狀態。
您也可以為表單元素新增事件處理器,例如在 onChange
時儲存表單欄位:
對於這種可能快速觸發多次事件的情況,我們建議使用 防抖 (debouncing) 來避免不必要的伺服器動作呼叫。
useEffect
您可以使用 React 的 useEffect
鉤子,在元件掛載或依賴項變化時呼叫伺服器動作。這對於依賴全域事件或需要自動觸發的變更非常有用。例如,onKeyDown
處理應用程式快捷鍵、無限滾動的交集觀察器鉤子,或在元件掛載時更新瀏覽計數:
請記得考慮 useEffect
的行為與注意事項。
錯誤處理
當錯誤被拋出時,它會被客戶端最近的 error.js
或 <Suspense>
邊界捕獲。我們建議使用 try/catch
來返回錯誤,以便由您的 UI 處理。
例如,您的伺服器動作可以透過返回訊息來處理建立新項目時的錯誤:
須知:
- 除了拋出錯誤外,您也可以返回一個物件供
useFormState
處理。請參閱 伺服器端驗證與錯誤處理。
重新驗證資料
您可以在伺服器動作中使用 revalidatePath
API 重新驗證 Next.js 快取:
或者使用 revalidateTag
透過快取標籤使特定資料擷取失效:
重新導向
如果您想在伺服器動作完成後將使用者重新導向到不同的路由,可以使用 redirect
API。redirect
需要在 try/catch
區塊外呼叫:
Cookies
您可以在伺服器動作中使用 cookies
API 來 get
、set
和 delete
cookies:
請參閱 其他範例 瞭解如何從伺服器動作刪除 cookies。
安全性
驗證與授權
您應該將伺服器動作視為公開的 API 端點,並確保使用者有權執行該動作。例如:
閉包與加密
在元件內定義伺服器動作會建立一個 閉包,該動作可以存取外部函式的範圍。例如,publish
動作可以存取 publishVersion
變數:
閉包在您需要擷取資料的 快照(例如 publishVersion
)以便在動作被呼叫時使用時非常有用。
然而,為了實現這一點,捕獲的變數會被發送到客戶端,並在動作被呼叫時返回伺服器。為了防止敏感資料暴露給客戶端,Next.js 會自動加密閉包變數。每次建構 Next.js 應用程式時,都會為每個動作生成一個新的私鑰。這意味著動作只能在特定建構中被呼叫。
須知: 我們不建議僅依賴加密來防止敏感值暴露給客戶端。相反,您應該使用 React taint APIs 主動防止特定資料被發送到客戶端。
覆寫加密金鑰(進階)
當您在多個伺服器上自行託管 Next.js 應用程式時,每個伺服器實例最終可能會有不同的加密金鑰,導致潛在的不一致。
為了解決這個問題,您可以使用 process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
環境變數覆寫加密金鑰。指定此變數可確保您的加密金鑰在建構之間保持一致,並且所有伺服器實例使用相同的金鑰。
這是一個進階使用情境,其中跨多個部署的一致加密行為對您的應用程式至關重要。您應該考慮標準的安全實踐,例如金鑰輪換和簽署。
須知: 部署到 Vercel 的 Next.js 應用程式會自動處理此問題。
允許的來源(進階)
由於伺服器動作可以在 <form>
元素中呼叫,這使它們容易受到 CSRF 攻擊。
在底層,伺服器動作使用 POST
方法,並且只允許此 HTTP 方法呼叫它們。這在現代瀏覽器中防止了大多數 CSRF 漏洞,特別是預設情況下使用 SameSite cookies。
作為額外的保護措施,Next.js 中的伺服器動作還會比較 Origin 標頭 與 Host 標頭(或 X-Forwarded-Host
)。如果這些不匹配,請求將被中止。換句話說,伺服器動作只能在託管它的頁面相同的主機上呼叫。
對於使用反向代理或多層後端架構(其中伺服器 API 與生產網域不同)的大型應用程式,建議使用配置選項 serverActions.allowedOrigins
來指定安全來源清單。該選項接受一個字串陣列。
瞭解更多關於 安全性與伺服器動作 的資訊。
其他資源
有關伺服器動作的更多資訊,請查閱以下 React 文件: