每次技术选型 Admin 模板时,我都会问自己一个问题:如果半年后老板说"把 Element Plus 换成 Ant Design",我有多大把握能扛下来?
这个问题的答案,几乎总是沉默。
因为大多数 Admin 模板的设计前提就是"你和这个 UI 框架锁死了"。业务逻辑写在组件里,页面耦合在 UI 库的 API 上,换框架不是换皮肤,是换骨架。
但有一个项目对这个老问题给出了不同的回答。Vue Vben Admin v5 —— 一个在同一个 monorepo 里跑着 Ant Design Vue、Element Plus、Naive UI、TDesign、Antdv Next 五套 UI 框架变体的 Admin 模板。
不是"支持多种 UI",是"同时拥有五种完整应用入口"。
这篇文章不翻译 README,我们从架构层面拆解它做对了哪些事。
https://github.com/vbenjs/vue-vben-admin
在深入之前:Admin 模板的三条进化路线
要理解 Vben Admin v5 的架构意义,得先看看 Admin 模板在 Vue 生态里的三条进化路线。
第一条线:脚手架时代。 早期的 vue-element-admin 代表了一个时代——它是一套写好的完整系统,功能齐全,开箱即用。但它的代价是:你继承的不仅是功能,还有作者的技术选型和设计决策。想换个 UI 库?想换种状态管理方式?对不起,改不动。
第二条线:组件库时代。 后来出现了一批"Admin 组件库",提供一些通用的表格、表单、弹窗组件。好处是灵活,你可以按需选用。坏处是:没有统一的架构理念,组件之间是松散的,你需要自己把它们组装成一个系统。
第三条线:架构平台时代。 Vben Admin v5 走的是这条路。它不是一堆页面,也不是散装的组件,而是一套架构方法论——定义核心的抽象层、适配模式、配置系统,然后在这个框架上跑多个完整应用。
v5 不是 v2 的升级,是完全重写。v2 走的是第二条线(功能齐全的 Admin 模板),v5 选择了第三条线。这个选择意味着:你拿到的不是一套现成的页面,而是一套可以衍生出无数套页面的架构。
理解了这一点,再看后面的细节,味道就不一样了。
一、先说结论:它的核心资产不是页面,是适配层
大多数 Admin 模板的核心资产是一堆写好的页面。Vben Admin v5 的核心资产,是一层让业务逻辑和 UI 框架解耦的适配层。
用一个不太准确但好理解的类比:它做了一件 Vue 生态里的"React Native"做的事 —— 定义了一套自己的"中间表示层",然后为每个 UI 框架写一个"渲染器"。
来看它的 monorepo 结构:
apps/
web-antd/ # Ant Design Vue 应用入口
web-ele/ # Element Plus 应用入口
web-naive/ # Naive UI 应用入口
web-tdesign/ # TDesign 应用入口
web-antdv-next/ # Antdv Next 应用入口
backend-mock/ # Nitro Mock 服务器
packages/
@core/ # 核心框架(不依赖具体 UI 框架)
base/ # 共享工具、图标、类型
ui-kit/ # 框架无关的 UI 原语(form-ui、layout-ui、menu-ui 等)
preferences/ # 偏好配置系统
composables/ # Vue 组合式函数
effects/ # 功能模块(权限、布局、请求、插件)
stores/ # Pinia 状态管理
关键在 packages/@core/ui-kit/。这里面是一组框架无关的 UI 原语 —— form-ui、layout-ui、menu-ui、popup-ui、tabs-ui,以及一套基于 shadcn-vue 的基础组件。它们不直接依赖 Ant Design 或 Element Plus,只定义"表单应该有什么行为"、"菜单应该怎么展开收起"这样的抽象接口。
然后每个 app 通过自己的 adapter 把这些抽象接口"翻译"成具体 UI 框架的组件。
这就是为什么换 UI 框架不需要重写业务逻辑。业务逻辑对抽象接口编程,adapter 负责翻译成具体的 UI 框架 API。
好架构的标准不是"能做什么",而是"能换什么"。
二、权限系统:三种模式,不再是二选一
几乎所有 Admin 模板都会处理权限问题。常见的做法是二选一:要么前端写死角色和路由映射,要么后端返回菜单列表前端动态生成。
Vben Admin 的做法更灵活:三种模式,运行时切换。
// packages/effects/access/src/accessible.ts(简化示意)
asyncfunctiongenerateRoutes(mode: AccessModeType, options) {
switch (mode) {
case'backend':
// 后端模式:从 API 获取菜单,动态生成路由
returnawaitgenerateRoutesByBackend(options);
case'frontend':
// 前端模式:基于角色过滤静态路由
returnawaitgenerateRoutesByFrontend(routes, roles);
case'mixed':
// 混合模式:前后端同时执行,合并结果
const [frontendResult, backendResult] = awaitPromise.all([
generateRoutesByFrontend(routes, roles),
generateRoutesByBackend(options),
]);
returnmergeRoutesByName(backendResult, frontendResult);
}
}
前两种模式大家都熟悉。关键是 mixed 模式——它会同时执行前端角色过滤和后端菜单拉取,然后把两边的结果按路由名合并。这在什么场景下有用?部分菜单由后端控制、部分由前端控制的混合权限场景。 比如系统设置页面的路由写死在前端,但业务模块的菜单从后端获取。
更关键的是,这个 mode 不是写死在代码里的环境变量,而是通过 PreferenceManager 在运行时读取的配置:
// 用户可以在偏好设置面板里切换权限模式
// 不需要重启应用,不需要重新编译
preferences.update({ app: { accessMode: 'backend' } });
这对技术负责人来说是个很实在的价值。项目初期用前端模式快速迭代,后端接口准备好后切到后端模式,遇到混合场景再切到 mixed —— 中间不需要重构权限逻辑。
三、偏好驱动架构:整个 UI 是一个配置对象
如果说多 UI 框架适配是 Vben Admin v5 最大的亮点,那 PreferenceManager 就是它最被低估的设计。
这个类做的事听起来简单:把整个 UI 的行为抽象成一个可配置的对象。但做到极致后,效果惊人。
// packages/@core/preferences/src/preferences.ts
classPreferenceManager {
// 15 大配置类别
// app / breadcrumb / copyright / footer / header / logo
// navigation / shortcutKeys / sidebar / tabbar / theme
// transition / widget + 自定义扩展
}
每个配置变更会自动:
节流保存
到 localStorage(150ms debounce,避免频繁写入) 同步更新 CSS 自定义属性
实现实时主题切换 触发断点检测
根据 Tailwind 断点自动切换移动端模式 跟随系统主题
( prefers-color-scheme),支持auto模式
真正让它与众不同的是 自定义扩展机制:
// apps/web-antd/src/preferences.ts
// 应用可以定义自己的偏好字段,不需要修改核心代码
exportconst preferences = definePreferencesExtension({
extraFields: {
tenantMode: 'single', // 租户模式(single/multi)
defaultTableSize: 'middle', // 默认表格尺寸
reportTitle: '', // 报表标题
enableFormFullscreen: true, // 表单全屏模式
},
// 每个字段会出现在偏好设置面板中
});
这意味着什么?你的业务团队可以把自己的配置需求(比如租户 ID、品牌色、默认语言)挂到这个偏好系统上,享受它自带的持久化、热更新、CSS 变量同步等能力。
不是每个好功能都需要从零造轮子,有时候好功能是把一个已有的轮子扩展到你需要的地方。
四、状态管理:把安全当回事的 Pinia 用法
大多数 Vue 项目用 Pinia 做状态管理,持久化就是 pinia-plugin-persistedstate 一行配置,数据明文存 localStorage。
Vben Admin 在生产环境多走了一步:
// packages/stores/src/setup.ts
importSecureLSfrom'secure-ls';
// 开发环境:普通 localStorage
// 生产环境:AES 加密存储
const storage = isDev
? localStorage
: newSecureLS({
isCompression: true,
encodingType: 'aes',
encryptionSecret: getStorageEncryptionKey(),
});
生产环境的 token、用户信息、权限数据全部经过 AES 加密后存入。不是防黑客(前端没有真正的安全),是防"随便打开 F12 就能看到 token"这种最低级别的信息泄露。
另外,每个应用的存储都有命名空间隔离:
const storagePrefix = `${VITE_APP_NAMESPACE}-${version}-${env}`;
同一个域名下部署多个应用不会互相覆盖状态。这种细节在单体 Admin 模板里很少见,但在 monorepo 架构里是必须的 —— 因为它本来就要同时管理五个应用。
五、路由守卫:分层设计的力量
Vben Admin 的路由守卫拆成了两层,这个拆分看似简单,但体现了好的关注点分离:
// apps/web-antd/src/router/guard.ts
// 第一层:通用守卫
functionsetupCommonGuard(router) {
router.beforeEach(() => {
NProgress.start(); // 页面加载进度条
// 记录已加载的页面,避免重复加载
});
}
// 第二层:访问守卫
functionsetupAccessGuard(router) {
router.beforeEach(async (to, from, next) => {
// 1. 检查 token
// 2. 首次鉴权后动态生成路由
// 3. 角色权限匹配
// 4. 403 处理
});
}
为什么要拆?因为这两个守卫的变化原因不同。通用守卫关心的是 UX(进度条、加载优化),访问守卫关心的是安全(鉴权、权限)。把不同变化原因的东西分开,是 SRP(单一职责原则)在路由层面的应用。
更实际的好处是:你可以单独替换或扩展其中一层,不影响另一层。比如你想把 NProgress 换成自己的加载动画,只改 commonGuard 就行。
六、请求层:两个 Client 的设计直觉
看它的 HTTP 请求封装:
// apps/web-antd/src/api/request.ts
export { requestClient, baseRequestClient };
两个导出,不是多余的设计。
requestClient带所有拦截器(token 注入、语言头、401 自动刷新、统一错误处理) baseRequestClient裸客户端,什么都没有
登录接口为什么要用 baseRequestClient?因为你还没有 token,不需要 token 注入拦截器。刷新 token 的接口为什么要用裸客户端?因为刷新逻辑本身就在拦截器里,用带拦截器的客户端会导致无限递归。
这种"一个带全副武装,一个干干净净"的设计,比"用一个客户端然后到处加 skipInterceptor: true"要干净得多。
七、工程质量:比代码能力更重要的是检查能力
如果说架构设计是"能不能写好",那工程质量体系就是"能不能一直写好"。
Vben Admin 的 lint 配置是我见过最"过分"的 Vue 项目:
oxlint → Rust 编写的超快 linter(毫秒级)
ESLint → 语义规则(vue/unicorn/perfectionist 等 10+ 插件)
oxfmt → 格式化(替代 Prettier)
Stylelint → 样式检查(Vue/SCSS 支持)
cspell → 拼写检查
commitlint → 提交信息规范(Angular convention)
SonarCloud → 代码质量门禁
CodeQL → 安全扫描
注意 oxlint 和 ESLint 是同时运行的。这不是重复劳动,是分工:oxlint 跑语法和风格规则(快),ESLint 跑语义和项目特定规则(慢但必要)。开发时用 oxlint 做即时反馈,CI 里跑完整 ESLint 检查。
这种分层 lint 策略保证了两个通常矛盾的目标:开发体验(不卡)和代码质量(不漏)。
八、monorepo 治理:pnpm catalog 协议的实战价值
这个项目用 pnpm workspace + catalog: 协议管理依赖版本。这是什么意思?
# pnpm-workspace.yaml
catalog:
vue:^3.5.0
vue-router:^5.0.0
pinia:^3.0.0
所有子包的 vue 依赖都指向同一个版本。不需要在每个 package.json 里手动对齐版本,不需要担心 web-antd 用 Vue 3.5 而 web-ele 不小心用 Vue 3.4。
对于同时维护五个应用的项目来说,这不是"好用"的问题,是"不用就会死"的问题。依赖版本漂移是 monorepo 最大的隐性成本,catalog: 协议把这个成本降到了零。
九、插件体系:功能模块的"乐高"组装
很多 Admin 模板的插件是"锦上添花"——加个图表库、加个富文本编辑器,改起来牵一发而动全身。
Vben Admin 的插件设计在 packages/effects/plugins/ 下,采用按需加载 + 类型安全的模式:
packages/effects/plugins/
echarts/ # 图表组件(基于 ECharts 6)
tiptap/ # 富文本编辑器(基于 TipTap 3)
vxe-table/ # 高级数据表格(基于 VXE Table 4)
motion/ # 动画组件(基于 @vueuse/motion)
每个插件是一个独立的包,有完整的 package.json 和类型导出。应用的 package.json 里需要哪个引哪个,不需要的插件根本不会被打包进来。
更值得学习的是它的 Tree Shaking 友好设计。每个插件包的入口文件只导出公共 API,内部实现全部标记为 private。配合 Vite 的 tree shaking,最终打包体积只包含你用到的部分。
以 ECharts 插件为例——它不是简单地把 ECharts 组件包一下,而是封装了一套符合项目设计语言的图表组件:
自动适配主题色(跟随 PreferenceManager 的主题配置) 自动响应式缩放(基于 ResizeObserver) 统一的加载状态和空数据态
这意味着你用的不是一个通用的 ECharts 封装,而是一个和项目 UI 语言一致的图表组件。这种"插件不是工具包,而是产品级组件"的思路,是很多项目忽略的。
十、内置 Mock 服务器:不是模拟数据,是完整 API 契约
大多数 Admin 模板的 mock 方案是在 src/mock/ 目录里写几个 JSON 文件,或者用 Mock.js 拦截 axios 请求。
Vben Admin v5 做了一件更重但更正确的事:用一个独立的 Nitro 服务跑完整的 Mock API。
apps/backend-mock/
routes/api/ # API 路由(和真实后端路径一致)
utils/ # 工具函数(JWT、faker 数据生成)
middleware/ # 中间件(鉴权校验)
这个 Mock 服务器:
路径和真实后端完全一致
—— 前端代码不需要任何 mock 相关的改动,切换后端时把请求地址一改就行 支持 JWT 鉴权
—— 登录、token 刷新、权限校验,和真实后端一样的流程 用 Faker 生成动态数据
—— 不是写死的 JSON,是每次请求都有合理变化的模拟数据
这对前后端并行开发的团队来说,价值巨大。前端不需要等后端接口完成就可以开始完整的功能开发和联调测试,而且测试流程和真实环境几乎一致。
当然,Nitro Mock 服务器增加了项目的启动成本。对于一个不需要 Mock 的个人项目来说,这可能是多余的。但对于需要协作的企业级项目,这个设计是生产力工具。
十一、国际化与工程细节:魔鬼在细节里
国际化(i18n)方面,Vben Admin 用 vue-i18n 11 做了懒加载语言包。不是把所有语言文件一次性全加载,而是按需加载当前需要的语言。配合 Vue Router 的懒加载,首屏不会因为国际化多出一个阻塞请求。
语言包结构也很规范:
packages/locales/
src/
langs/
zh-CN/ # 简体中文
en-US/ # 英文
每个语言包是一个独立的 JSON 文件,按功能模块拆分(登录页、设置面板、通用文案等)。新增语言只需要在 langs/ 下加一个文件夹,不需要修改核心代码。
另外几个值得注意的工程细节:
HMR 对 Pinia 做了专门处理
每个 store 模块都有 import.meta.hot.accept(acceptHMRUpdate(...)),开发时修改 store 不需要刷新整个页面markRaw用于大型配置对象PreferenceManager 合并大型配置时用 markRaw()避免 Vue 不必要的响应式代理开销动态路由用
import.meta.glob懒加载路由组件不是一次性导入,而是按需加载,减少首屏 JS 体积 不支持 IE
明确只支持 Chrome 80+ 和现代浏览器,没有 polyfill 包袱,可以放心用最新 JavaScript 特性
这些细节单独看都不算"大功能",但组合在一起就是"这个项目的开发者是真的在用它做产品,不是写完就扔"的证明。
十二、它不适合谁?
写到这里,好像这个项目全是优点。但它确实有不适合的场景:
简单项目:如果你只需要一个登录页 + 几个表格页面,Vben Admin 的架构复杂度是多余的。它的价值在规模上来之后才体现。
需要深度定制 UI 的场景:虽然它支持多 UI 框架,但核心 UI 原语的设计是通用的。如果你需要高度定制化的组件交互,可能需要 fork 后深度修改。
从 v2 升级:v5 是完全重写,不兼容 v2。如果你是 v2 用户,升级等于迁移到新项目。
十三、总结
Vben Admin v5 最值得学习的不是它的页面写得有多好,而是它回答了一个架构层面的问题:
当你的项目需要同时服务多种技术选型时,如何设计才能让变化成本最低?
它的答案是:抽象出中间层,用适配模式隔离变化点,用配置系统替代硬编码,用工程质量保证持续正确。
这不是 Vue Admin 模板的专利。这套思路可以迁移到任何需要"支持多种方案"的项目中。
真正的技术债不在于写了多少代码,而在于换不动什么东西。好的架构让变化成为常态,而不是事故。
你现在的 Admin 项目,如果明天要换 UI 框架,需要改多少文件?欢迎在评论区聊聊。
如果这篇文章对你有启发,点个赞或在看,让更多人看到。