
Unstract 前端工程指南Vite 7 Bun Biome 构建体系、VITE_ 环境变量与 Docker 热更新实践【免费下载链接】unstractLLM-Driven Extraction of Unstructured Data — Built for API Deployments ETL Pipeline Workflows项目地址: https://gitcode.com/GitHub_Trending/un/unstract本文以 Unstract 前端工程的官方文档 frontend/README.md 为主体结合仓库中真实的 vite.config.js、package.json、VITE_MIGRATION.md 与 Docker 构建文件系统讲解该 React 前端如何完成从 Create React AppCRA到 Vite 7 的迁移以及如何用 Bun 作为包管理器完成本地开发、环境变量配置、生产构建与容器化部署。读完后你可以独立拉起前端开发服务器、配置代理与热更新HMR并理解其 Docker 镜像中运行时配置注入的完整机制。一、技术栈与迁移背景从 CRA 到 Vite 7Unstract 前端是 Unstract一个面向非结构化数据的 LLM 抽取平台的用户界面核心功能覆盖 Prompt Studio、工作流编排、ETL 管道与 API 部署等模块。官方文档明确说明构建工具Vite当前^7.3.5配合vitejs/plugin-react ^4.4.0包管理器Bun要求 1.0.0依赖锁文件为bun.lock代码检查与格式化Biome^2.3.13统一替代 ESLint Prettier测试Vitest^3.2.6 happy-dom 环境Node.js 要求20.19.0见 package.json 的engines字段。需要注意一处文档与代码的差异README 开头写“built with React 18”但从 package.json 当前依赖表看react与react-dom均为^19可以推断 React 大版本已在迁移之后跟进升级以依赖声明为准。迁移有两个关键时间节点来自 VITE_MIGRATION.md事件时间说明初次迁移 CRA → Vite 62025-10-19从 react-scripts 5.0.1 迁移到 Vite 6.0.5升级至 Vite 72026-02-04Vite 7.3.1、Vitest 3.2.4、vitejs/plugin-react 4.4.0Vite 7 带来的硬性变化包括Node.js 最低要求提升到 20.19/22.12Node 18 被移除Vitest 必须升到 3.2浏览器目标默认从modules改为baseline-widely-available配置文件中__dirname被import.meta.dirname取代在 vite.config.js 中可以看到path.resolve(import.meta.dirname, ./src)的实际用法。目录结构上CRA 与 Vite 的差异是index.html从public/移到仓库根目录即 frontend/index.htmlpublic/只放纯静态资源代理配置从src/setupProxy.js移入server.proxy。当前的 index.html 使用script typemodule src/src/index.jsx/script作为应用入口并移除了 CRA 的%PUBLIC_URL%占位符。二、环境准备与快速启动1. 前置条件Bun1.0.0 或更高版本推荐Node.js20.19.0 或更高版本兼容性要求。2. 安装依赖并配置环境变量git clone https://github.com/Zipstack/unstract.git cd unstract/frontend # 安装依赖 bun install # 复制示例环境变量文件 cp sample.env .env仓库自带的 sample.env 是真实可用的起点其当前内容为VITE_BACKEND_URLhttp://localhost:8000 # Analytics - PostHog (set to false to disable tracking) # Default: false for local development VITE_ENABLE_POSTHOGfalse # For development NODE_ENVdevelopment # Enable file watching via polling instead of filesystem events # 这些对 Docker 容器内的热更新至关重要 # 普通文件系统事件通知在挂载卷上不可靠 CHOKIDAR_USEPOLLINGtrue # HMR WebSocket 端口 - 必须匹配外部可访问端口Traefik 80 # 不是 Vite 内部端口3000因为浏览器是通过代理连接的 WDS_SOCKET_PORT80README 示例中给出的另一组取值用于本地直连开发VITE_BACKEND_URLhttp://frontend.unstract.localhost:8081 VITE_ENABLE_POSTHOGfalse VITE_FAVICON_PATH/path/to/custom/favicon.ico VITE_CUSTOM_LOGO_URL/path/to/custom/logo.svg3. 启动开发服务器bun start # 等价于 bun run dev应用运行在http://localhost:3000端口由env.PORT或默认 3000 决定见 vite.config.js。HMR 默认开启修改组件后页面自动更新且不丢失组件状态Vite 提供近乎即时的服务器启动、按需编译和构建错误浮层error overlay。生产构建预览bun run preview # http://localhost:4173三、环境变量体系VITE_ 前缀与内置变量这是 CRA 迁移中最容易踩坑的部分。Vite 只暴露以VITE_开头的自定义变量访问方式从process.env改为import.meta.env// ✅ 正确 console.log(import.meta.env.VITE_BACKEND_URL); // ❌ 错误CRA 风格不再可用 console.log(process.env.REACT_APP_BACKEND_URL);官方给出的迁移映射VITE_MIGRATION.mdREACT_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;Vite 提供四个内置变量import.meta.env.MODEdevelopment/production、import.meta.env.DEV、import.meta.env.PROD、import.meta.env.BASE_URL。环境文件加载规则.env所有场景、.env.local所有场景git 忽略、.env.[mode]指定模式、.env.[mode].local指定模式且 git 忽略。修改.env后必须重启开发服务器才能生效。对 TypeScript 使用者vite-env.d.ts 与 tsconfig.json 提供了类型支撑可按 VITE_MIGRATION.md 中的示例为ImportMetaEnv声明VITE_BACKEND_URL、VITE_ENABLE_POSTHOG等字段以获得补全。四、vite.config.js 深度解析vite.config.js 是整个前端工程的核心配置文件以下按配置块逐一拆解。4.1 代理Proxy配置/api转发到后端proxy: env.VITE_BACKEND_URL env.VITE_BACKEND_URL.trim() ! ? { /api: { target: env.VITE_BACKEND_URL, changeOrigin: true, secure: false, // 同时转发 WebSocket 升级 —— Socket.IO 日志/结果通道 // 使用纯 websocket 传输连接 /api/v1/socket ws: true, }, } : undefined,见 vite.config.js开发时业务代码直接调用相对路径即可被透明代理axios.get(/api/v1/users); fetch(/api/v1/workflows);从源码注释可以看出一个关键细节ws: true是必要的——Prompt Studio 的结果流式推送走 Socket.IO 的/api/v1/socket纯 WebSocket 升级请求若不代理到后端开发环境下结果永远无法流式更新到 UI生产环境不受影响由 Traefik 路由。4.2 Docker 兼容的开发服务器配置server: { host: 0.0.0.0, // 允许容器外访问 port: Number(env.PORT) || 3000, allowedHosts: env.VITE_DEV_ALLOWED_HOSTS ? ... : [], // 按环境放行 Host 头 watch: { usePolling: true, interval: 100 }, // 轮询文件监听 hmr: { port: Number(env.PORT) || 3000, clientPort: env.WDS_SOCKET_PORT ? Number(env.WDS_SOCKET_PORT) : 3000, ...(env.VITE_DEV_HMR_PROTOCOL ? { protocol: env.VITE_DEV_HMR_PROTOCOL } : {}), }, }见 vite.config.js三个环境变量分别解决容器/K8s 场景下的不同问题CHOKIDAR_USEPOLLINGtrueusePollingDocker 挂载卷不支持原生文件系统事件改用 100ms 间隔轮询保证热更新触发WDS_SOCKET_PORT浏览器通过反向代理如 Traefik 80 端口访问时HMR WebSocket 必须指向外部端口而非容器内部端口VITE_DEV_HMR_PROTOCOL当页面经 TLS 终结的 ingress 以 https 提供时HMR socket 需显式使用wss否则 Vite 按自身 http 服务推断协议socket 永远打不开VITE_DEV_ALLOWED_HOSTS逗号分隔的 Host 白名单用于集群 Pod 后浏览器发送真实域名、被 Vite DNS 重绑定防护拦截的场景。4.3 两个自定义 Rollup 插件这是从源码结构看最值得学习的部分两个插件都服务于同一目标让 OSS开源仓库与 Docker 构建时从 unstract-cloud 仓库复制进来的src/plugins/插件树共存。optionalPluginImports()vite.config.js当代码里try { await import(./plugins/...) } catch {}引用了不存在的插件路径时不直接让构建失败而是解析为一个空模块JS 导出throw new Error(Optional plugin not available)图片类资源导出空字符串由运行时的 catch 兜底。jsxInJs()vite.config.js把src/下含 JSX 的.js文件用transformWithEsbuild以loader: jsx、jsx: automatic编译。源码注释明确警告jsx: automatic是 load-bearing 的缺失时 esbuild 默认走 classic runtime 生成React.createElement(...)而这些插件文件并未 import React结果是构建全绿、HTTP 200页面却在渲染 router 时抛ReferenceError: React is not defined白屏。注释还解释了为何不复用 Vite 的esbuild选项其include会替换默认过滤条件导致.ts/.tsx完全不转换且单文件 transform API 的loader只能是一个字符串把 TypeScript 交给 JSX 解析器会把泛型当成标签因此单独写插件并限定/src/.*\.js$是刻意为之。4.4 插件注册顺序与构建产物配置插件顺序有讲究vite.config.jsoptionalPluginImports()必须在tailwindcss()之前保证缺失的 cloud-plugin 导入先被解析为 stubjsxInJs()必须排在react()之前先把.js里的 JSX 变成普通 JSReact 转换与 Rollup 的 import 分析才能正常进行。构建相关配置vite.config.jsbuild: { target: esnext, outDir: build, // 刻意不用默认的 dist/保持与 Docker 流水线兼容 sourcemap: true, cssCodeSplit: false, // 单一样式表按 chunk 加载的 CSS 会因导航顺序 // 让跨组件同等特异性规则的胜负不可预测 chunkSizeWarningLimit: 1000, rollupOptions: { output: { manualChunks: { react-vendor: [react, react-dom, react-router-dom], pdf-vendor: [ react-pdf-viewer/core, react-pdf-viewer/default-layout, react-pdf-viewer/highlight, react-pdf-viewer/page-navigation, pdfjs-dist, ], }, }, }, }值得注意README 与 VITE_MIGRATION.md 中提到的antd-vendorchunk 在当前配置里已不存在——从 package.json 依赖表看Ant Design 已被radix-ui、lucide-react、sonner等组件库取代manualChunks也相应只保留了react-vendor与pdf-vendor两组。此外define: { process.env: {} }为仍期望process.env的第三方库兜底optimizeDeps.include预打包 React 三件套以加快冷启动并在optimizeDeps.esbuildOptions中为.js指定jsxloader。五、工程脚本与 Biome 检查package.json 的scripts定义了完整命令集命令实际执行用途bun start/bun run devvite启动带 HMR 的开发服务器bun run previewvite preview本地预览生产构建:4173bun run buildvite build生产构建输出到build/bun testvitestVitest 测试watch 模式bun run lint/lint:fixbiome lint [--write] src/检查/自动修复 lintbun run format/format:fixbiome format [--write] src/检查/自动修复格式bun run check/check:fixbiome check [--write] src/lint 格式一体化bun run lint:changedgit diff 管道 biome check --write只检查本次变更的.[jt]sx?文件bun run typechecktsc --noEmitTypeScript 类型检查Biome 配置见 biome.json启用 git VCS 忽略、2 空格缩进、80 列行宽、双引号、尾逗号all并对 CSS 解析开启tailwindDirectives支持 Tailwind v4 指令linter 采用精选规则集关闭recommended显式开启correctness、suspicious、style等分类下的高价值规则如noDoubleEquals、noDebugger、noVar、useConst、noUnusedVariables等并开启assist的organizeImports自动整理导入。六、Vitest 测试配置vitest.config.mjs 是独立于vite.config.js的第二份配置其中的注释值得细读Vitest不会读取vite.config.js因此jsxInJs()与optionalPluginImports()两个插件必须在此镜像一份——注释记载了这一不对称曾导致两个真实事故一次是 248 个测试中只有 126 个被静默加载却仍全绿另一次是 OSS 检出下任何触及src/helpers/GetStaticData.js动态插件导入的测试直接收集失败。关键测试选项globals: true、environment: happy-dom、setupFiles: ./src/setupTests.js对应 src/setupTests.js、css: falseTailwind v4 的import tailwindcss/pluginat-rule 无法被 Vitest 的 CSS 管线处理故测试中整体 stub CSS、resolve.alias中补齐→./src注释标注为 P0-14 修复此前缺失该别名首个 import/components/ui/*的测试会解析失败、exclude额外排除build/避免测试扫描器走进生产构建产物。Playwright 端到端测试位于仓库级tests/e2e/ui不在 Vitest 范围内。七、Docker 开发容器与生产镜像前端被完整容器化开发/生产共用 frontend.Dockerfile 的多阶段构建基于oven/bun:1-alpine。7.1 从仓库根启动./run-platform.sh # 仓库根的一键脚本run-platform.sh 存在 # 或手动 docker compose up前端将暴露在http://frontend.unstract.localhost。7.2 开发容器开发阶段镜像的关键点frontend.Dockerfile先只拷贝package.json与bun.lock执行bun install --frozen-lockfile --ignore-scripts利用层缓存开发服务器设置PORT80与生产 NGINX 端口对齐EXPOSE 80启动命令先执行/app/generate-runtime-config.sh再bun run startCMD 见第 34 行因为 alpine 基础镜像不会自动跑/docker-entrypoint.d/HMR 依赖 sample.env 中的CHOKIDAR_USEPOLLINGtrue、WDS_SOCKET_PORT浏览器经代理连接的端口与 4.2 节的vite.config.js轮询/HMR 配置共同工作。7.3 生产构建与运行时配置注入生产阶段frontend.Dockerfile流程bun install --frozen-lockfile --ignore-scripts→bun run buildVite 输出到build/基于nginx:1.29.1-alpine将build/拷入/usr/share/nginx/html并用仓库内 nginx.conf 覆盖默认配置用sed在构建之后向index.html注入script src/config/runtime-config.js/script第 69 行。之所以必须后置注入是因为 Vite 构建期会处理所有script标签而该文件构建时并不存在容器启动时 generate-runtime-config.sh 把 Docker 环境写入/usr/share/nginx/html/config/runtime-config.js生成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 字符串并对VITE_前缀保留REACT_APP_回退兼容生产代码随后从window.RUNTIME_CONFIG读取白标favicon/logo与 PostHog 开关——即改白标配置无需重新构建镜像。八、构建优化与性能手动 chunk 拆分的收益vendor 代码变化频率低、可并行下载、主包更小叠加 Vite 的依赖预打包与 tree-shaking构成生产包缓存策略。README 给出的官方性能对比CRA vs Vite文档口径的参考值指标CRAVite开发服务器启动10–30 秒1–2 秒HMR 更新2–5 秒 1 秒生产构建60–120 秒30–60 秒生产构建还包含esnext目标、内容哈希文件名、source map 生成可通过build.sourcemap配置、基于路由动态导入的自动分包。九、常见问题排查Troubleshooting环境变量不加载确认VITE_前缀确认通过import.meta.env.VITE_*访问而非process.env修改.env后重启开发服务器。Docker 中 HMR 失效检查.env中CHOKIDAR_USEPOLLINGtrue检查 vite.config.js 的watch.usePolling与hmr.clientPortWDS_SOCKET_PORT是否与外部端口一致确认 volume 挂载正确。构建报 “Cannot find module”核对导入路径bun pm ls package确认依赖已安装清理重装rm -rf node_modules bun install清理 Vite 缓存rm -rf node_modules/.vite。端口 3000 被占用lsof -ti:3000 # 找到占用进程 kill -9 $(lsof -ti:3000) # 结束它 # 或换端口vite --port 3001构建/开发缓慢清 Vite 缓存、bun update升级依赖、检查src/下过大文件、临时关闭 source map。十、代码组织与静态资源静态资源一律从public/目录以绝对路径引用// ✅ 正确 img src/images/logo.png altLogo / // ❌ 错误CRA 风格 img src{${process.env.PUBLIC_URL}/images/logo.png} altLogo /路由级代码分割使用动态导入const Dashboard lazy(() import(./pages/Dashboard))Vite 会为动态导入的模块自动拆出独立 chunk。SVG 支持两种用法public/icons/下直接引用或通过vite-plugin-svgr以import Logo from ./logo.svg?react方式作为 React 组件导入由svgr()插件支撑。React Strict Mode 在开发环境默认启用组件会被挂载两次以暴露副作用与不安全的生命周期写法该行为只发生在开发环境。当前前端目录结构结合 package.json、index.html 实际内容frontend/ ├── docs/ # 文档VITE_MIGRATION.md 等 ├── public/ # 静态资源manifest.json、icons/、favicon.ico ├── src/ │ ├── assets/ # 图片、字体 │ ├── components/ # 可复用 React 组件 │ ├── helpers/ # 工具函数 │ ├── hooks/、pages/、store/、layouts/、routes/、lib/ │ ├── config.js # 应用配置 │ ├── index.jsx # 应用入口 │ └── setupTests.js # Vitest setup 文件 ├── index.html # HTML 入口Vite 风格位于根目录 ├── vite.config.js # Vite 配置代理/HMR/chunk/自定义插件 ├── vitest.config.mjs # 独立测试配置镜像 vite 插件 ├── biome.json # Biome 检查/格式配置 ├── sample.env # 环境变量样例 ├── generate-runtime-config.sh # Docker 运行时配置生成脚本 ├── nginx.conf # 生产镜像 NGINX 配置 └── package.json # 依赖与脚本十一、CRA 迁移快速清单如果你接手旧分支或需要对照迁移差异README 给出的检查清单并对照 VITE_MIGRATION.md 中的回滚步骤所有.env中的REACT_APP_*改为VITE_*代码中process.env替换为import.meta.envSVG 导入改用?react查询参数由vite-plugin-svgr提供删除 HTML 中的%PUBLIC_URL%改用绝对路径代理配置从setupProxy.js迁移到server.proxy本仓库已含ws: true增强验证 Docker 内 HMR 与文件监听正常工作生产构建输出确认位于build/而非dist/。以上即 Unstract 前端“Vite 7 Bun Biome Vitest Docker”完整工程链路的落地细节本地一条bun start即可带 HMR 与/api代理开发bun run build产出面向 NGINX 的静态包容器场景则通过轮询监听、HMR 端口对齐与运行时配置注入三层机制保证白标与热更新同时可用。所有关键实现均可在 vite.config.js、vitest.config.mjs、biome.json 与 frontend.Dockerfile 中逐行核对。【免费下载链接】unstractLLM-Driven Extraction of Unstructured Data — Built for API Deployments ETL Pipeline Workflows项目地址: https://gitcode.com/GitHub_Trending/un/unstract创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考