Next.js@14 server actions 使用体验

Next.js Server Actions

Next.js@14 中默认开启 Server Actions

Next.js 先打通了 React 在客户端与服务端“渲染”分界。Server Actions 则打通了客户端与服务端“数据”分界。

渲染分界

客户端与服务端渲染一致,就是常说的 SSR。

数据分界

客户端与服务端之间互通需要一个载体。一般为服务端暴露的一个 http 实现的接口方法,客户端通过 fetch 或 xhr 调用。

使用 server actions 则不需要再关注 http 方法地址与需要接受的数据格式。就像调用本地函数,数据则通过函数的入参传输。不用再关注 http 接口所接受的数据格式与处理返回格式。

虽然 Next.js 在幕后是使用 http 的 post 方法进行传输。就约等于 Next.js 帮我们实现了 http 接口的创建,返回数据处理。重建 Next.js 应用的心智模型。

server actions 因为服务端不再暴露 http 的方法接口地址,只有该 server actions 函数能进行数据的调用。并且返回的数据结构为 React 流(参考文档)。虽然可以通过手动添加 http request header Next-Action 属性来达到伪造请求。但一定程度上迷惑破译者。

官方解释

Behavior

  • Server actions can be invoked using the action attribute in a <form> element:
    • Server Components support progressive enhancement by default, meaning the form will be submitted even if JavaScript hasn't loaded yet or is disabled.
    • In Client Components, forms invoking Server Actions will queue submissions if JavaScript isn't loaded yet, prioritizing client hydration.
    • After hydration, the browser does not refresh on form submission.
  • Server Actions are not limited to <form> and can be invoked from event handlers, useEffect, third-party libraries, and other form elements like <button>.
  • Server Actions integrate with the Next.js caching and revalidation architecture. When an action is invoked, Next.js can return both the updated UI and new data in a single server roundtrip.
  • Behind the scenes, actions use the POST method, and only this HTTP method can invoke them.
  • The arguments and return value of Server Actions must be serializable by React. See the React docs for a list of serializable arguments and values.
  • Server Actions are functions. This means they can be reused anywhere in your application.
  • Server Actions inherit the runtime from the page or layout they are used on.

体验

在使用 Next.js 开发文件管理系统时,去掉了客户端调用 http 接口的封装过程。例如接口新增参数、减少参数,改变返回类型等对接口进行更改时,借助 TypeScript 或者 JSDoc 的类型描述则可有较好的开发提示。

可以直接通过 server actions 调用 node.js 的 fs 下的方法。也就是老被吐槽的,可能会自己干掉自己。

删除方法

'use server'
import fs from 'fs'

export const del = () => {
  fs.rmSync('/')
}

调用删除方法

'use client'
import { del } from './del'

export function DelBtn() {
    return <button onClick={() => del()}/>
}

客户端组件可以用在任何地方进行调用,例如组件初始化、销毁;各种 dom 事件。

Server Actions 调用时返回一个 Promise,所以可以结合使用 ahooksuseRequest 或者 swr 提升数据消费体验。例如常用的“缓存”、“屏幕聚焦重新请求”等功能。

并不是 Next.js 不再需要 http 的接口了。例如实现自己的流式传输数据时还是需要 http 接口的。例如图片文件的查看、下载文件内容,上传文件等操作。

默认情况下 Server Actions 的 body 大小为 1mb。可通过修改 bodysizelimit 参数增加或减少。

next.config.js

/** @type {import('next').NextConfig} */
 
module.exports = {
  experimental: {
    serverActions: {
      bodySizeLimit: '2mb',
    },
  },
}

调用截图

当鼠标 hover 到顶部面包屑导航时,出现当前选中目录下所有文件夹的下拉菜单。使用 Server Actions 获取菜单内容。

截屏2023-12-26 12.59.57.png

请求载荷

[
    {
        "path": "/Downloads/ways",
        "only_dir": "1",
        "has_file_stat": "1"
    }
]

响应

0:["$@1",["development",null]]
1:[{"name":"JPG2","is_directory":true,"stat":{"dev":16777220,"mode":16877,"nlink":3,"uid":501,"gid":20,"rdev":0,"blksize":4096,"ino":120655456,"size":96,"blocks":0,"atimeMs":1703242044321.7495,"mtimeMs":1703242019496.5264,"ctimeMs":1703242044324.9827,"birthtimeMs":1703242019496.5264,"atime":"$D2023-12-22T10:47:24.322Z","mtime":"$D2023-12-22T10:46:59.497Z","ctime":"$D2023-12-22T10:47:24.325Z","birthtime":"$D2023-12-22T10:46:59.497Z"}},]

伪造 fetch 请求,会发现返回当前页面,而不是数据。

截屏2023-12-26 13.42.31.png

request headers

POST /path/Downloads/ways/PSD HTTP/1.1
Accept: text/x-component
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6
Connection: keep-alive
Content-Length: 63
Content-Type: text/plain;charset=UTF-8
Cookie: readdir-sort=desc_date; display-type=card; card-column=3; window=%257B%2522width%2522%253A747%252C%2522height%2522%253A1122%257D; viewport-size={%22width%22:747%2C%22height%22:1122}
DNT: 1
Host: localhost:3000
Next-Action: 706aae5c02f31f5f481711066003ad67eeb6dc82
Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22path%22%2C%7B%22children%22%3A%5B%5B%22path%22%2C%22Downloads%2Fways%2FPSD%22%2C%22oc%22%5D%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%5D%7D%5D%7D%5D%7D%2Cnull%2Cnull%2Ctrue%5D
Origin: http://localhost:3000
Referer: http://localhost:3000/path/Downloads/ways/PSD
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"

通过分析 http request headers,Server Actions 在 header 中插入了下面的关属性。

...
Next-Action: 706aae5c02f31f5f481711066003ad67eeb6dc82
...

fetch 主动将该值插入 headers 内则可手动成功调用 Server Actions。

fetch('/path/Downloads/ways/PSD', {method:'POST', headers:{'Next-Action':'706aae5c02f31f5f481711066003ad67eeb6dc82'}, body: JSON.stringify([{path: "", only_dir: "1", has_file_stat: "1"}])})

使用开发工具搜索 706aae5c02f31f5f481711066003ad67eeb6dc82 关键词,发现如下结果

截屏2023-12-26 13.51.03.png

生产环境下则为被压缩混淆

截屏2023-12-26 13.53.31.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容