news 2026/9/16 10:58:53

Unstract 前端工程指南:Vite 7 + Bun + Biome 构建体系、VITE_ 环境变量与 Docker 热更新实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unstract 前端工程指南:Vite 7 + Bun + Biome 构建体系、VITE_ 环境变量与 Docker 热更新实践

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 App(CRA)到 Vite 7 的迁移,以及如何用 Bun 作为包管理器完成本地开发、环境变量配置、生产构建与容器化部署。读完后你可以独立拉起前端开发服务器、配置代理与热更新(HMR),并理解其 Docker 镜像中运行时配置注入的完整机制。

一、技术栈与迁移背景:从 CRA 到 Vite 7

Unstract 前端是 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 当前依赖表看,reactreact-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.0

Vite 7 带来的硬性变化包括:Node.js 最低要求提升到 20.19+/22.12+(Node 18 被移除);Vitest 必须升到 3.2+;浏览器目标默认从modules改为baseline-widely-available;配置文件中__dirnameimport.meta.dirname取代(在 vite.config.js 中可以看到path.resolve(import.meta.dirname, "./src")的实际用法)。

目录结构上,CRA 与 Vite 的差异是:index.htmlpublic/移到仓库根目录(即 frontend/index.html),public/只放纯静态资源,代理配置从src/setupProxy.js移入server.proxy。当前的 index.html 使用<script type="module" src="/src/index.jsx"></script>作为应用入口,并移除了 CRA 的%PUBLIC_URL%占位符。

二、环境准备与快速启动

1. 前置条件

  • Bun:1.0.0 或更高版本(推荐);
  • Node.js:20.19.0 或更高版本(兼容性要求)。

2. 安装依赖并配置环境变量

git clone https://github.com/Zipstack/unstract.git cd unstract/frontend # 安装依赖 bun install # 复制示例环境变量文件 cp sample.env .env

仓库自带的 sample.env 是真实可用的起点,其当前内容为:

VITE_BACKEND_URL=http://localhost:8000 # Analytics - PostHog (set to false to disable tracking) # Default: false for local development VITE_ENABLE_POSTHOG=false # For development NODE_ENV=development # Enable file watching via polling instead of filesystem events # 这些对 Docker 容器内的热更新至关重要: # 普通文件系统事件通知在挂载卷上不可靠 CHOKIDAR_USEPOLLING=true # HMR WebSocket 端口 - 必须匹配外部可访问端口(Traefik = 80) # 不是 Vite 内部端口(3000),因为浏览器是通过代理连接的 WDS_SOCKET_PORT=80

README 示例中给出的另一组取值(用于本地直连开发):

VITE_BACKEND_URL=http://frontend.unstract.localhost:8081 VITE_ENABLE_POSTHOG=false VITE_FAVICON_PATH=/path/to/custom/favicon.ico VITE_CUSTOM_LOGO_URL=/path/to/custom/logo.svg

3. 启动开发服务器

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.md):

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';

Vite 提供四个内置变量:import.meta.env.MODE'development'/'production')、import.meta.env.DEVimport.meta.env.PRODimport.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_URLVITE_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_USEPOLLING=true+usePolling:Docker 挂载卷不支持原生文件系统事件,改用 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/插件树共存。

  1. optionalPluginImports()(vite.config.js):当代码里try { await import("./plugins/...") } catch {}引用了不存在的插件路径时,不直接让构建失败,而是解析为一个空模块(JS 导出throw new Error('Optional plugin not available'),图片类资源导出空字符串),由运行时的 catch 兜底。

  2. jsxInJs()(vite.config.js):把src/下含 JSX 的.js文件用transformWithEsbuildloader: "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.js):optionalPluginImports()必须在tailwindcss()之前,保证缺失的 cloud-plugin 导入先被解析为 stub;jsxInJs()必须排在react()之前,先把.js里的 JSX 变成普通 JS,React 转换与 Rollup 的 import 分析才能正常进行。

构建相关配置(vite.config.js):

build: { 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-uilucide-reactsonner等组件库取代,manualChunks也相应只保留了react-vendorpdf-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本地预览生产构建(:4173)
bun run buildvite build生产构建,输出到build/
bun testvitestVitest 测试(watch 模式)
bun run lint/lint:fixbiome lint [--write] src/检查/自动修复 lint
bun 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,显式开启correctnesssuspiciousstyle等分类下的高价值规则,如noDoubleEqualsnoDebuggernoVaruseConstnoUnusedVariables等),并开启assistorganizeImports自动整理导入。

六、Vitest 测试配置

vitest.config.mjs 是独立于vite.config.js的第二份配置,其中的注释值得细读:Vitest不会读取vite.config.js,因此jsxInJs()optionalPluginImports()两个插件必须在此镜像一份——注释记载了这一不对称曾导致两个真实事故:一次是 248 个测试中只有 126 个被静默加载却仍全绿,另一次是 OSS 检出下任何触及src/helpers/GetStaticData.js动态插件导入的测试直接收集失败。

