430 - 《RSC》
发布于 2024年4月14日
近期要做 RSC 的支持,再看了一遍相关文档,整理如下。
1、生成 RSC 协议字符串。
比如我们有个组件树,App -> Foo。调 RSDWS(react-server-dom-webpack/server) 渲染 App 即可得到 RSC 协议的字符串。
import rsdws from "react-server-dom-webpack/server";
const { renderToPipeableStream } = rsdws;
import { App } from "./app/App.js";
renderToPipeableStream(<App />).pipe(process.stdout);
// 输出
// J0:["$","div",null,{"children":[["$","h1",null,{"children":"App"}],["$","p",null,{"children":"Foo"}]]}]
现在来加一个 Client 组件 Bar,注意 Client 组件在 RSC render 时不会引真实文件,而是被替换成 { $$typeof, filepath, name }
的格式。这一步通常由框架或构建工具来做。然后 renderToPipeableStream 时加上 bundle 相关配置,webpack 的场景下需要 id、name 和 chunks 字段。
const Bar = {
$$typeof: Symbol.for("react.module.reference"),
filepath: "Bar.tsx",
name: "Bar",
};
renderToPipeableStream(<App />, { 'Bar.tsx': { Bar: { id: 'Bar.tsx', name: 'xxx', chunks: [] } } }).pipe(process.stdout);
// 输出
// M1:{"id":"Bar.tsx","name":"xxx", "chunks":[]}
// J0:["$","div",null,{"children":[["$","h1",null,{"children":"App"}],["$","p",null,{"children":"Foo"}],["$","@1",null,{}]]}]
其中 Mx
表示模块,而 ["$","@1",null,{}]
表示使用 M1 模块。
2、react-server condition。
基于 0227-server-module-conventions,前面的代码要跑起来,是需要加 --conditions react-server
的,比如 node --conditions react-server /path/to/file.js
。原因是 react-server-dom-webpack 的 package.json 中 server 有 react-server 的 condition。
一个好处是,如果错误地在 RSC 中使用 useState、useEffect 等不支持的 hooks,由于根本没这个 export,会直接报错。
file:///private/tmp//Foo.js:2
import { useEffect, useState } from "react";
^^^^^^^^^
SyntaxError: Named export 'useEffect' not found. The requested module 'react' is a CommonJS module, which may not support all module.exports as na