
Unstract 前端 Vite 迁移实战指南从 Create React App 到 Vite 7 的完整演进【免费下载链接】unstractLLM-Driven Extraction of Unstructured Data — Built for API Deployments ETL Pipeline Workflows项目地址: https://gitcode.com/GitHub_Trending/un/unstract导读本文以 Unstract 前端仓库 frontend/docs/VITE_MIGRATION.md 为骨架系统梳理其构建工具从 Create React Appreact-scripts 5.0.1迁移至 Vite 7.3.1 的完整过程涵盖环境变量体系改造REACT_APP_*→VITE_*、开发服务器代理、Docker 容器化构建与运行时配置注入、产物分包优化、测试框架切换Jest → Vitest以及 Vite 7 升级要点。读完本文你将掌握一套可复用的 CRA → Vite 迁移方法论并理解 Unstract 前端当前vite.config.js中每一项关键配置背后的设计动机。迁移概览一次从构建工具到运行体系的整体换血Unstract 前端的 Vite 迁移分两个阶段完成当前版本状态如下初始迁移日期2025-10-19从 Create React Appreact-scripts 5.0.1迁移至 Vite 6.0.5 vitejs/plugin-react 4.3.4Vite 7 升级日期2026-02-04当前版本为 Vite 7.3.1 vitejs/plugin-react 4.4.0 Vitest 3.2.4。项目以Bun作为包管理器锁文件为 frontend/bun.lock因此文档与脚本中的依赖安装、构建、测试命令均以bun开头。迁移的目的不仅是换一个打包器CRA 的 Webpack 体系在大型 React 应用中启动慢、HMR 延迟高、配置魔改困难而 Vite 基于原生 ES Modules 的开发服务器可做到近乎秒级启动与即时热更新。同时Docker 容器化部署要求文件监听、代理转发、运行时环境变量注入等能力必须可控这些正是 Vite 配置模型的强项。变更清单移除与新增移除的依赖与文件react-scripts依赖CRA 的核心src/setupProxy.jsCRA 的代理配置文件迁移后由vite.config.js的server.proxy取代package.json中 CRA 专属的eslintConfig字段Webpack 与 Babel 相关依赖Vite 内部使用 esbuild 与 Rollup无需这些打包链。新增的依赖与文件vite当前 frontend/package.json 中为^7.3.5devDependenciesvitejs/plugin-react^4.4.0vitest^3.2.6取代 Jest 作为测试运行器vite.config.js构建配置位于 frontend/vite.config.jsvitest.config.mjs测试配置位于 frontend/vitest.config.mjs。从当前 frontend/package.json 可以看出迁移后前端还陆续引入了tailwindcss/vite、vite-plugin-svgrSVG 按 React 组件导入、biomejs/biome代码检查与格式化等 Vite 生态插件这些工具与 Vite 的插件机制天然协作将在后文配置解析中展开。文件结构变化HTML 入口上移到根目录Vite 强制要求入口 HTML 位于项目根目录或通过root显式指定因此目录结构发生了如下调整Before (CRA): frontend/ ├── public/ │ └── index.html ├── src/ │ ├── index.js │ └── setupProxy.js └── package.json After (Vite): frontend/ ├── index.html ← 移动到根目录 ├── public/ ← 仅存放静态资源 ├── src/ │ └── index.js ├── vite.config.js ← 新增配置文件 └── package.json当前仓库的 frontend/index.html 正是这一结构的体现其关键改动点如下移除%PUBLIC_URL%占位符CRA 环境下%PUBLIC_URL%会在构建时替换为应用部署的公共路径Vite 中 public 目录资源直接以根路径/引用因此href%PUBLIC_URL%/manifest.json改为href/manifest.json新增模块入口脚本script typemodule src/src/index.jsx/scriptVite 以原生 ES Modules 方式加载入口当前仓库入口为/src/index.jsx保留标准结构div idroot/div、noscript提示等均沿用 CRA 惯例。环境变量迁移从REACT_APP_*到VITE_*前缀映射Vite 只向客户端代码暴露以VITE_为前缀的环境变量通过import.meta.env访问因此所有前端环境变量必须重命名REACT_APP_BACKEND_URL → VITE_BACKEND_URL REACT_APP_ENABLE_POSTHOG → VITE_ENABLE_POSTHOG REACT_APP_FAVICON_PATH → VITE_FAVICON_PATH REACT_APP_CUSTOM_LOGO_URL → VITE_CUSTOM_LOGO_URL代码访问方式的变化// Before (CRA): const backendUrl process.env.REACT_APP_BACKEND_URL; const isDev process.env.NODE_ENV development; // After (Vite): const backendUrl import.meta.env.VITE_BACKEND_URL; const isDev import.meta.env.MODE development;注意NODE_ENV的替代品Vite 使用import.meta.env.MODE表示当前模式development/production使用import.meta.env.DEV/import.meta.env.PROD的布尔值判断更为简洁。源码中的实际使用Unstract 前端将配置集中在 frontend/src/config.js 中统一管理采用三级回退优先级运行时配置window.RUNTIME_CONFIG容器化环境注入优先级最高构建期环境变量import.meta.env.VITE_*开发环境使用代码内默认值最终兜底。例如 favicon 的解析逻辑为runtimeConfig.faviconPath || import.meta.env.VITE_FAVICON_PATH || /favicon.ico。入口文件 frontend/src/index.jsx 同样以runtimeConfig.enablePosthog || import.meta.env.VITE_ENABLE_POSTHOG的方式读取 PostHog 开关且约定运行时配置中空字符串会“故意”回退到构建期环境变量。开发服务器配置层面的环境变量vite.config.js中通过loadEnv(mode, process.cwd(), )加载全部环境变量第三个参数传表示不过滤前缀因此配置文件中可以读取VITE_BACKEND_URL、PORT、WDS_SOCKET_PORT、VITE_DEV_ALLOWED_HOSTS、VITE_DEV_HMR_PROTOCOL等变量来动态组装 server 配置。Package.json 脚本变化迁移前后的脚本对照如下以文档记录为准当前 frontend/package.json 的 scripts 已在其基础上扩展{ scripts: { dev: vite, // 新增推荐的开发命令 start: vite, // 更新现在运行 Vite build: vite build, // 更新Vite 构建 preview: vite preview, // 新增预览生产构建产物 test: vitest // 更新Vitest 取代 Jest } }当前仓库在迁移后还引入了 Biome 生态脚本lint、lint:fix、format、format:fix、check等以及typechecktsc --noEmit。同时注意engines.node 20.19.0的限制——这正是 Vite 7 对 Node 版本的最低要求。开发服务器代理从 setupProxy.js 到 server.proxyCRA 时代通过src/setupProxy.js配置代理// Before (setupProxy.js) module.exports (app) { app.use(/api/v1, createProxyMiddleware({ target: process.env.REACT_APP_BACKEND_URL, changeOrigin: true, })); };Vite 迁移后在vite.config.js中声明式配置// After (vite.config.js) —— 文档示例 export default defineConfig({ server: { proxy: { /api: { target: env.VITE_BACKEND_URL, changeOrigin: true, secure: false, }, }, }, });当前仓库 frontend/vite.config.js 的代理配置更精细仅当VITE_BACKEND_URL非空时才启用代理并且增加了ws: true选项。这一细节有明确的业务动机——Unstract 的 Prompt Studio 结果流通过 Socket.IO 通道/api/v1/socket实时推送该通道只使用 WebSocket 传输若不开启ws: true开发环境下 WebSocket 升级请求无法被代理到后端导致结果无法流式渲染到 UI。生产环境不受影响由 Traefik 直接路由/api/v1/socket。源码级配置解析当前 vite.config.js 全貌文档中的配置示例是迁移时的快照而当前仓库的 frontend/vite.config.js 已演进为一份更完整的配置理解它才能真正掌握这套构建体系。以下按区块解析。插件链pluginsplugins: [ optionalPluginImports(), tailwindcss(), // Tailwind v4必须位于 optionalPluginImports() 之后 jsxInJs(), react({ include: **/*.{jsx,js,tsx,ts} }), svgr(), ],optionalPluginImports()仓库自研 Rollup 插件。Unstract 的src/plugins/目录在 OSS 检出中默认不存在由 unstract-cloud 仓库在 Docker 构建时以 overlay 方式拷贝进来而代码中存在try { await import(./plugins/...) } catch {}的容错导入模式。该插件把指向/plugins/的相对导入解析为空模块资源文件则导出空字符串默认导出使构建在缺少云插件树时依然成功运行时由 catch 分支兜底。这正是 frontend/docs/P0-FOUNDATION.md 中 P0-G1 门禁所验证的路径OSS 检出的每次构建都在走optionalPluginImports分支。tailwindcss()Tailwind v4 官方 Vite 插件必须排在optionalPluginImports()之后确保缺失的云插件导入先被解析为桩模块。jsxInJs()另一个自研插件通过transformWithEsbuild(code, filepath, { loader: jsx, jsx: automatic })处理.js文件中的 JSX。注释中详细记录了jsx: automatic是“承重墙”配置——缺失时构建依然成功但运行时因缺少 React 导入而白屏。云插件树中有 9 个.js文件包含 JSX且对 OSS 仓库不可见因此该插件是长期需求而非过渡方案。react({ include: **/*.{jsx,js,tsx,ts} })包含.js与 TypeScript 文件使它们同样获得 Fast Refresh。svgr()支持import Logo from ./logo.svg?react式导入。值得强调的是配置中刻意不设置esbuild覆盖项代码注释解释了原因include会整体替换 Vite 默认过滤规则导致.ts/.tsx完全不被转换loader只接受单一字符串把 TypeScript 交给 JSX 解析器会误读泛型T。这是排查过两次构建失败后总结的结论。开发服务器serverserver: { host: 0.0.0.0, port: Number(env.PORT) || 3000, allowedHosts: env.VITE_DEV_ALLOWED_HOSTS ? ... : [], watch: { usePolling: true, interval: 100 }, hmr: { port: Number(env.PORT) || 3000, clientPort: env.WDS_SOCKET_PORT ? Number(env.WDS_SOCKET_PORT) : Number(env.PORT) || 3000, ...(env.VITE_DEV_HMR_PROTOCOL ? { protocol: env.VITE_DEV_HMR_PROTOCOL } : {}), }, proxy: ..., }host: 0.0.0.0保证容器/集群内可访问watch.usePolling: true, interval: 100使用轮询监听文件变化这是 Docker 卷挂载场景下的关键配置后文 Docker 小节详述HMR 端口clientPort兼容旧WDS_SOCKET_PORT环境变量VITE_DEV_HMR_PROTOCOL用于 TLS 终结的 ingress 场景——页面经 https 提供时HMR socket 必须使用 wss 协议443 端口否则 socket 永远无法建立allowedHostsVite 7 默认拒绝非 localhost/IP 的 Host 头DNS rebinding 防护但集群内 dev server 位于 ingress 之后、浏览器发送真实域名时所有请求会被拦截因此支持通过VITE_DEV_ALLOWED_HOSTS逗号分隔如.example.com按环境放行空列表即 Vite 默认行为。构建配置buildbuild: { target: esnext, outDir: build, sourcemap: true, cssCodeSplit: false, chunkSizeWarningLimit: 1000, rollupOptions: { output: { manualChunks: { ... } } }, }outDir: buildVite 默认输出到dist/这里显式改为build/以维持与旧构建体系的兼容Dockerfile 中 nginx 拷贝的正是/app/buildcssCodeSplit: false合并为单一样式表。注释说明按 chunk 拆分的 CSS 会按导航顺序加载导致同优先级跨组件规则解析不可预期target: esnext输出面向现代浏览器配合 Vite 7 默认的baseline-widely-available浏览器目标。全局常量与依赖预构建define: { process.env: {}, // 兼容仍引用 process.env 的第三方库 }, optimizeDeps: { include: [react, react-dom, react-router-dom], exclude: [], esbuildOptions: { loader: { .js: jsx } }, },define将process.env整体替换为空对象满足遗留依赖optimizeDeps.include把核心依赖提前预构建pre-bundling加快冷启动esbuildOptions.loader让依赖扫描阶段也能解析.js中的 JSX。Docker 构建配置多阶段构建与运行时配置注入文档指出 docker/dockerfiles/frontend.Dockerfile 已为 Vite 改造当前仓库中的实现是典型的多阶段构建development / builder / production。环境变量前缀变更# Changed from REACT_APP_ to VITE_ prefix ENV VITE_BACKEND_URL运行时配置注入构建后注入而非构建期内联Vite 在构建期会处理 HTML 中所有script标签若源index.html中写有script src/config/runtime-config.js而该文件在构建时并不存在会导致构建失败。因此解决方案是源 index.html 不包含该标签构建完成后由 Dockerfile 用 sed 注入# Inject runtime config script into index.html after build RUN sed -i s|/head| script src/config/runtime-config.js/script\n /head| /usr/share/nginx/html/index.html运行时配置脚本的双前缀兼容frontend/generate-runtime-config.sh 在容器启动时nginx 的/docker-entrypoint.d/40-env.sh生成/usr/share/nginx/html/config/runtime-config.js内容同时支持VITE_与REACT_APP_前缀向后兼容旧部署window.RUNTIME_CONFIG { faviconPath: ${VITE_FAVICON_PATH:-${REACT_APP_FAVICON_PATH}}, logoUrl: ${VITE_CUSTOM_LOGO_URL:-${REACT_APP_CUSTOM_LOGO_URL}}, enablePosthog: ${VITE_ENABLE_POSTHOG:-${REACT_APP_ENABLE_POSTHOG}}, version: ${APP_VERSION} };脚本中的js_escape()函数会转义反斜杠与双引号保证变量值注入 JS 字符串后语法仍有效APP_VERSION取自UNSTRACT_APPS_VERSION环境变量。这与 frontend/src/config.js 的读取逻辑一一对应形成“脚本写入 → 浏览器读取 → 配置合并”的完整闭环。开发阶段的监听与 HMR 适配Docker 开发环境使用卷挂载文件系统事件在容器内不可靠因此vite.config.js开启轮询监听同时 dev 镜像将PORT80与 nginx 生产端口对齐ENV PORT80 EXPOSE 80 CMD [/bin/sh, -c, /app/generate-runtime-config.sh bun run start]HMR 相关配置clientPort兼容WDS_SOCKET_PORT、VITE_DEV_HMR_PROTOCOL也已在 server 小节说明二者配合保证容器化环境中的热更新可用。性能优化分包与预构建手动分包manualChunks将第三方依赖拆分为稳定命名的 chunk浏览器可长期缓存不变的部分build: { rollupOptions: { output: { manualChunks: { react-vendor: [react, react-dom, react-router-dom], antd-vendor: [antd, ant-design/icons], pdf-vendor: [ react-pdf-viewer/core, react-pdf-viewer/default-layout, react-pdf-viewer/highlight, react-pdf-viewer/page-navigation, pdfjs-dist, ], }, }, }, }当前 frontend/vite.config.js 保留了react-vendor与pdf-vendor两个分包PDF 查看器是 Unstract Prompt Studio 的核心依赖antd-vendor已不在其中——这与前端后续向 shadcn/ui 体系演进见 frontend/docs/P0-FOUNDATION.mdradix-ui、lucide-react、tailwind-merge已进入依赖树的现状一致。可见分包策略会随依赖结构动态调整。依赖预构建optimizeDepsoptimizeDeps: { include: [react, react-dom, react-router-dom, antd, ant-design/icons], }将高频依赖预构建为缓存过的产物显著降低冷启动时的依赖解析耗时。测试迁移从 Jest 到 Vitestpackage.json中test: vitest表明测试运行器已切换。独立的 frontend/vitest.config.mjs 是 Vitest 专用配置Vitest 不读取vite.config.js其核心要点镜像了jsxInJs()与optionalPluginImports()两个插件文件注释记录了深刻的教训——Vitest 不读vite.config.js若只在一个配置里修复会导致测试套件“静默”只加载 126/248 个测试却仍报全绿或导入组件的测试在收集阶段就失败表现为文件失败而非断言失败极易误判为基础设施噪音resolve.alias的→./src与vite.config.js保持一致否则首个导入/components/ui/*的测试就会解析失败对应 P0-14 任务test.css: falseTailwind v4 的import tailwindcss/pluginat-rule 无法被 Vitest 的 CSS 管道处理故在测试中桩掉 CSS 导入environment: happy-dom、globals: true、setupFiles: ./src/setupTests.js轻量 DOM 环境与全局 API 配置exclude排除build/生产构建产物落盘后不应被测试收集器遍历。类型定义方面frontend/vite-env.d.ts 引用了vite/client与vite-plugin-svgr/client的类型声明为import.meta.env与*.svg?react导入提供类型支持。Vite 7 升级要点2026-02-04从 Vite 6 升级到 Vite 7 时Unstract 前端完成了以下适配Node.js 最低版本提升从 18.0.0 提高到 20.19或 22.12Node 18 被彻底放弃package.json的engines字段已同步为20.19.0Vitest 同步升级从 2.x 升级到 3.2当前 3.2.4package.json 中为^3.2.6这是 Vite 7 兼容性的硬性要求浏览器目标变化默认从modules变为baseline-widely-available配置现代化__dirname替换为import.meta.dirname当前vite.config.js与vitest.config.mjs中path.resolve(import.meta.dirname, ./src)即为该写法的体现已废弃功能清理Feature状态splitVendorChunkPlugin未使用项目采用手动manualChunksSass legacy API未使用项目无 SCSS 文件版本对照表PackageBeforeAftervite^6.0.5^7.0.07.3.1vitest^2.1.8^3.2.03.2.4vitejs/plugin-react^4.3.4^4.4.0Node.jsengines18.0.020.19.0迁移检查清单供开发者自查本地.env文件全部改用VITE_前缀部署配置中的自定义环境变量同步改名所有process.env.REACT_APP_*引用更新为import.meta.env.VITE_*测试开发服务器bun start或bun run dev测试生产构建bun run build验证 Docker 环境中的 HMR 正常工作核对后端 API 调用的代理配置。常用开发命令# 安装依赖 bun install # 启动开发服务器 bun start # 或 bun run dev # 生产构建 bun run build # 预览生产构建产物 bun run preview # 运行测试 bun test # 代码检查Biome bun run lint # 代码格式化 bun run format:fix常见问题与解决方案环境变量加载不到原因变量缺少VITE_前缀或仍通过process.env访问。// ❌ 错误 console.log(process.env.REACT_APP_BACKEND_URL); // ✅ 正确 console.log(import.meta.env.VITE_BACKEND_URL);Docker 中 HMR 不工作排查路径确认vite.config.js开启了server.watch.usePolling并在.env中设置CHOKIDAR_USEPOLLINGtrueCRA 时代的遗留习惯同时核对clientPort/VITE_DEV_HMR_PROTOCOL是否适配了 ingress 的端口与协议。构建输出目录不对Vite 默认输出到dist/Unstract 显式配置为build/以维持兼容// vite.config.js build: { outDir: build, }.js文件中的 JSX 在构建时报语法错误原因Vite 默认不把.js交给 JSX 解析器。解决方案不要用esbuild.loader全局覆盖会破坏 TypeScript而是按 Unstract 的做法用transformWithEsbuild自研jsxInJs插件并指定jsx: automatic。esbuild: { loader: jsx, include: /src\/.*\.jsx?$/, } // 注意文档中的 esbuild 方案是迁移期过渡配置 // 当前仓库已改为 jsxInJs() 插件方案原因见 vite.config.js 注释运行时配置脚本导致构建失败问题Vite 构建期会尝试处理script src/config/runtime-config.js而该文件构建时不存在。解决方案源index.html中不写该标签构建完成后由 Dockerfile 的 sed 命令注入# In docker/dockerfiles/frontend.Dockerfile RUN sed -i s|/head| script src/config/runtime-config.js/script\n /head| /usr/share/nginx/html/index.html静态资源导入报错解决方案public 目录资源使用/前缀而非%PUBLIC_URL%// ❌ 错误CRA 写法 img src{${process.env.PUBLIC_URL}/logo.png} / // ✅ 正确Vite 写法 img src/logo.png /回滚说明如需要退回 CRA若需回滚到 Create React App从 git 历史恢复package.json删除vite.config.js与根目录index.html恢复public/index.html与src/setupProxy.js环境变量回退VITE_*→REACT_APP_*代码访问方式回退import.meta.env→process.env执行bun install恢复react-scripts。迁移收益小结更快的开发服务器近乎瞬时启动更快的 HMR更新即时生效无需整页刷新优化的构建产物更好的 tree-shaking 与代码分割更小的包体积精细的 chunk 策略现代化工具链基于原生 ES Modules更好的 Docker 支持轮询文件监听使容器内开发体验可靠。需要强调的是上述收益是 Unstract 前端迁移文档所记录的工程实践结论实际效果会随项目规模与依赖结构而异。对于正在规划同类迁移的团队本文的 checklist、源码级配置解析与排错清单均可直接复用建议结合 frontend/vite.config.js、frontend/vitest.config.mjs 与 docker/dockerfiles/frontend.Dockerfile 的当前实现对照阅读。【免费下载链接】unstractLLM-Driven Extraction of Unstructured Data — Built for API Deployments ETL Pipeline Workflows项目地址: https://gitcode.com/GitHub_Trending/un/unstract创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考