Skip to content

Commit 6c1a67c

Browse files
authored
perf(react): scope React DOM prebundles by environment (#532)
React Pages currently pre-includes both React DOM entry points in browser and Worker dependency optimization. Include `react-dom/client` in the browser and `react-dom/server` in the Worker, with regression coverage for those scopes and the existing SSR includes. An installed-package comparison on a two-page React app reduced generated prebundle JavaScript by **1,580,422 bytes** (source maps and metadata excluded): | Environment | Before | After | | --- | ---: | ---: | | Client | 1,660,390 B | 992,097 B | | Worker | 1,669,081 B | 756,952 B | | SSR | 2,705,925 B | 2,705,925 B | The 80-sample benchmark used 20 randomized baseline/branch pairs for each cold/warm cache state, with fresh dev processes and browser contexts. Every sample passed hydration, interaction, and client-navigation checks. Browser transfer was effectively unchanged, and neither cold nor warm process-to-hydration time showed a statistically significant improvement (adjusted p = 0.751 and 0.800). This supports a smaller generated cache; it does not establish a startup speedup. Timings came from a small React fixture with background host activity. Validation: - Focused React plugin tests: 4 passed. - `vp run test --base origin/main`: 10,750 passed, 31 skipped. - `vp run check`: all 33 tasks passed. - Formatting and lint passed for both changed files. - `vp test run --pool=forks --maxWorkers=4`: 14,048 passed, 31 skipped.
1 parent 7967791 commit 6c1a67c

2 files changed

Lines changed: 28 additions & 11 deletions

File tree

‎packages/react/src/plugin-pages.ts‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,9 @@ const RENDERER_ID = 'virtual:void-pages-renderer';
1212
const CLIENT_ENTRY_ID = 'virtual:void-pages-client';
1313
const WORKER_ENVIRONMENT_NAME = 'void_worker';
1414
const REACT_DEDUPE = ['react', 'react-dom'];
15-
const REACT_OPTIMIZE_DEPS = [
16-
'react',
17-
'react/jsx-runtime',
18-
'react/jsx-dev-runtime',
19-
'react-dom',
20-
'react-dom/client',
21-
'react-dom/server',
22-
];
23-
const WORKER_OPTIMIZE_DEPS = [...REACT_OPTIMIZE_DEPS, 'void/live/client'];
15+
const REACT_OPTIMIZE_DEPS = ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'];
16+
const CLIENT_OPTIMIZE_DEPS = [...REACT_OPTIMIZE_DEPS, 'react-dom/client'];
17+
const WORKER_OPTIMIZE_DEPS = [...REACT_OPTIMIZE_DEPS, 'react-dom/server', 'void/live/client'];
2418

2519
// SSR HTML bypasses transformIndexHtml, so @vitejs/plugin-react cannot inject
2620
// its Fast Refresh preamble. Without it, the dev client entry fails to load.
@@ -193,11 +187,11 @@ export function pagesPlugin(pagesDir: string, options?: PagesPluginOptions): Plu
193187
// Pre-include React entries that are discovered late via dynamic
194188
// imports during SPA navigation, avoiding a full-page reload
195189
// from Vite's dep optimizer.
196-
include: REACT_OPTIMIZE_DEPS,
190+
include: CLIENT_OPTIMIZE_DEPS,
197191
},
198192
ssr: {
199193
optimizeDeps: {
200-
include: REACT_OPTIMIZE_DEPS,
194+
include: [...CLIENT_OPTIMIZE_DEPS, 'react-dom/server'],
201195
},
202196
},
203197
environments: {

‎packages/react/test/unit/plugin-pages.test.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,29 @@ describe('pagesPlugin dev config', () => {
8989
});
9090
});
9191

92+
it('includes only the React entry points needed by each environment', () => {
93+
const config = loadServeConfig() as {
94+
optimizeDeps: { include: Array<string> };
95+
ssr?: { optimizeDeps?: { include?: Array<string> } };
96+
environments: {
97+
void_worker: { optimizeDeps: { include: Array<string> } };
98+
};
99+
};
100+
101+
expect(config.optimizeDeps.include).toContain('react-dom/client');
102+
expect(config.optimizeDeps.include).not.toContain('react-dom/server');
103+
expect(config.environments.void_worker.optimizeDeps.include).toContain('react-dom/server');
104+
expect(config.environments.void_worker.optimizeDeps.include).not.toContain('react-dom/client');
105+
expect(config.ssr?.optimizeDeps?.include).toEqual([
106+
'react',
107+
'react/jsx-runtime',
108+
'react/jsx-dev-runtime',
109+
'react-dom',
110+
'react-dom/client',
111+
'react-dom/server',
112+
]);
113+
});
114+
92115
it('rescans dev renderer loads after adding a page file', async () => {
93116
writePage('index.tsx', 'export default function Index() { return <h1>Home</h1>; }\n');
94117

0 commit comments

Comments
 (0)