☰
从零搭建rea前端工程化基线:Vue3+TypeScript中后台实践
2026/10/21 13:57:15 网站建设 项目流程

我第一次拿到 rea 这个代号的时候,项目仓库里只有一行注释:rea = Rapid Enterprise Application。说实话,团队里每个人对这串字母都有自己的理解,有人说是 React Easy App,有人说是 Really Extensible Architecture,还有人开玩笑说这就是“热啊”的拼音。后来讨论逐渐收敛,我们把它定位成一个面向中后台业务场景的前端工程化基线:用最少的配置让团队在 5 分钟内启动一个新项目,同时把路由、状态管理、请求层、代码规范这些最容易打架的部分一次性定好。这篇文章会把我自己完整的搭建过程、选型理由和踩坑记录都摊开,给想维护自有脚手架,或者准备从零开始搭中后台模板的朋友做一个参考。如果你刚接触前端工程化,建议先看第二部分的设计取舍,不要直接抄配置;如果你负责团队基础设施,可以直接跳到第三部分的实操链路。后面所有代码都以 Vue 3 + TypeScript 为基础,这也是大多数中后台项目比较顺手的组合。

1. 项目概述:rea 到底解决什么问题

1.1 名字由来与定位

先说名字。“rea”原本是仓库里的临时代号,Rapid Enterprise Application 是我们后来补上的全称。这个全称比代码本身更重要,因为它把目标说清楚了:Rapid 代表快,Enterprise 代表企业级约束,Application 代表它最终要支撑真实业务交付,而不是一个炫技 Demo。我们想要的不是一个组件库,也不是一个低代码平台,而是一套“新项目启动时拿来就能跑”的基线模板。明确了这层定位之后,很多设计冲突就变得好解决了:凡是能帮新项目更快交付的配置就保留,凡是需要大量定制还可能拖慢启动的模块就砍掉。

从这种定位出发,rea 的核心职责被拆成三件事:统一技术栈、统一目录约定、统一工程化配置。统一技术栈意味着新项目不需要再投票选框架,减少重复讨论;统一目录约定让路由、状态、API 这几种文件放在固定位置,任何人接手都能快速找到入口;统一工程化配置把 ESLint、Prettier、Husky、构建部署这些基础设施固化下来,避免十个人产生十种配置风格。这三件事单独看都不难,难的是把它们做到“新项目能直接继承”的程度。如果只是在 README 里写一段“建议这样做”,那没有任何约束力,必须把规则落到模板文件和自动化检查里。

早期团队里也有人质疑,Vue 项目不是有现成脚手架吗,为什么还要造一个模板?这个问题很合理。现成模板解决的是“项目能不能跑”的问题,rea 想解决的是“多人一起来写,代码还能不能一样好维护”的问题。这两个问题之间隔着一层很厚的自定义配置,而这一层恰好是很多团队的真实痛点。自己搭模板要耗费时间,但长期看是在给整个团队节省重复沟通和修复烂摊子的成本。

1.2 解决的核心痛点:手工复制项目带来的失控

在没有 rea 之前,团队里启动一个新中后台项目的标准做法,是找最近的一个老项目把目录复制一份,然后删掉业务代码。这个方法看着效率很高,实际上有个很隐蔽的问题:老项目里那些“临时补丁”会被当成公共配置一路流传下去。比如某个项目为了赶上线改过 ESLint 规则,下个项目继承以后,新代码也继承了这种放任状态,规范就慢慢失效了。还有的项目在本地环境加过特殊代理,复制到新项目后别人根本不知道那段配置是干什么的,出了问题也不敢删,越积越多。

复制式启动还会带来依赖漂移。A 项目用 Vue 3.2,B 项目改成 Vue 3.4,C 项目再把 Element Plus 从 1.x 升到 2.x,每个项目升级状态都不一样。等需要统一解决问题时,维护成本已经变成项目数量的线性叠加。手工搭建的时间成本也很可观,装依赖、配路由、配 ESLint、配代理,一套下来熟练的人也要半小时到一小时,还不一定记得补齐所有细节。如果中途换人,交接成本又得再算一遍。这些重复劳动加在一起,已经远超一个模板项目的开发成本。