关键测试选项:globals: trueenvironment: "happy-dom"setupFiles: "./src/setupTests.js"(对应 src/setupTests.js)、css: false(Tailwind 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.jsonbun.lock执行bun install --frozen-lockfile --ignore-scripts,利用层缓存;
  • 开发服务器设置PORT=80与生产 NGINX 端口对齐,EXPOSE 80
  • 启动命令先执行/app/generate-runtime-config.shbun run start(CMD 见第 34 行),因为 alpine 基础镜像不会自动跑/docker-entrypoint.d/
  • HMR 依赖 sample.env 中的CHOKIDAR_USEPOLLING=trueWDS_SOCKET_PORT(浏览器经代理连接的端口)与 4.2 节的vite.config.js轮询/HMR 配置共同工作。

7.3 生产构建与运行时配置注入

生产阶段(frontend.Dockerfile)流程:

  1. bun install --frozen-lockfile --ignore-scriptsbun run build(Vite 输出到build/);
  2. 基于nginx:1.29.1-alpine,将build/拷入/usr/share/nginx/html,并用仓库内 nginx.conf 覆盖默认配置;
  3. sed在构建之后index.html注入<script src="/config/runtime-config.js"></script>(第 69 行)。之所以必须后置注入,是因为 Vite 构建期会处理所有<script>标签,而该文件构建时并不存在;
  4. 容器启动时 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)

  1. 环境变量不加载:确认VITE_前缀;确认通过import.meta.env.VITE_*访问(而非process.env);修改.env后重启开发服务器。
  2. Docker 中 HMR 失效:检查.envCHOKIDAR_USEPOLLING=true;检查 vite.config.js 的watch.usePollinghmr.clientPortWDS_SOCKET_PORT)是否与外部端口一致;确认 volume 挂载正确。
  3. 构建报 “Cannot find module”:核对导入路径;bun pm ls <package>确认依赖已安装;清理重装rm -rf node_modules && bun install;清理 Vite 缓存rm -rf node_modules/.vite
  4. 端口 3000 被占用
lsof -ti:3000 # 找到占用进程 kill -9 $(lsof -ti:3000) # 结束它 # 或换端口:vite --port 3001
  1. 构建/开发缓慢:清 Vite 缓存、bun update升级依赖、检查src/下过大文件、临时关闭 source map。

十、代码组织与静态资源

静态资源一律从public/目录以绝对路径引用:

// ✅ 正确 <img src="/images/logo.png" alt="Logo" /> // ❌ 错误(CRA 风格) <img src={`${process.env.PUBLIC_URL}/images/logo.png`} alt="Logo" />

路由级代码分割使用动态导入:const Dashboard = lazy(() => import('./pages/Dashboard')),Vite 会为动态导入的模块自动拆出独立 chunk。SVG 支持两种用法:public/icons/下直接引用,或通过vite-plugin-svgrimport 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.env
  • SVG 导入改用?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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 10:53:54

AI如何革新学术写作:从文献管理到格式优化

1. 项目概述&#xff1a;当学术写作遇上AI效率革命去年帮导师审阅研究生论文时&#xff0c;有个现象让我印象深刻&#xff1a;超过70%的延期毕业案例&#xff0c;问题都出在论文写作环节。学生们不是在文献海洋里迷失方向&#xff0c;就是在格式调整中耗尽耐心。这让我开始思考…

作者头像 李华
网站建设 2026/9/16 10:53:04

My new feature

My new feature 【免费下载链接】rerun Visualize, query, and stream to train on multimodal robotics data. 项目地址: https://gitcode.com/GitHub_Trending/re/rerun Short description. Docs: TODO(name): add docs link Example: TODO(name): add example link …

作者头像 李华
网站建设 2026/9/16 10:52:52

2026年四款零基础生产力工具深度评测

1. 项目概述&#xff1a;2026年四款零基础神器解析2026年已经到来&#xff0c;技术工具的迭代速度远超我们想象。最近我在实际工作中测试了四款真正适合零基础用户的生产力工具&#xff0c;它们完美诠释了"复杂功能简单化"的设计理念。这四款工具覆盖了内容创作、数据…

作者头像 李华
网站建设 2026/9/16 10:51:24

【Numpy】第四部分:数据拼接、拆分与变形(工程篇)

第四部分&#xff1a;数据拼接、拆分与变形&#xff08;工程篇&#xff09; 在处理多源异构数据或构建复杂的系统仿真时&#xff0c;经常需要将不同维度的计算结果拼接成增广矩阵&#xff08;例如状态空间中的 [A∣B][A\vert{}B][A∣B] 矩阵&#xff09;&#xff0c;或者提取长…

作者头像 李华
网站建设 2026/9/16 10:49:56

Astryx Vega/Vega-Lite集成指南:声明式图表与Astryx主题联动

Astryx Vega/Vega-Lite集成指南&#xff1a;声明式图表与Astryx主题联动 【免费下载链接】astryx An open source design system thats fully customizable and agent ready 项目地址: https://gitcode.com/GitHub_Trending/as/astryx Astryx 是一个开源、完全可定制的设…

作者头像 李华
网站建设 2026/9/16 10:49:47

用Interactive Html Bom把PCB版图与BOM变成可交互HTML

简介&#xff1a;InteractiveHtmlBomForAD 是一款面向 AD&#xff08;AutoDesk Inventor&#xff09;的交互式物料清单生成工具&#xff0c;基于前端技术开发&#xff0c;服务电子设计与结构工程等需要频繁维护 BOM 的岗位&#xff1b;它把 AD 工程数据解析为可在浏览器中直接查…

作者头像 李华