frp frpc 仪表盘:Vue 3 + Vite 管理面板的搭建、构建与二进制集成全解
frp 项目为客户端提供了一套内置的 Web 管理面板 frpc-dashboard,用于可视化查看和管理 frpc 的代理(Proxy)与访问者(Visitor)配置。本文基于仓库中的 web/frpc/README.md 展开,完整覆盖该文档给出的依赖安装、开发热重载、生产构建与 ESLint 检查四步工作流,并结合 Makefile、vite.config.mts 与 embed.go 等仓库文件,深入到前端如何与 frpc 二进制内的 HTTP API 对接、构建产物又如何通过 go:embed 打包进最终可执行文件的实现细节。读完后,你可以独立完成该面板的本地开发调试,并理解其从源码到 frpc 单文件的完整集成链路。
面板定位:frpc 客户端的管理界面
frp 的 Web 前端位于 web/ 目录下,采用 npm workspaces 组织为三个子工程:
web/frpc/—— frpc 客户端仪表盘(即本文主角,package.json中名为frpc-dashboard);web/frps/—— frps 服务端仪表盘;web/shared/—— 两侧共用的组件与样式库。
这一结构在根级 web/package.json 中声明:"workspaces": ["shared", "frpc", "frps"],并提供了统一的单元测试入口 test:unit(vitest)。
frpc 面板的运行时数据来自 frpc 二进制自带的 HTTP API。以 conf/frpc_full_example.toml 为例,webServer.port = 7400 配置后,frpc 会在 7400 端口对外提供 API 与页面,路由定义见 client/api_router.go。前端源码 web/frpc/src/api/frpc.ts 与 web/frpc/src/api/http.ts 则封装了对这套 API 的调用。
安装依赖:workspaces 下的依赖管理
原始文档给出的第一步是:
yarn install
这里需要结合仓库现状补充一个关键事实:仓库根级提供的是 package-lock.json 而非 yarn.lock,且所有 Makefile 目标实际都走 npm。查看 web/frpc/Makefile:
install:
@cd .. && npm install
也就是说,文档中的 yarn install 在实际仓库中应当替换为在 web/ 目录下执行 npm install(或直接执行 make install,它会自动 cd .. 到 web/ 再安装)。这样做的原因是 frpc、frps、shared 三个工程共用一套 workspaces 依赖,必须在 web/ 根目录统一安装,子目录单独安装会破坏 @shared 别名解析(见下文 Vite 配置部分)。
开发模式:make dev 启动 Vite 热重载
文档第二步是开发用热重载:
make dev
对应 Makefile 目标:
dev:
@npm run dev
而 package.json 中 dev 脚本就是 vite 本体,即启动 Vite 7 开发服务器。web/frpc/vite.config.mts 中与开发体验强相关的配置有两处,值得展开:
1. API 代理(第 50~60 行)
server: {
allowedHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
proxy: {
'/api': {
target: process.env.VITE_API_URL || 'http://127.0.0.1:7400',
changeOrigin: true,
},
},
},
前端所有 /api 前缀的请求会被代理到本地 frpc 实例的 HTTP API 服务,默认地址 http://127.0.0.1:7400,与 frpc 默认 webServer.port 一致。如果你把 frpc 的 webServer 配在其他地址,可通过环境变量 VITE_API_URL 覆盖代理目标——这是联调本地 frpc 二进制时最重要的开关。
2. 路径别名(第 25~31 行)
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'@shared': fileURLToPath(new URL('../shared', import.meta.url)),
},
dedupe: ['vue', 'element-plus', '@element-plus/icons-vue'],
},
@shared 别名指向 web/shared,这正是 frpc 与 frps 共享 UI 组件(如 web/shared/components/ 下的 ActionButton、BaseDialog 等)的机制;dedupe 则保证 Vue/Element Plus 在 monorepo 中只有一份实例。
此外,vite.config.mts 还启用了 unplugin-auto-import 与 unplugin-vue-components(均带 ElementPlusResolver),即 Element Plus 组件按需自动导入,生成物见仓库中的 auto-imports.d.ts 与 components.d.ts;SCSS 全局注入 @use "@shared/css/_index.scss" as *;,使共享变量与 mixin 在每个样式文件中直接可用。
生产构建:make build 的类型检查 + 压缩
文档第三步:
make build
对应 build: install 依赖 npm run build,而 package.json 中:
"build": "run-p type-check build-only",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit"
即构建是两阶段并行执行:vue-tsc --noEmit 做 TypeScript 类型检查,vite build 产出 dist/。构建阶段 vite.config.mts(第 39~49 行)还做了体积优化:
build: {
assetsDir: '',
chunkSizeWarningLimit: 1000,
minify: 'terser',
terserOptions: { compress: { drop_console: true, drop_debugger: true } },
},
terser 压缩并移除 console/debugger 调用,assetsDir: '' 让资源与 index.html 平铺在 dist/ 根下——这一点与后文的 go:embed dist 直接相关。
需要特别注意的是根目录 Makefile 第 4 行的构建标签联动逻辑:
NOWEB_TAG = $(shell [ ! -d web/frps/dist ] || [ ! -d web/frpc/dist ] && echo ',noweb')
若 web/frpc/dist 不存在,编译 frpc 时会自动追加 noweb 构建标签,产出不含面板的二进制;反之则正常嵌入前端。因此完整的可带面板二进制应执行 make all(等价于 env fmt web build),先经 frpc-web 目标($(MAKE) -C web/frpc build)产出 dist/,再执行 go build -tags frpc ./cmd/frpc。
代码质量:make lint
文档第四步:
make lint
对应 npm run lint,即 eslint . --fix。ESLint 基于 Vue 3 + TypeScript 栈配置(eslint-plugin-vue、@vue/eslint-config-typescript、Prettier,见 web/frpc/eslint.config.js 与 package.json 的 devDependencies)。另有一个不带 --fix 的 lint:check 脚本,专供 CI 使用——根目录 Makefile 的 web-ci 目标即演示了完整 CI 链路:
web-ci:
cd web && npm ci && npm run lint:check --workspace frps && npm run lint:check --workspace frpc && npm run test:unit && npm run build --workspace frps && npm run build --workspace frpc
单元测试同样可以本地运行:web/ 根目录下 npm run test:unit(vitest),frpc 侧的用例如 web/frpc/test/proxy-converters.test.ts,覆盖代理配置的表单转换逻辑。
从 dist 到二进制:embed 集成机制
frpc 面板最终不是独立部署的 Web 服务,而是打进 frpc 可执行文件。核心在 web/frpc/embed.go:
//go:build !noweb
//go:embed dist
var EmbedFS embed.FS
func init() {
assets.Register(EmbedFS)
}
init() 在包加载时把 embed.FS 注册到公共的 assets/assets.go。Register(第 52~56 行)通过 fs.Sub(fileSystem, "dist") 取到 dist 子树,使上层无需感知打包前缀;Load(第 40~50 行)则决定运行时的文件系统来源——若传入磁盘路径则优先从磁盘读文件(便于调试),否则用内存中的嵌入内容,两者皆无时退化为 emptyFS(所有路径 404)。
与之配合的 web/frpc/embed_stub.go 是一个空实现:
//go:build noweb
package frpc
当编译带 noweb 标签时,embed 文件被排除、stub 生效,二进制即不含前端资源——这正与根 Makefile 中 NOWEB_TAG 的自动探测形成闭环。frps 侧存在完全对称的 web/frps/embed.go 结构。
小结:一条可复现的本地工作流
综合文档与仓库源码,frpc 仪表盘的完整本地工作流为:
- 在
web/下npm install(等价cd web/frpc && make install,README 中的yarn install以 npm 为准); cd web/frpc && make dev启动热重载,配合本地运行的 frpc(webServer 默认 7400)或设置VITE_API_URL指向目标实例;make build执行类型检查并产出web/frpc/dist;- 回到仓库根目录
make frpc(或make all)生成带面板的bin/frpc; - 任何时刻用
make lint(本地自修复)或npm run lint:check(CI 严格模式)保持代码风格,npm run test:unit运行前端单测。
整个体系的关键设计在于:前端以 monorepo workspace 形式与 frps 面板共享组件层,构建产物通过 go:embed + 构建标签(noweb)无缝收进单一 Go 二进制,开发者体验(Vite 代理 7400 端口)与最终交付形态(无独立 Web 服务)保持一致。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00