Upgrading Remix from 1.0 to 2.0: Solving Vite DynamicImport Preload Issues and Custom Optimizations
This article details the Ctrip Travel front‑end team's experience upgrading their framework from Remix 1.0 to Remix 2.0, focusing on Vite's DynamicImport optimization that caused 404 resource loads, and explains the mechanisms, code transformations, and custom fixes they implemented to ensure proper modulepreload and CDN host injection.
The Ctrip Travel front‑end team upgraded their project from Remix 1.0 to Remix 2.0 and encountered 404 errors for CSS and JavaScript assets caused by Vite’s optimization of dynamic imports (DynamicImport). This article explains the problem, the underlying Vite mechanisms, and the custom solutions they applied.
1. Introduction
Remix 2.0 introduces many build‑tool upgrades. After the upgrade, static resources injected with a CDN host in Remix 1.0 no longer worked, leading to missing modulepreload tags and 404 responses.
2. Lazy Loading Overview
Lazy loading defers loading of resources until they are needed. In React, React.lazy combined with dynamic import() achieves module lazy loading, while image and route lazy loading are common variants.
3. How Vite Handles Lazy‑Loaded Modules
3.1 Appearance
Vite inserts a __vitePreload wrapper around each dynamic import and adds a placeholder __VITE_PRELOAD__ . The wrapper inserts <link rel="modulepreload"> tags for the imported module and all its dependencies.
__vitePreload(async () => {
const { Contact } = await import('./Contact.tsx');
return { Contact };
}, __VITE_PRELOAD__, '');3.2 Mechanism
During the transform hook, Vite scans source files with es‑module‑lexer to find dynamic imports, then rewrites them to call __vitePreload and records dependencies via __vite__mapDeps . If the module does not already import the preload helper, Vite adds an import statement for it.
3.3 Implementation Details
The plugin’s transform , resolveId , load , renderChunk , and generateBundle hooks work together to:
Detect dynamic imports.
Wrap them with the preload helper.
Inject the helper code when missing.
Replace the __VITE_PRELOAD__ placeholder with a call to __vite__mapDeps([...]) that maps dependency indices to actual asset URLs.
Generate a __vite__mapDeps function at the top of each chunk.
3.4 Code Example
const __vite__mapDeps = (i, m = __vite__mapDeps, d = m.f || (m.f = [
'assets/Contact-BGa5hZNp.js',
'assets/Phone-CqabSd3V.js',
'assets/Name-Blg-G5Um.js'
])) => i.map(i => d[i]);
const Contact = React.lazy(() =>
__vitePreload(() => import('./Contact-BGa5hZNp.js'), __vite__mapDeps([0,1,2])));4. Internal Remix Modifications
To fix the 404s, the team added a runtime CDN host (AresHost) to every dynamically loaded asset. Because Vite’s experimental.renderBuiltUrl cannot access request‑time variables, they injected the host via a global window.__remixContext.aresHost set in the server‑rendered HTML.
The preload helper was extended to prepend this host to URLs when creating <link rel="modulepreload"> or loading CSS, ensuring correct CDN resolution both on the client and during SSR.
5. Conclusion
The article demonstrates a deep dive into Vite’s dynamic‑import preload optimization, the challenges faced when upgrading Remix, and the practical code‑level adjustments required to keep modulepreload functional and CDN‑aware. The team’s experience provides a valuable reference for front‑end engineers dealing with similar framework migrations and build‑tool customizations.
Ctrip Technology
Official Ctrip Technology account, sharing and discussing growth.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.