REA 是最近大半年我一直放在手边打磨的一套前端工程化方案,全称叫 Rapid Enterprise Architecture。说白了,它就是给中后台应用准备的一个"起步包",把路由权限、请求封装、状态管理、多环境配置、构建优化这些每个系统都要重复做一遍的事情,提前用一种稳定、可复制的方式处理掉。这篇文章我会从头讲到尾:为什么会有 REA、里面每个模块怎么设计、怎么靠它在一天内拉出一个新系统,以及我实际踩过的坑。如果你正在负责内部系统开发,或者想给团队统一一套前端基线,这篇会比较对胃口;要是你只是一个人做小项目,里面的设计思路一样可以挑着用。
1. REA 到底是什么,为什么需要它
1.1 先讲一个让人头大的场景
如果你带过三五个前端同学,大概率见过这种场面:团队里有三个业务系统,A 用的是两年前的旧脚手架,B 是某个同事趁周末临时拼的,C 干脆从别的项目里拷贝过来改了个名。表面看都能登录、能查数据,真到"加一个按钮权限""统一换主题""接手离职同事的代码"的时候,三套系统三种写法,改一处要跑三个仓库。我在早期阶段就吃过这种亏,一个内部运营平台,光权限逻辑就同时存在三份互相对不上的实现,新人进来第一周全在问"这个公共方法在哪个项目里最好使"。
这就是 REA 想解决的核心问题。它不是一个前端框架,也不绑定某一家公司的内部技术,它是一套工程化基线:目录结构、代码规范、权限模型、请求层封装、构建配置都被提前约定好。团队里任何一个新项目用它起步,长出来的样子都差不多,相当于给"快速搭建企业应用中后台"这件事做了一个标准答案。
1.2 REA 的定位:不是框架,是起步包
很多人一听模板脚手架就皱眉,觉得又是造轮子。REA 的定位不太一样,它做的是"决策收敛"。中后台系统 80% 的功能高度相似:登录、菜单、列表、表单、详情、权限控制、接口联调。与其每个项目各自纠结,不如在起点就把这些公共决策做掉。REA 全称 Rapid Enterprise Architecture,强调的不是架构有多复杂,而是"你能多快进入业务开发"。
它内部拆成三层:
- 模板层:一个可直接运行的项目骨架,包含路由、权限、请求、状态管理等基础能力。
- 预设层:针对不同场景的配置组合,比如"纯列表管理型""带复杂工作流型""需要多租户型"。
- 工具层:一个命令行工具,用来创建项目、新增页面、更新模板、检查规范。
这个结构的好处是,模板可以被团队持续维护,而不是某个人电脑上一份孤零零的代码。每次有新踩坑经验,沉淀进模板,全团队的项目都能同步受益。
1.3 哪些项目适合用 REA
先泼一盆冷水,REA 不是万能药。它最适配的场景是:中后台管理系统、运营平台、数据看板、配置中心这类以"表单 + 列表 + 权限 + 增删改查"为主体的应用。这些项目痛点极其一致,值得统一基线。
不适合的场景也明确,比如内容型官网、重度编辑器、Canvas 类应用、强自定义交互动效的产品。这些项目个性化占比太高,用统一模板反而会被束缚。我自己划清这条线之后,团队里再也没有人为了"要不要用模板"吵过架,边界清楚,选择就简单。
2. 技术落点:为什么这些选型最省心
2.1 框架与构建层的选择思路
REA 的技术选型原则是"可信、成熟、可替换"。我见过太多项目为了炫技选冷门方案,半年后社区没动静了,只能自己硬扛。中后台系统最重要的是稳,所以框架层优先选社区活跃、资料多、生态完善的方案。对应到 REA,默认前端框架就是类组件化 SPA 的主流选择,构建工具则选配置与性能平衡得最好的那一档。
有人问为什么不用性能指标更激进的方案?原因很简单,中后台项目往往要接入各种第三方库,团队里每个人的熟练度也不一样。构建工具太新,插件生态跟不上,遇到问题连文档都翻不到,省下的那点构建时间还不够填坑的。REA 在这一点上的取舍是:默认使用成熟稳定方案,但通过封装让底层可以替换。今天团队想换一个构建工具,REA 的目录结构和环境变量规范不变,只是切换适配层。
2.2 数据流与请求层别过度设计
中后台应用的状态管理,我见过最严重的问题不是缺方案,而是过度设计。有些团队一上来就把所有数据都塞进全局状态库,列表页的筛选条件、弹窗里的临时输入全放进去,最后根本搞不清状态从哪来、在哪改、什么时候丢。REA 对状态管理的边界做了明确规定:全局状态只放跨页面、跨组件的"真全局"数据,比如登录用户信息、主题配置、权限点集合;页面内部的搜索条件和表单数据,一律只放在页面级作用域里。
请求层更是如此。不要重复封装轮子,但也别裸用。REA 的请求层做成一个可插拔的封装模块,统一处理 Token 注入、接口错误码识别、HTTP 状态码拦截、超时取消、重复请求合并。业务页面里只负责描述"我请求了什么接口",不需要关心 Header 怎么带、错误弹窗怎么弹。这一点在后面核心模块部分我会展开讲。
2.3 类型系统、代码规范与自动化
工程化基线里,类型系统和代码规范属于"开始麻烦、后面真香"的部分。REA 从第一版就强制开启严格模式类型检查,宁可初始化时多改几个类型报错,也不允许"差不多就 any 一下"。原因很简单,中后台系统里接口字段多、改动频繁,没有类型约束,联调阶段经常是改一个字段、崩三个页面。
自动化规范方面,REA 内置了代码检查(Lint)和格式化配置,并在 Commit 之前强制执行。命令长什么样不重要,重要的是从机制上保证:任何人提交的代码,风格都一致。再加上每个新项目自带 CI 配置样例,跑测试、执行类型检查、构建验证这三件事在代码入库时就自动完成。常见的"我本地是好的,到测试环境就挂了"这类问题,在 CI 阶段就能拦掉一大半。
3. 核心模块拆解:REA 里每一块都在解决什么问题
3.1 路由与权限的联动设计
权限和路由是连在一起的,不能拆开做。REA 的默认设计是:路由表是菜单的唯一数据源,每个路由通过 meta 字段声明需要的权限点,菜单则由路由表过滤生成。这样菜单不会单独维护一份,权限点也不会散落在服务端和前端两套逻辑里。
给个简化示例:
const routes = [ { path: '/order/list', name: 'OrderList', component: () => import('@/views/order/list.vue'), meta: { title: '订单列表', permission: 'order:list' } }, { path: '/order/detail/:id', name: 'OrderDetail', component: () => import('@/views/order/detail.vue'), meta: { hidden: true, permission: 'order:detail' } } ]菜单生成逻辑就做一件事:把有权限并且没有标记 hidden 的路由渲染成菜单。按钮级权限用统一的指令或函数处理,页面里一个按钮需不需要显示,一行代码完成。实际落地时要注意,权限点和路由字段的对应关系一定要写进团队约定文档里,否则过两个月就会有人开始"借"别人的权限点,权限体系很快会失效。
动态路由和静态路由的区别也需要讲清楚。REA 默认采用"登录后拉权限 -> 过滤路由 -> 动态注册"的流程,而不是启动时一次性全量注册。这么做逻辑上更严谨,菜单不会先闪出一堆用户没权限的页面再消失,首屏加载体积也更小。代价是路由守卫的逻辑要写对顺序,这个我在后面出错排查部分专门说。
3.2 请求层封装与错误兜底
请求层是 REA 里最容易被低估的一块。它就是所有接口的必经之路,在这里把规则定死,后面每个业务页面都能少写一堆样板代码。REA 对请求层做了四件事:
第一,统一注入身份凭证。用户登录成功后,Token 放内存和本地持久化两处,请求拦截器自动从内存读取,丢失后再从持久层恢复。这样做的好处是切换窗口标签页后,用户身份不会闪断。
第二,统一解包响应。业务接口往往包着多层结构,页面只需要拿到真正的业务数据体。封装层把这些壳全部去掉,页面里拿到的就是可以直接渲染的数据。
第三,统一错误处理。HTTP 状态码代表传输层成果不代表业务成功,很多接口返回 200 但业务码是失败的。REA 封装里把业务码定义成枚举,全局只弹一次错误提示,避免失败请求被页面和全局弹窗各弹一遍。
第四,请求取消和去重。列表页切换筛选条件时,上一次未完成的请求应该被取消;短时间内重复触发的相同请求应该被合并。这两件事做不好,用户会明显感觉到页面卡顿或者数据闪烁。
// 请求层包了一层统一处理,业务侧只需要写这一句 const list = await request.get('/api/order/list', { page, size })这段代码后面,拦截器自动完成携带凭证、校验业务码、超时取消三件事。新同学看到这种写法,会觉得很省事;老同学则知道,省事的背后是封装层把细节扛了。
3.3 状态管理的边界划分
状态管理最怕没有边界。REA 把状态分成三类,并且文档里写死:
- 全局态:登录态、用户信息、权限点、主题偏好。这类状态跨页面、跨组件,放全局状态库。
- 页面态:列表搜索条件、当前页码、表单编辑内容、弹窗开关。这类状态只属于当前页面,放在页面作用域,不放进全局库。
- 服务端缓存:字典数据、组织树、公共下拉选项。这类数据重复请求率高,REA 默认在请求层做缓存,而不是放进全局状态库由业务代码手动维护。
服务端缓存这点经常被忽略。很多团队把字典数据请求一次之后塞进全局状态里,然后到处读取,一旦某个页面改了它又不清缓存,数据就悄悄不对了。REA 的做法是在请求层按接口统一加缓存 TTL,页面完全无感知,缓存过期自动重新请求。这么设计以后,状态库里的东西反而变少了,整体心智负担大幅降低。
3.4 多环境配置的切换机制
中后台项目必然会面对开发、测试、预发、生产多个环境,接口域名不同、调试开关不同、日志级别不同。REA 使用环境变量文件按环境拆分配置,关键点是"变量只在构建期注入"。
# 环境变量文件组织方式 .env # 所有环境公共配置 .env.dev # 开发环境 .env.staging # 预发/测试环境 .env.prod # 生产环境页面代码里通过import.meta.env或对应方式读取。比如:
const baseURL = import.meta.env.VITE_API_BASE_URL这里最容易踩的坑是变量名前缀。很多构建工具只允许带特定前缀的变量进入业务代码,其他变量会被丢弃。REA 在模板里把所有外部变量都加了统一前缀,并且加了运行时探针,新项目启动时如果发现关键变量缺失,直接在控制台打警告。宁可启动时就报错,也不要等到接口全部 404 了才回头查配置。
3.5 通用业务组件的沉淀方式
模板里除了基础能力,还内置了一套通用业务组件。最典型的是"查询列表页"骨架:上方是搜索条件,中间是操作按钮和表格,下方是分页器。这个结构在中后台项目里出现频率极高,REA 直接把它做成了一个页面容器组件。
<PageContainer :search-config="searchConfig" :table-config="tableConfig" :pagination="pagination" @query="handleQuery" />一个配置文件描述搜索字段和表格列,剩下的数据请求和分页联动由容器内部处理。这不是为了炫技,是为了让"增删改查"页面变成配置工作,而不是重复劳动。组件库的沉淀原则是:被三个以上页面用到的功能才提升为公共组件,每个公共组件必须带示例页和文档。没有这两个东西的组件,半年之后就没人敢动。
3.6 目录与命名约定
REA 对目录结构的约定是"按业务功能划分,不按技术类型划分",同时控制嵌套层级,避免深不见底。假设一个后台系统,目录看起来是这样:
src/ api/ # 接口层,按业务域拆文件 components/ # 全局公共组件 layouts/ # 页面布局骨架 router/ # 路由配置 stores/ # 全局状态 utils/ # 工具函数 views/ # 页面,按业务模块分目录 order/ list.vue detail.vue命名约定也简单:文件统一小写加中划线,组件统一用大驼峰命名,接口文件按业务域命名而非按页面命名。这套约定最大的价值不在风格统一,而是新人找代码的时间大大缩短。我看到很多团队不重视这个,结果做交接时"代码在哪个文件"全靠前辈记忆力,实在没必要。
4. 用 REA 从零拉一个项目,完整实操过程
4.1 初始化脚手架
REA 真正减轻工作量的时刻是从命令行开始的。新项目建立时,不再需要翻旧项目、删无用代码、猜依赖版本,直接执行:
npx rea create admin-web --template vue-ts命令行会先做几个交互式选择:要用哪套 UI 组件库、状态管理方案选哪个、需不需要对接权限中心、要不要内置日志上报。选完自动生成目录、装依赖、做一次初始化 Git 提交。整个过程大概一分钟,结束后跑一下:
cd admin-web npm run dev浏览器开起来就能看到登录页,接口默认走 mock 数据。我最喜欢这个阶段的设计:新项目第一次启动一定不能报错,否则新人对脚手架的第一印象就坏了。任何环境问题都应该在初始化脚本里提前检测,而不是等用户手动处理。
4.2 拉完模板后的第一件事
模板能跑通只是开始,接下来有几件事必须按顺序处理。第一,改项目元信息:项目名、包名、页面标题、logo 这些藏在代码里的硬编码都要换成新项目自己的。REA 在模板里把这些集中放在一个配置文件里,方便一次改完。第二,把 Git 远端地址换成新仓库,不要让新项目还挂在模板仓库上。第三,关掉 mock,把接口地址切到联调环境。
我见过最快的翻车就是第二步忘记处理,一个新功能分支直接推到了模板公共仓库上,代码还带出了别人项目的残留配置。后来 REA 在初始化脚本里加了一个交互确认,初始化完成前必须确认远端地址,算是从机制上堵住了这个洞。
4.3 权限与菜单怎么从 mock 切到真实后端
初始化项目默认使用本地 mock 权限数据,即登录后固定返回一组权限点。切换真实后端时,只需要换一个"拉取用户信息和权限点"的适配函数。这里有个建议:权限数据的返回结构要提前和后端同学约定好,最好用扁平的权限点数组而不是嵌套的树。嵌套树在菜单生成时看着方便,但一旦后端把某个子节点的父节点漏掉,前端渲染就出问题;扁平数组再配合前端路由元信息去映射,简单且不容易错。
接入真实后端之后,还要验证三个画面:未登录用户访问页面是否跳登录;登录后低权限用户是否看不到无权限菜单;如果用户被回收了权限,下次刷新页面菜单是否正常隐藏。这三件事在模板里都有默认逻辑,但每个项目的后端返回格式各不相同,适配层一定要写单元测试,别指望一次联调就万事大吉。
4.4 构建、部署与发布检查
开发完成后要发布,REA 在构建阶段提供了两个默认脚本:一个做完整类型检查和代码检查,另一个做产物构建。生产构建会开启代码压缩、包体积预检、依赖分包。部署环节,产物默认输出到 dist 目录,支持将静态资源单独上传到 CDN,入口 HTML 留在服务器侧。这些在模板里都有注释参考,直接把构建生成的路径配置给部署平台就行。
这里强调一个容易忽略的细节:中后台项目经常做成单页应用,路由使用前端路由,此时部署方需要把不存在的路径都回退到入口 HTML,否则用户手动刷新二级页面会出现 404。这个配置在常规部署文档里都有,但几乎每个月都会有团队在这里卡住,REA 模板在构建产物里附带了一份部署说明,明确写出服务端要做的回退配置。
5. 性能与体感优化
5.1 首屏加载,第一直观印象
中后台系统虽然面向内部用户,但首屏加载做不到位,照样会被吐槽"这系统怎么这么慢"。REA 在模板上默认做了几件事:路由级懒加载、第三方依赖单独分包、首屏接口并行请求、业务代码不做首屏全量加载。路由级懒加载最简单也最有效:用户只看得到当前页面组件,其他页面的代码先不下载。
第三方依赖单独分包要看饼图数据。曾经有个项目,一个图表库占用近 300KB,静态导入让首屏体积多了三分之一。后来改成按需加载、按需注册,首屏体积降到了原来的六成。做法很简单:让图表库只在实际用到的页面里动态导入,不要出现在全局入口。这类优化在 REA 模板里默认就做好了,新项目不需要再踩一遍。
5.2 构建产物分析,别只看体积数字
REA 自带产物分析脚本,跑一次能看到每个依赖打包后的体积。但分析时要会看重点:不是所有大体积依赖都要压,要区分"首屏路径上"和"用户交互后才会用到"的依赖。首屏路径上出现的大体积依赖必须优化;后者可以放到懒加载里,不需要压代码,等用户点开对应功能再加载也行。
我曾经在一张导出报表页面里引了一个很大的处理库,导致首屏加载慢。后来只是把页面对应的组件改成懒加载,首屏压力马上缓解。这类优化价值在于,不牺牲功能,也能带来明显的体验提升。
5.3 运行期体验,决定用户愿不愿意用
系统跑起来之后,体感细节往往决定用户评价。REA 内置了路由切换进度条、全局请求 loading 状态、错误边界兜底和页面缓存策略。路由切换进度条看似小事,但对中后台很实用:接口慢时,用户至少知道系统在干活,而不是以为卡死了。
页面缓存也很讲究。列表页用户翻到第三页,点进详情再返回,如果列表恢复到第一页,体感很割裂。常见做法是把列表页的状态缓存住,返回时保持原位置。REA 模板对该保留的页面和该重新加载的页面做了明确标注,新人照着约定走,就不会出现"返回丢数据"的吐槽。
6. 我在实操中踩过的坑和排查方法
6.1 依赖版本冲突,比想象中频繁
模板维护最头疼的不是写代码,而是依赖版本在不同项目里跑着跑着就分叉了。A 项目锁住了一个依赖的旧版本,B 项目因为新需求升了新版本,公共逻辑在两边表现不一致。解决办法是两条腿走路:模板仓库必须维护精确锁文件,新项目初始化时按锁文件安装,不做"随缘升级";团队里统一依赖升级的频率,比如每个迭代集中评估一次,而不是谁想在本地升就升。
有一次我在模板里升级了某个基础库的大版本,结果排查发现三个存量项目的业务代码对旧接口有依赖,升级后全部编译失败。后来学乖了,基础库升级先在模板仓库里开一个试验分支,用自动化脚本跑一遍存量项目的冒烟用例,通过之后才合入正式模板。这套流程虽然保守,但非常稳。
6.2 权限路由不生效,九成是顺序问题
自己做过的项目里,权限路由不生效这次那个"为什么我配了权限还是能进"的问题。排查下来大多数是路由守卫的执行顺序写错了。路由守卫里要做的动作有先后:先确认用户是否已登录,再拉取用户信息和权限点,再过滤路由,最后放行目标页面。如果权限点还没拉回来就去判断"有没有权限",结果一定是 false,用户就会被错误地挡在页面外。
另一种常见问题是:某些页面配了权限,但用户登录后第一次刷新却能访问,第二次就进不去了。这通常是路由动态注册的时序问题,即页面路径已经注册了,权限过滤逻辑却没有重新执行。排查思路很简单,在路由守卫里打几行日志,把登录状态、权限点集合、判定结果依次打出来,一眼就能看到是哪个环节断掉。
6.3 构建内存溢出,别一上来就调 Node 内存
第一次遇到编译内存溢出,很多人第一反应是给构建进程加内存参数。确实,这是最快能让构建恢复的办法,但根本原因往往不是内存不够,而是分包策略出了问题。某个第三方库把所有模块都打进了一个大包,解析时内存就被撑爆了。正确的处理方式是先跑一次依赖分析,找到那个"罪魁祸首"包,主动把它拆开或者改成按需加载。
如果确实遇到大型项目代码量很大,构建内存需求本来就高,那再去调大构建进程内存也不迟。REA 模板里默认配置了一个相对合理的上限,并且在文档中说明:先分析包、再决定是否加内存。顺序反过来,你只是在掩盖问题。
6.4 环境变量消失,先看前缀和加载时机
环境变量没生效是高频问题。第一种是变量名前缀不对,构建工具默认只暴露带特定前缀的变量,其他一律过滤掉,业务代码里读出来就是 undefined。第二种是加载时机问题,有人在页面运行时读取了一个构建期才注入的变量,结果读到的永远是空。第三种是换环境之后没有重新构建,本地起 dev 服务读的是内存里旧的配置,改动 .env 文件后必须重启才能生效。
这类问题排查起来最快的手段,是在业务代码里把整个环境变量对象打印出来,对照工程文档看哪些变量存在、哪些丢了。REA 模板在启动时自动打印变量探针信息,目的就是让这类问题在第一时间暴露,而不是让接口请求到十万八千里之外才发现配置不对。
6.5 踩过几次坑之后,我的真实感受
模板这种工程化项目,短期内看不出来有多大价值,但坚持用下来,最大的受益不是省了写重复代码的时间,而是团队沟通成本降下来了。以前讨论一个页面交互,大家要先问清楚你是用什么方式写的;现在打开目录、看一眼组件、查一下请求封装,直接进入正题。REA 对我来说最大的收获是把一些隐性经验变成了显性约定,至少新人来了,不再需要靠"问前辈"才能干活。
最后分享一个小习惯:每隔一段时间,我会把最近踩的坑和临时解决方案整理进模板的更新日志。这些东西如果只写在某个项目的注释里,下一次换项目时就丢光了。写进模板之后,整个团队都会受益,这个习惯是我觉得整个项目最有意义的部分。