Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RSCの時代にReactとフレームワークの境界を探る

Avatar for uhyo uhyo
September 06, 2025

 RSCの時代にReactとフレームワークの境界を探る

2025-09-06 フロントエンドカンファレンス北海道2025

Avatar for uhyo

uhyo

September 06, 2025
Tweet

More Decks by uhyo

Other Decks in Technology

Transcript

  1. 我々が良く知るRSC 10 import { Client } from “./client.tsx”; export const

    SC = () => { return ( <div> <Client /> </div> ); }; server.tsx “use client”; export const Client = () => { return ( <p>client!</p> ); }; client.tsx import サーバーからクライアントをimportしてコンポーネントを使用できる。
  2. フレームワーク無しのRSC 11 const Client = { /* 省略 */ };

    export const SC = () => { return ( <div> <Client /> </div> ); }; server.tsx export const Client = () => { return ( <p>client!</p> ); }; client.tsx 独立 サーバーとクライアントは独立した、別々のプログラムになる。
  3. フレームワーク無しのRSC 12 const Client = { /* 省略 */ };

    export const SC = () => { return ( <div> <Client /> </div> ); }; server.tsx server.tsxはサーバー側用のReactランタイムで 実行。Clientはサーバー側でレンダリングしない。 Clientは「Clientというコンポーネント」の ままクライアント側に送られる。 クライアント側がClientのレンダリングを 担当。
  4. フレームワーク無しのRSC 13 export const Client = () => { return

    ( <p>client!</p> ); }; client.tsx client.tsxはクライアント用のReactランタイムで 実行。 SC部分がただのHTMLとなった状態で クライアント側のランタイムに渡される。 こちらはClientの定義を知っており、 Clientをレンダリングできる。
  5. フレームワーク無しのRSCの問題 14 const Client = { /* 省略 */ };

    export const SC = () => { return ( <div> <Client /> </div> ); }; server.tsx export const Client = () => { return ( <p>client!</p> ); }; client.tsx 別々のプログラムで2つの定義を同期させる必要がある。非常にDX悪い。
  6. 置き換えの実例 置き換え前(実際のクライアント用モジュール) 'use client'; export function Greet({ name }: {

    name: string }) { return <>Hello {name}</>; } 23 Waku v0.25.0 のソースコードから一部改変して引用(次のスライドも同じ) https://github.com/wakujs/waku/blob/473d481f0d926e05c9b0b8af5d2b777ff47eb70b/packages /waku/tests/vite-plugin-rsc-transform-internals.test.ts
  7. 置き換えの実例 置き換え後(サーバー側から見える同モジュール) import { registerClientReference } from 'react-server-dom-webpack/server.edge’; export const

    Greet = registerClientReference(()=>{ throw new Error('It is not possible to invoke a client function from the server: /src/App.tsx#Greet'); }, '/src/App.tsx', 'Greet'); 24
  8. バンドラプラグインの実装① { name: 'rsc:use-client', async transform(code, id) { /* 中略

    */ const ast = await parseAstAsync(code) if (!hasDirective(ast.body, 'use client')) { delete manager.clientReferenceMetaMap[id] return } /* 以下略 */ 28 ‘use client’ を持つ ファイルが変換対象
  9. バンドラプラグインの実装② const result = transformDirectiveProxyExport_(ast, { /* 省略 */ runtime:

    (name, meta) => { let proxyValue = /* 省略 */ return ( `$$ReactServer.registerClientReference(` + ` ${proxyValue},` + ` ${JSON.stringify(referenceKey)},` + ` ${JSON.stringify(name)})` ) }, 29 各exportを registerClientRefere nce呼び出しに変換