M
Vue 3TypeScript架构SaaS

Vue 3 + TS 在多租户 SaaS 后台的实践:分层、权限与可维护性

把 vue3-admin-ts 沉淀一篇文章出来,聊聊在多租户 + 多角色权限场景下,前端架构是怎么演进的。

背景

过去一年多在做一个装修行业 SaaS 平台,营销、交付、商户、集团四个子系统并行,前端主仓库是 vue3-admin-ts。这里把几个关键决策整理一下。

1. 分层:API 层 vs Service 层

早期所有请求都堆在 src/api/,文件多了之后业务逻辑和 HTTP 调用耦合死。重构后:

  • src/api/ —— 只负责 HTTP 调用,返回原始响应。
  • src/service/ —— 业务逻辑层:组合多个 API、缓存策略、字段归一化。
  • src/store/ / Composable —— 状态层,只通过 service 拿数据。

好处:换请求库(axios → fetch)、换字段命名、加埋点,都只动单层。

2. 权限:路由级 + 按钮级双轨

vue3-admin-ts 用了 roles 模式(见 src/settings.tspermissionMode):

  • 路由级router.beforeEach + 动态 addRoute,按角色裁剪可见菜单。
  • 按钮级:自定义 v-permission 指令,避免在每个组件里写 v-if="user.roles.includes('admin')"

权限点用后端下发的二维数组,前端不做硬编码兜底——这能避免”前后端权限对不上”的经典 bug。

3. 可维护性:几个小约定

  • 常量集中:路由名、权限点、字典值全部进 src/consts/ 或专门的 dict/ 目录,不允许在组件里写魔法字符串。
  • 组件目录按业务域划分,不按组件类型(components/Button/ ❌,views/marketing/coupon/ ✅)。
  • Vuex 模块自动注册require.context 扫描,避免每次加 store 都要改入口。
  • Patch 机制:Element Plus 有过几次回归 bug,直接打 patch 进 patches/,比等官方修快。

4. 一些踩过的坑

  • 多租户的数据隔离:前端不要相信 URL 里的 tenantId,永远从用户上下文取。
  • 路由缓存:keep-alive + 动态路由时,include 名字要稳定,否则会反复重新渲染。
  • 类型生成:openapi-typescript 自动生成类型后,DTO 改字段不会让你编译报错——CI 一定要跑 tsc-check

收尾

架构没什么银弹,关键是让下一次改需求的人(可能是三个月后的自己)能快速看懂。这个仓库在这一年多的迭代里基本没大改过结构,主要就是新加模块,说明方向是对的。