对比项手工复制/搭建使用 rea
新项目可运行时间约 0.5~3 小时5~10 分钟
代码规范一致性依赖复制来源,逐渐漂移统一固定
请求层/路由/状态管理各自实现,风格不同统一封装
部署配置每个项目重复编写模板内置
长期维护成本随着项目数量线性上升集中收敛到模板

并不是说有了 rea 就一劳永逸。模板本身也需要持续维护,但至少把“从零到跑起来”这个过程压缩掉,把精力留给真正有业务价值的部分。一个基础设施类的工具,看的不只是第一次启动快不快,更要看它在两三年后还能不能继续被团队心甘情愿地使用。rea 的规模控制、依赖收敛和目录约定,都是奔着这个长期目标去的。

1.3 适合谁用,不适合谁用

我先说一个诚实的边界:如果你只是想找一个现成的后台管理界面模板,rea 不是最优选择,市面上有功能更完整的成品方案可以直接下载使用。rea 更像一个“半成品工程基线”,它提供的是架子,不是页面功能。它的核心价值体现在复用、维护和演进上。如果你想从里面找现成的用户管理、权限菜单、数据报表,那会失望。但如果你需要的是一套稳定、清爽、能长期维护的工程起点,它非常合适。

如果你是初级前端,可以把 rea 当成一个学习工程化配置的活教材,看看路由、状态管理、请求封装是怎么组织在一起的;如果你是中高级前端,可以用它作为新项目起点,在上面快速叠加业务;如果你是团队里负责基础设施的人,rea 的价值最大,你可以直接借鉴它的目录划分和流水线设计,把它改成自己的内部模板。我这篇文章里复盘的所有决策,都可以对照自己的场景调整,不需要照搬。

另一个判断标准是团队规模。三五个人写一个短期活动页,不需要这种东西;几十人长期维护多个中后台应用,工程化模板的价值会非常明显。规模越小,引入规范的成本反而越容易被感知;规模越大,不规范带来的成本才会暴露。所以如果团队还处于小团队阶段,可以先尝试局部引入,比如只统一请求层和目录,不必一步到位。工程化是手段不是目的,只有当它节省的成本大于引入的负担时,才值得投入。

2. 整体设计思路与方案选型:不堆功能,做减法

2.1 为什么不用现成的中后台模板

业内已经有不少开源中后台模板,功能非常全,权限、用户管理、图表、编辑器几乎都有。那为什么还要自己搭?原因有两点。第一,这类模板本质上是“一个完整应用”,它带着强烈的作者业务偏好。你用它的时候不是把业务往里塞,而是先把它的业务逻辑剥出来,这个剥离过程通常会比从零搭建更痛苦。你删掉别人写的用户管理、删掉别人写的消息中心,还要担心删漏了某个依赖导致编译失败,最后代码里留着大量用不到的东西。

第二,这类模板一般会深度绑定一套自建封装,哪天作者升级了核心依赖,你要么跟着大改,要么 fork 后自己维护,相当于背上了一个没有文档的存量系统。真正可长期使用的脚手架,应该像一块能够自由拼装的地基,而不是一座装满家具的样板房。相比之下,我们做 rea 时给自己定了一条原则:不堆功能,只封装那些会让项目失控的部分。业务相关的东西全部不放进去。

比如我们不会在 rea 里内置用户管理页面、角色配置页面,这些跟具体业务强相关,模板一旦带上就不可避免被复制改造。相反,路由守卫、请求拦截器、环境变量约定、目录规范这些是跨业务稳定的,才值得沉淀进模板。这个“稳定性优先”的判断标准,后来帮我们挡掉了很多无意义的追加需求。有人提过“把 Excel 导入组件也加到模板里吧”,被我们拒了,因为那是业务层的东西,放进模板只会让下一次启动变得更慢。

