服务端与客户端边界指令
'use client' 划分的是哪一种边界?
- 模块边界: 文件顶部的字符串指令将该模块及其传递依赖纳入客户端模块子树,不只影响某一个 JSX 标签;
- 传播范围: 客户端模块导入的普通模块无需每个都再写指令;
- 执行含义: 客户端组件可以由服务器生成首屏 HTML,'use client' 不等于“完全跳过 SSR”;
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
'use server' 怎样标记可远程调用的函数?
- 函数或模块: 放在服务端 async 函数体开头,或文件顶部使导出的异步函数成为 Server Functions;
- 客户端导入: 需要客户端直接 import 时使用模块级声明,函数级声明适合在服务端定义后通过 props 传递引用;
- 语法位置: 指令在其他语句与 import 前,可以有注释,使用单引号或双引号字符串,不用模板字符串;
跨边界传递的值有哪些限制?
| 值类型 | Server 向 Client 传递的返回数据 | Server Function 参数 |
|---|---|---|
| 基本值、普通可序列化对象 | 支持 | 支持 |
| Array、Map、Set、Date 等受支持内建值 | 成员可序列化时支持 | 成员可序列化时支持 |
| Promise、Server Function 引用 | 支持 | 支持 |
| JSX 元素 | 可作为内容传入 | 不作为调用参数 |
| 普通函数、DOM 节点、自定义类实例 | 不支持 | 不支持 |
- 特殊类型: 还需区分 FormData、类型化数组等协议支持值;“可序列化”不等于仅能 JSON.stringify,也不等于所有对象都支持;
- Symbol: 仅支持全局注册的符号,任意本地 Symbol 不能跨边界;
如何避免把整条服务端依赖带进客户端?
- 缩小边界: 将指令放在真正需要交互的入口,避免给页面最高层无差别添加而扩大客户端 bundle;
- 内容组合: 用 children 传入服务端渲染内容,而不是在客户端模块中导入服务端专用实现;
- 安全核对: 指令划分执行边界,不自动清理敏感数据,跨边界前仍要选择可公开字段;