Replies: 5 comments 2 replies
-
@yyx990803 recently suggested a solution for this, and this is my attempt to distill it into writing: Rather than rely on // entry-client.jsx
import 'hydrogen:client-components' Which resolves to: import '/some/path/to/A.client.js';
import '/some/path/to/B.client.js';
... This can happen during the This informs Vite's module graph about all the interconnected client component dependencies, and ideally it prevents any unnecessary or duplicate chunks from being generated. A couple follow-up questions (Evan if you're listening...)
|
Beta Was this translation helpful? Give feedback.
-
Update: From the React core team, expectation-setting regarding trying to match parity with Webpack versions of RSC:
I feel OK with continuing to reach the capabilities I've outlined above with this expectation in mind. We'll do our best to be ahead of the curve and ready to implement further optimizations that the React team finds when iterating on the Webpack version. |
Beta Was this translation helpful? Give feedback.
-
Turns out that @threepointone has already put together a prototype of RSC in an esbuild environment: https://github.com/threepointone/server-components-demo/tree/esbuild-take-2 A couple great takeaways:
|
Beta Was this translation helpful? Give feedback.
-
Hey @jplhomer, curious if this ended up landing in Vite? (or rollup) Update: Ah if this is right, it looks like it's pending on facebook/react#22952 |
Beta Was this translation helpful? Give feedback.
-
For reference to others reaching this issue, @nksaraf is working on a new PR to add a vite integration for React server components: |
Beta Was this translation helpful? Give feedback.
-
UPDATE (June 2023) - please see the comment below: #4591 (comment)
This is a discussion and a placeholder of sorts to track support for React Server Components in Vite.
I'm a developer at Shopify, and we're building Hydrogen, a new React framework powered by Vite along with a set of useful commerce components to use in other frameworks.
Hydrogen is powered by a home-grown version of React Server Components, but we're working with the React core team to create an official version that supports Vite (and Rollup-style dev tools).
Developers at Shopify are putting in the work to make this a reality 🎉 but we're also happy to welcome community contributions!
Below is a list of TODOs along with some background context.
TODO
ReactFlightWebpackPlugin
for ViteReactFlightWebpackNodeLoader
for ViteReactFlightClientWebpackBundlerConfig
for ViteEach of these implementations will live upstream in facebook/react and will be contributed via PRs.
Implement
ReactFlightWebpackPlugin
for ViteWebpack implementation: https://github.com/facebook/react/blob/c6f2188ed6e8d44c73e61aa1ebfd10c9e066d5b3/packages/react-server-dom-webpack/src/ReactFlightClientWebpackPlugin.js
This plugin is responsible for finding all Client Components in the project and assembling a manifest to be used by the "flight runtime." Additionally, this plugin should instruct the bundler that these client components exist and are dependents of the client "flight runtime," as if they were each going to be dynamic
import()
(which actually happens in the flight runtime). This ensures that the same chunks are shared between multiple dependents.This behavior is critical when it comes to building client components that depend on Context. Otherwise, Context mismatches lead to bugs in the code and the inability for components to communicate with one another.
To make this work, the clearest path is to rely on Rollup's
emitFile
function. We can easily search for all components in a project matching/\.client\.(t|s)x$/
, emit a chunk for each one, and mark the flight runtime module asimplicitlyLoadedAfterOneOf
for each chunk.Progress and blockers
vite build
which has access tothis.emitFile()
vite
dev mode does NOT provide access tothis.emitFile()
, so we need to find a workaround.Our biggest blocker is with the fact that Vite tends to optimize dependencies in dev mode, which often leads to duplicated chunks/modules. We're forced to load the raw file path of the client components in the flight runtime, because we can't generate a manifest of chunks to reference.
Behind the scenes, Vite prevents this hook from being used because it straight-up doesn't use Rollup in dev mode. This makes sense!
Ideas for workarounds:
this.emitFile
in dev mode, which ends up bundling the chunk that is referenced. Leverage existingesbuild
bundle behavior used foroptimizeDeps
, create an in-memory manifest, and reference those chunks when performing dynamic imports of client components in the flight runtime.pre
hook and manually add all client components in the project to the list ofoptimizeDeps.include
, forcing Vite to bundle all of them, and then use those when dynamically importing on the client.Implement
ReactFlightWebpackNodeLoader
for ViteWebpack implementation: https://github.com/facebook/react/blob/c6f2188ed6e8d44c73e61aa1ebfd10c9e066d5b3/packages/react-server-dom-webpack/src/ReactFlightClientNodeLoader.js
While the official server components demo resolves to a separate CJS Webpack implementation, the React team has also created an ESM version of the loader (linked above).
This loader is responsible for transforming Client Components into Proxy modules.
When a server component tree is being streamed to the client, the flight runtime on the server "renders" the tree in a JSON-ish syntax consisting of nested tuples representing the application. The tuples are created based on component types:
p
anddiv
Proxy
objects thanks to this plugin, are represented by modules like@1
and@2
which are referenced in the same JSON payload. These references contain information for the client flight runtime to perform dynamic imports. This information comes from the previous plugin.Progress and blockers
Ideally, we could just re-use the existing ESM Node loader that the React team has created! However, I do not understand the API. Maybe it's geared toward being a Babel-compatible plugin?
transform
hookImplement
ReactFlightClientWebpackBundlerConfig
for ViteWebpack implementation: https://github.com/facebook/react/blob/c6f2188ed6e8d44c73e61aa1ebfd10c9e066d5b3/packages/react-server-dom-webpack/src/ReactFlightClientWebpackBundlerConfig.js
This plugin is responsible for taking the JSON-ish tuple syntax created in the previous step, and parsing it on the client. Thankfully, most of the complexity is abstracted away into the flight client runtime, so the only thing we need to customize is the bundler config.
Specifically, we need to come up with Vite-comparable methods for
__webpack_chunk_load__(chunkId)
and__webpack_require__(moduleData.id)
.Progress and blockers
import()
to load chunksrequire()
to load modules. Currently, React expects a synchronous function (akaasync/await
is not supported). We either need to find a synchronous solution in a modern/Vite world, or useimport()
and modify the function expectations upstream in React core.About React Server Components
Announced in December 2020, React Server Components are the way the React core team plans to build better support for server + client interactions in modern React applications.
I highly encourage you to watch the video and check out the server components demo.
RSC is really meant to be implemented and abstracted by meta-frameworks like Next.js and Hydrogen instead of by the end-user. By providing drop-in plugins for Vite and other rollup-compat tools, we open the door to lots more modern development workflows.
The biggest benefits the Hydrogen team sees of using React Server Components:
React Server Components is still in an experimental stage and is not yet stable, but Hydrogen is buying into this pattern and plans to help push it forward.
Beta Was this translation helpful? Give feedback.
All reactions