2.2 技术栈选型:每个选择背后的取舍

技术栈最终结果:Vite 作为构建工具,Vue 3 作为框架,TypeScript 作为默认语言,Pinia 管状态,Vue Router 4 管路由,Axios 管请求,Element Plus 提供基础组件,ESLint、Prettier、Husky 管代码质量和提交规范。单个技术选型都有替代方案,我更想讲的是保留核心的原因,因为只有理解了这些原因,后续遇到替换需求时才知道该动哪里。

构建工具这块,我们最早纠结要不要继续用 Webpack。存量项目都是 Webpack 经验,换 Vite 意味着全团队要适应新东西。但实测开发环境热更新差距太明显,中后台页面又非常多,改一行代码等编译这件事完全没必要。Vite 基于 ESM 的开发模式让冷启动从几十秒变成秒级,这个体感差异非常强烈,迁移成本完全值得。类型系统选 TypeScript 没有太多争议,企业级项目的长期演进和重构,离开类型系统风险会高一个量级,哪怕新人刚开始写起来慢一点,也要坚持。

状态管理选了 Pinia 而不是 Vuex。中后台业务的状态其实不算特别复杂,Pinia 直接砍掉了 Vuex 里大量样板代码,Mutation 和 Action 的区分在实际协作中很难约束住,反而容易让新人困惑。Pinia 的写法更接近普通对象操作,配合 devtools 调试也直观。组件库选 Element Plus 主要原因是中后台常见的表单、表格、弹窗组件成熟度高,团队里也已经有使用经验,换来换去不值得。这些选型单独看都很常规,合在一起才构成一个统一、可预期的技术栈。

2.3 目录结构与模块划分:先想清楚每一层放什么

rea 的目录结构经过几次调整,最后稳定成下面这个样子:

rea/ ├── src/ │ ├── api/ # 与后端接口一一对应的请求函数 │ ├── assets/ # 静态资源,如图片、全局样式 │ ├── components/ # 全局通用组件 │ ├── layout/ # 主框架布局 │ ├── router/ # 路由表与守卫 │ ├── stores/ # 状态仓库 │ ├── styles/ # 全局样式变量和基础样式 │ ├── types/ # 全局类型声明 │ ├── utils/ # 工具函数 │ ├── views/ # 页面级组件 │ ├── App.vue │ └── main.ts ├── .env.development ├── .env.production ├── .eslintrc.cjs ├── .prettierrc.cjs ├── index.html ├── package.json ├── tsconfig.json └── vite.config.ts

这个结构最核心的约定是分层依赖方向。views 层只允许调用 api 和 stores,不允许直接发请求;api 层负责与后端接口对齐,不要在业务组件里散落请求逻辑;stores 只放跨页面共享的数据,页面内部的临时状态就留在组件里。这个约定写起来很容易,真正维护时最难的是阻止“图省事”的代码绕过分层。目录结构本身不会保证代码质量,但它是代码质量讨论的基础。当代码放错位置时,我们至少能有一个公认的“正确位置”可以参照。

另外,我们特意留了 types 目录而不是把所有类型散在各组件里。中后台项目很多数据结构是后端返回的实体类型,这些类型会在多个模块复用,统一放 types 能避免重复定义和“看起来像但不一样”的两个接口类型。比如用户信息类型,A 页面定义一个,B 页面再定义一个,等后端加字段时就要同步改两处,漏掉一处就会出现类型不一致。这个设计在项目变复杂以后价值很大,虽然初期看起来只是多了一个目录。

3. 核心功能实操:从零拉起一个 rea 项目

3.1 初始化与基础配置:让工程在 5 分钟内跑起来

实操开始。假设你的机器上已经装好了 Node.js 和 pnpm,版本建议参考官方要求。创建项目我用的是 Vite 的 Vue + TypeScript 模板:

