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

相关推荐
热爱编程的小曾23 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin34 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox