Next.js v15 - 服务器操作以及调用原理

约定

服务器操作是在服务器上执行的异步函数。它们可以在服务器组件和客户端组件中调用,用于处理 Next.js 应用程序中的表单提交和数据修改。

服务器操作可以通过 React 的 "use server" 指令定义。你可以将该指令放在 async 函数的顶部以将该函数标记为服务器操作,或者放在单独文件的顶部以将该文件的所有导出标记为服务器操作。

typescript 复制代码
export default function Page() {
  // 服务器操作
  async function create() {
    'use server'
    // 修改数据
  }
 
  return '...'
}
typescript 复制代码
'use server'
 
export async function create() {}

应用

React 扩展了 HTML 元素,允许通过 action 属性调用服务器操作。

typescript 复制代码
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结合使用

typescript 复制代码
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 参数,以及表单数据:

typescript 复制代码
'use server'
 
export async function updateUser(userId, formData) {}

程序化表单提交

你可以使用 requestSubmit() 方法以编程方式触发表单提交。例如,当用户使用 ⌘ + Enter 键盘快捷键提交表单时,你可以监听 onKeyDown 事件

typescript 复制代码
'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" 时,它会让该函数保留在服务器上。

相关推荐
熊猫钓鱼>_>2 小时前
动态网站发布部署核心问题详解
前端·nginx·容器化·网页开发·云服务器·静态部署
方也_arkling2 小时前
elementPlus按需导入配置
前端·javascript·vue.js
我的xiaodoujiao3 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 44--将自动化测试结果自动推送至钉钉工作群聊
前端·python·测试工具·ui·pytest
沛沛老爹3 小时前
Web开发者转型AI:多模态Agent视频分析技能开发实战
前端·人工智能·音视频
David凉宸3 小时前
vue2与vue3的差异在哪里?
前端·javascript·vue.js
笔画人生3 小时前
Cursor + 蓝耘API:用自然语言完成全栈项目开发
前端·后端
AC赳赳老秦3 小时前
外文文献精读:DeepSeek翻译并解析顶会论文核心技术要点
前端·flutter·zookeeper·自动化·rabbitmq·prometheus·deepseek
小宇的天下3 小时前
Calibre 3Dstack --每日一个命令day18【floating_trace】(3-18)
服务器·前端·数据库
毕设源码-钟学长3 小时前
【开题答辩全过程】以 基于web技术的酒店信息管理系统设计与实现-为例,包含答辩的问题和答案
前端
css趣多多3 小时前
this.$watch
前端·javascript·vue.js