pnpm create vite rea --template vue-ts cd rea pnpm install pnpm add pinia vue-router axios element-plus

选择 pnpm 而不是 npm,主要原因是磁盘占用和安装速度。团队多个项目使用同一套依赖,如果用 npm,每个 node_modules 都是独立副本,pnpm 通过内容寻址的方式共享依赖,安装速度和占用量都会好很多。实际维护时,pnpm 的 lockfile 粒度更细,CI 环境拉取依赖也更稳定。如果你对 pnpm 不熟,最直接的体感就是“装依赖好像变快了”,而且不会再出现一个项目占好几 GB 磁盘的情况。

依赖装完后,第一件事是修改 vite.config.ts,加上路径别名和开发代理:

import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, server: { port: 5173, proxy: { '/api': { target: 'http://127.0.0.1:3000', changeOrigin: true } } } })

路径别名的作用是避免出现../../../这种相对路径,开发代理的作用是解决本地开发跨域问题。后端的网关地址通过环境变量读取会更灵活,开发环境默认指到本地 Mock 服务即可。这个阶段最容易踩的坑是只配了 vite.config.ts,没有同步 tsconfig 里的 paths,IDE 会一直提示找不到模块。后面第 4 节会专门说,这里先记住“两边要同时改”。

3.2 路由和状态:把权限控制收敛到守卫里

中后台应用几乎躲不开权限控制。rea 里的做法是把路由表分成两层:基础路由和业务路由。基础路由包括登录页、404 页等;业务路由在用户登录后,根据后端返回的菜单权限动态添加。这里给的示例是简化版,但整体思路可以直接迁移。先看基础部分:

import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', name: 'Login', component: () => import('@/views/login/index.vue') }, { path: '/', component: () => import('@/layout/index.vue'), redirect: '/dashboard', children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index.vue'), meta: { title: '工作台', icon: 'dashboard' } } ] } ] const router = createRouter({ history: createWebHistory(), routes }) // 全局前置守卫 router.beforeEach((to, from, next) => { const userStore = useUserStore() if (to.path === '/login') return next() if (!userStore.token) return next({ path: '/login' }) next() }) export default router

路由设计中有一点值得注意:页面组件全部使用动态 import,这样打包时会自动拆成独立 chunk,而不是揉进一个巨大的首屏 bundle。实际点击菜单时才会加载对应页面,这对中后台存在大量低频页面的场景非常友好。权限控制放在守卫里而不是散落各个页面,是因为“未登录不能访问”是一个全局规则,集中处理只需要写一次,后面新增页面时天然就带上保护。

状态管理这边,userStore 是几乎所有中后台都会用到的示例,存放 token 和用户信息,并提供 login/logout 动作。用 Pinia 写起来非常直白:

import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: localStorage.getItem('token') || '', userInfo: null }), actions: { setToken(token: string) { this.token = token localStorage.setItem('token', token) }, async fetchUserInfo() { const data = await fetch('/api/user/info') this.userInfo = data } } })

真实项目里 fetch 会换成统一的请求层函数。把 token 存在 localStorage 是起步阶段最简单的方案,如需更严格的安全策略,可以换成内存存储加刷新 token 机制。但无论选哪种,路由守卫、请求拦截器、状态仓库都要对“未登录”有统一反应,这也是我们把 userStore 放在 rea 模板里而不是业务代码里的原因。模板提供一个标准实现,业务再按需调整,总比每个项目从零设计好。

3.3 请求层封装:把错误处理统一收口

请求层是每个中后台项目的命脉,也是 rea 花工夫最多的部分。封装原则是:业务代码不要直接使用 Axios 实例散弹式请求,而是通过一个统一导出的 request 函数发起请求;所有统一逻辑都放在拦截器里,业务层只关心数据成功后的结果。下面这段代码会把 token 注入、业务码判断、错误提示都集中处理:

import axios from 'axios' import { ElMessage } from 'element-plus' const service = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || '/api', timeout: 15000 }) // 请求拦截器 service.interceptors.request.use((config) => { const { token } = useUserStore() if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) // 响应拦截器 service.interceptors.response.use( (response) => { const res = response.data if (res.code !== 0) { ElMessage.error(res.message || '请求异常') return Promise.reject(new Error(res.message)) } return res.data }, (error) => { if (error.response?.status === 401) { ElMessage.error('登录已过期,请重新登录') // 跳转登录页 } else { ElMessage.error(error.message || '网络异常') } return Promise.reject(error) } ) export default service

这段代码把三件事统一了。第一,token 注入集中在请求拦截器,业务代码不需要到处手动带 Authorization。第二,后端约定的业务码通过响应拦截器统一判断,成功时直接返回 data,失败时弹出统一提示。第三,401 这类认证错误被集中识别为登录过期,整个应用只有这一处处理逻辑,不会出现不同页面各自弹窗的问题。如果每个页面都自己判断错误码,几十个页面就会写出几十种不一致的提示方式。

有人会问,业务码判断放拦截器里,那某些接口想自己处理错误怎么办?我的处理方法是在响应拦截器里保留一个约定:return Promise.reject(error),业务层继续 catch 就行。模板内置的只是默认行为,不对的地方业务可以覆盖。原则是“默认值合理,特殊情况可以绕开”,而不是把所有灵活性都做进去,后者的维护成本太高。请求层封装最怕的是把简单问题复杂化,能用拦截器解决的就不要额外引入库。

3.4 提交规范与流水线:让机器接管人工检查

工程化模板如果不包含质量检查和构建流水线,那它顶多算个目录规范。rea 里的质量闸门有四道:ESLint 检查代码质量,Prettier 统一格式,Husky 配合 lint-staged 在提交前检查,最后在合并请求时跑全量检查和构建。下面这个命令安装并初始化 Husky:

pnpm add -D husky lint-staged pnpm exec husky init

接着在 package.json 中配置 lint-staged,让每次提交只检查本次改动的文件,避免全量检查带来的性能和噪音问题:

{ "lint-staged": { "*.{vue,ts,tsx}": ["eslint --fix", "prettier --write"] } }

这样配置之后,提交代码时如果某个文件有语法问题,会被直接卡住。因为只检查暂存文件,团队里已有的历史代码不受影响,逐步规范化而不是一次性推翻重来,实施阻力会小很多。如果你的团队还在为“要不要强制 lint”争论,建议先从只检查新增代码开始,等大家习惯了再收紧规则。

合并请求时的流水线,以通用的 CI 平台为例。流水线只需要三个核心阶段:安装依赖、质量检查、构建产物。依赖安装用锁文件加只读模式,保证每次环境一致;质量检查跑 lint 和类型检查;构建产物上传到部署服务器,静态站打压缩包,带 Node 服务的情况就构建镜像。以下是一个精简后的流水线配置:

stages: - install - lint - build install: stage: install script: - pnpm install --frozen-lockfile lint: stage: lint script: - pnpm lint - pnpm type-check build: stage: build script: - pnpm build artifacts: paths: - dist/

这套流水线的核心价值在于:人工检查会被遗忘,机器检查不会。只要流水线配过一次,后面的每次合并都会被统一规则保护。很多人觉得这跟 rea 这个模板没太大关系,但正是这些“看不见的底层设施”,才让模板不仅仅是一个代码仓库,而是一套可运转的研发流程。基础设施项目最难的从来不是写代码,而是让所有人愿意按同一个流程走。

4. 常见问题与排查技巧实录

4.1 配置不生效:三个最容易想到的方向

我在维护 rea 的过程中,几乎每天都会遇到配置不生效的案例。最典型的是环境变量。Vite 只向客户端代码暴露以VITE_开头的变量,如果你在.env里写了一个API_BASE_URL,然后在代码里直接读,一定拿到 undefined。解决方案是命名改成VITE_API_BASE_URL。这个规则在文档里写得很清楚,但实际团队里还是反复出现,所以我直接在模板的.env.example里加了两行注释,让新人复制环境变量文件时一眼就能看到。

第二个典型问题是路径别名在 IDE 里报红但编译能过。原因是vite.config.ts只是构建工具,TypeScript 语言服务认的是tsconfig.json。所以@别名必须在两处配置:一处是resolve.alias,另一处是compilerOptions.paths。漏掉任何一边都会出现“编译正常但编辑器疯狂报错”的割裂体验。这个坑最坑的地方在于,它不影响运行,只影响开发体验,所以很容易被忽视,直到代码里的相对路径越写越长才被想起。

第三个典型问题是代理不生效。很多情况下后端接口返回 404,不是跨域错误,而是代理把/api原样转发给了后端地址,但后端没有/api前缀。应对方式是修改 proxy 配置,用rewrite去掉/api:

proxy: { '/api': { target: 'http://127.0.0.1:3000', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } }

这类问题最麻烦的点不是解决方案难,而是问题表象都一样:页面请求报错。有了这几个排查方向,大部分情况都能在几分钟内定位。我的经验是先看浏览器网络面板,确认请求发到了哪个地址、返回了什么状态码,再回到配置里核对。盲目改配置只会把问题搞得更乱,先定位再动手永远比乱试高效。

4.2 构建性能:几个低成本的优化手段

中后台项目页面多,依赖大,构建慢是常见问题。rea 里用了几个低成本方案,按收益从高到低列一下。第一个是路由懒加载,所有业务页面都用动态 import,这是收益最明显、改动最小的手段。很多项目首屏加载慢,就是因为把所有页面都打包进一个 chunk,用户访问登录页也要下载几十个页面的代码。拆开后,首屏只需要加载当前页面和公共依赖。

第二个是依赖预构建。Vite 启动时会把 node_modules 里需要转译的依赖提前打包,如果觉得预构建慢,可以通过optimizeDeps.include把常用库提前声明进去。这个操作对开发环境体验提升明显,尤其是项目依赖了很多小的纯 ES 模块时,第一次启动速度会快很多。

第三个是按需引入组件。Element Plus 配合自动化插件可以直接解决整包引入过大的问题,但这类方案在模板里落地时要额外注意样式文件有没有同步按需加载,否则会出现“功能正常,样式丢失”的经典错误。这个错误排查起来非常费时,因为我习惯在模板里直接留一个按需加载的配置模板,方便后续项目直接启用。第四个是产物压缩,构建后用 gzip 或 brotli 压缩静态资源,成本很低,收益却很直观。一个插件配置片段:

import vitePluginCompression from 'vite-plugin-compression' plugins: [ vitePluginCompression({ algorithm: 'gzip', threshold: 1024, deleteOriginalFile: false }) ]

在我们自己一个实际业务包里,单靠路由懒加载加按需组件,产物 gzip 体积从接近 1MB 降到 300 多 KB,首屏加载体感明显变快。性能优化有很多高级玩法,但对中后台项目来说,先把这几件基本功做到位,收益已经足够。不要一上来就上微前端、模块联邦这些重型方案,先看 bundle 里到底装了什么,往往砍掉几个大依赖比折腾架构有效得多。

4.3 兼容性与环境差异:六个容易漏的坑

下面这个表格整理我在交付过程中遇到的兼容性坑,标记一下方便后续排查:

现象原因处理方式
本地正常,CI 环境构建失败依赖版本不一致CI 使用 lockfile + frozen-lockfile
老的 iOS 浏览器打不开目标浏览器不支持 ESM调整 build.target 或引入兼容插件
环境变量生产环境生效但开发环境没有变量没加 VITE_ 前缀统一命名规范
组件样式时有时无按需导入配了 JS 漏了样式变量检查插件 style 导入配置
代理在部分机器生效部分失效本机 hosts 或防火墙拦截切换到 mock 服务排查
提交时 lint 报了一堆历史错误lint-staged 覆盖范围不对只检查暂存文件

这些坑并不深奥,但因为分散在配置文件的各个角落,排查成本往往比解决成本高。rea 的价值之一就是把这些坑提前踩平,后来者不需要再走一遍同样的弯路。维护模板时我也经常提醒自己:遇到一次问题,就要把修复方案沉淀回模板里,否则同一个问题会在每个新项目上重复出现,那模板就失去了意义。记录是最好的知识管理,不管是写注释还是写文档,都比脑内记忆可靠。

5. 这套方案用下来,体感和扩展空间

5.1 最大变化:新人上手和跨项目维护都变轻松了

rea 从落地到现在,最直观的变化体现在两个场景。第一个是新同学入职。以前接到一个新项目,第一周往往在判断“这个项目的请求层为什么长这样”“路由注册到底在哪”,现在直接用 rea 起步,目录结构跟培训材料完全一致,代码风格也天然统一,只需要关注业务本身。新人第一周就能开始提代码合并请求,这个速度在过去是不敢想象的。对于团队来说,少一周的熟悉期,相当于每个新人都节省了实实在在的研发成本。

第二个场景是跨项目维护。之前几个中后台各写各的,现在大家只要知道“这是用 rea 起的项目”,就可以预判代码位置和套路,定位问题的速度提升非常明显。以前为了找一个接口的请求函数,可能要全局搜几层封装,现在直接去src/api下面按文件名找,几乎没有认知负担。模板带来的这种可预测性是特别有价值的,它让团队的知识可以跨项目复用,而不只是写在某个人的脑子里。

当然,模板一开始也遭遇过抵触。有老同事觉得“多加一层抽象不如直接写代码痛快”,这种声音在项目进入中期维护后自然变少了,因为模板约束的坏味道正在帮他们省时间。我个人的体会是:工程化模板的前期投入不一定能立刻看到回报,但把时间拉长到两三个项目周期,回报是非常可观的。如果只看第一个项目,你甚至会觉得自己多花了半天时间搭架子;但看到第三、第四个项目的启动和维护成本时,这笔账就完全不一样了。

5.2 后续可以继续演进的方向

rea 目前还停留在“一套模板”的阶段,我个人觉得演进方向有三个。第一是拆成 npm 包,把配置部分变成可安装的依赖,新项目只需要保留业务代码,模板升级时通过依赖版本控制,而不是每次手动同步文件。这个方向能解决模板带来最大的痛点:“模板更新了,老项目怎么同步?”只要在 package.json 里升一下版本,再跑一次迁移命令,就能把新规范拉下来,可比手动 diff 目录强多了。

第二是接入 Monorepo,把多个中后台项目放在同一个仓库里,共享配置和工具库,统一依赖升级节奏。这样跨项目的公共代码改动不用发版就能联动,但也需要配套的工具链和团队纪律,否则容易出现一个仓库里包依赖关系混乱的情况。第三是沉淀内部业务组件库,当多个项目反复出现相同的业务模块,比如组织选择器、批量导入弹窗,可以考虑抽取成包。这一步在前两步之后做更稳妥,因为先在模板层面统一了工程结构,抽取组件才有合适的土壤。

这些方向都不复杂,但每一步都需要额外的维护投入。我的建议是不要急着做,等项目数量积累到足够出现痛点再动,过早抽象反而会增加使用成本。基础设施这种东西,最怕的就是做过头。每加一个依赖、每多一层封装,都在给使用者增加理解成本。rea 能顺利跑起来,不是因为技术选型多新,而是因为中间砍过很多看起来很有用的功能。做脚手架的人,克制比炫技更重要。这个道理,也是我维护 rea 这么久最深的一个体会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询