资讯动态

rea:一套由前端工程化实践驱动的Vue3项目模板

发布时间:2026/10/21 4:44:24 来源:尧图企业网站定制
最近我遇到一个挺有意思的情况前阵子给一个新来的同事移交项目他接手的是一个从零启动的 H5 活动页。我本以为他看完代码后能直接上手结果他第一反应是问我“这个项目是怎么初始化出来的为什么目录是这么分的请求层为什么这样封装”我才意识到平时自己一套顺手搭好的模板其实包含了不少隐含的工程决策。于是我把这套东西重新整理了一遍取了个代号叫 reaRapid Experience App快速体验应用模板专门用来解决“从零起一个可维护的前端项目”这件事目前已经在内部多个小项目里跑过几轮整体稳定。这篇文章就围绕 rea 这套模板展开从设计思路、工程结构、实操配置再到坑点排查完整过一遍。适合正在搭自己脚手架的人也适合刚接手别人项目、想弄明白目录和配置为什么长这样的人。内容偏前端工程化但我尽量说人话把背后的取舍讲清楚而不是单纯丢一堆配置让读者自己悟。1. 项目由来与核心定位1.1 为什么我会做一个叫 rea 的模板先说背景。做前端时间长了会发现一个很现实的问题大部分项目的起点长得很像——初始化一个构建工具、配一下 TypeScript、接一下路由、封装一个请求层、加几个工具函数、再搞定代码规范。你说这些事情难吗不难。但每开一个新项目都来一遍真的烦而且容易漏细节。有一段时间我发现团队里几个人各自从不同旧项目里拷初始代码拷出来的东西五花八门有人用的是 Vue2 Webpack有人用的 Vite Vue3还有人直接用纯 HTML 写活动页。每个人的请求封装风格都不一样状态管理有的用 Pinia有的直接手写全局变量。这种混乱对协作来说是个隐患所以我决定整理出一套统一的起点代码。rea 这个名字本身没有太复杂的含义就是 Rapid Experience App 的缩写。它解决的核心问题用一个词概括就是“快速起项目”。我希望能做到拿到仓库地址克隆下来装上依赖一条命令跑起来五分钟内看到页面能渲染、能发请求、能跳路由而且代码结构和命名习惯是团队里大家都认可的。回头看这套模板的价值不只是省时间更重要是降低了新人的理解成本。以前接手一个旧项目光看懂目录结构就要花一天用 rea 之后大家用的都是同一套骨架沟通成本明显降下来了。1.2 rea 解决的核心痛点是什么痛点其实可以拆成三个层面。第一层是“重复劳动”。每开一个新项目都要重新处理构建工具配置、ESLint 规则、路径别名、请求拦截器、路由懒加载这些琐碎的事情。这些工作本身没什么技术含量但一旦漏了后面补起来很痛苦。比如忘了配路径别名代码里全是 ../../… 让人头大的相对路径又比如请求层没做统一超时处理接口一慢页面就转圈圈。第二层是“规范不统一”。多人协作的场景下有没有统一的格式化规则、提交信息规范、目录约定直接影响代码的长期可维护性。我用 rea 内置了一套约定比如组件文件名用驼峰还是中划线、页面文件放哪个目录、样式怎么组织、状态管理什么时候该用什么时候不该用这些都写清楚团队按同一个标准来Review 代码的时候不用再为风格争论。第三层是“扩展路径不明”。很多新手初始化完项目不知道往哪个方向加东西。比如要做权限路由应该改哪里要接国际化能不能低成本插进去rea 在结构设计上预留了这些插槽路由配置、导航守卫、全局布局都是独立目录改起来不需要动到主入口文件。这三个痛点解决之后项目就不会因为初始化时图省事而埋下长期隐患。所以我常说脚手架写得好不好不体现在启动速度上而是体现在项目写到一半时加功能是否顺手、换人接盘是否顺畅。1.3 技术选型为什么不选其他方案技术选型这块每个人都有自己的偏好但 rea 的选择是基于几个现实条件做的。构建工具我选的是 Vite。原因很直接它启动快、热更新快、配置简单。团队里大部分项目都不是那种需要深度定制构建流程的超大型应用Vite 的开发体验优势非常明显。当然 Webpack 也不是不行但它的配置复杂度和启动速度对于追求“快速体验”的定位来说不友好。选 Vite 的另一个好处是它内置了对 TS、JSX、CSS Modules 等常见能力的支持少装很多插件。框架层面我选了 Vue 3组合式 API。如果你更习惯 Reactrea 的思路也可以迁过去核心逻辑是一样的只是语法层面的区别。为什么选 Vue 3因为我们内部历史项目多数是 Vue 技术栈迁移路径平滑。组合式 API 对逻辑复用很友好把一个功能相关的状态、计算属性、方法写在一起读代码的时候思路更连贯。状态管理用的 Pinia。相比 VuexPinia 的 API 更简洁对 TypeScript 的支持也更好。我平时对这个库的评价是简单到不太需要看文档定义一个 store 就跟定义一个大对象一样直接。路由和请求层分别用的 Vue Router 4 和 Axios这两个基本算是社区里的标准答案了稳定、生态成熟、踩坑资料也多不会有选型风险。这些选择不一定是最新最潮的但都是我实际跑过项目之后觉得最顺手的组合。工具的最终目的只有一个让写业务代码的人不用被基建问题分心。2. 工程结构设计与开发规范2.1 目录结构一个能“顺着找文件”的布局目录结构是 rea 里最值得讲的一部分。很多人初始化项目的时候目录随便分业务代码堆在 views 里组件乱放在 components 下公共样式堆一个文件里写到最后连自己都找不到东西。rea 的目录布局长这样src/ ├── api/ # 接口定义按业务模块拆文件 ├── assets/ # 静态资源图片、全局样式 ├── components/ # 公共组件 ├── composables/ # 组合式函数逻辑复用 ├── directives/ # 自定义指令 ├── layouts/ # 布局组件 ├── router/ # 路由配置 ├── stores/ # 状态管理 ├── styles/ # 全局样式变量与主题配置 ├── utils/ # 工具函数 └── views/ # 页面组件按路由模块分文件夹这个结构有两个核心设计原则。第一个原则是“按业务模块切分而不是按文件类型堆叠”。api 目录里不是放一个巨大的 index.js而是按业务域拆成 user.js、order.js、goods.js 这类文件一个接口对应一个函数函数命名直接表达语义比如 getUserList、createOrder。views 目录也是按模块分文件夹比如 views/user/UserList.vue、views/order/OrderDetail.vue页面、子组件、页面级样式放在同一个模块文件夹下方便整体搬迁。第二个原则是“跨页面复用的东西才往上提”。components 目录只放至少两个页面会用到的组件如果一个组件只属于某一个页面那就直接放到该页面的文件夹下面而不是全局共享。这样全局 components 目录不会越来越臃肿组件之间的依赖关系也变得清晰。layout 文件夹单独拎出来是因为很多应用会有不同风格的页面布局比如有的页面有侧边栏有的页面是纯独立活动页。布局抽出来之后路由配置里按需套用就行不用在每个页面里重复去写顶部栏和侧边栏。另外还有一个容易被忽略的点src 目录下的所有文件默认按功能划分但跨目录引用路径要尽量简洁。我习惯在配置里加好别名比如 指向 src 目录。这样你在组件里写 import { getUserList } from /api/user而不是写一堆 ../../。路径别名不是偷懒是为了让引用关系变得一目了然。2.2 代码规范与提交规范代码规范在多人项目里尤其重要。rea 内置了一套 ESLint Prettier 的标准配置规则不算严苛但能保证基本一致性。核心规则包括单引号优先行尾不加分号缩进两格。组件命名使用 PascalCase文件名使用中划线小写。模板中使用 kebab-case 引用组件标签名用连字符。变量命名使用 camelCase常量使用 UPPER_SNAKE_CASE。禁止使用 var统一使用 const / let。这些规则看起来简单但没法靠自觉必须靠工具约束。Commit 信息方面rea 内置了简单的提交规范校验格式为type(scope): subject比如fix(user): 修复用户列表分页错误。type 限定为 feat、fix、docs、style、refactor、perf、test、chore 这几种避免提交信息变成“update”“修改”这类没营养的描述。可能有人觉得规范太多会束缚写代码的手感但实际跑过两个项目之后我的体感是规范带来的收益远大于束缚。当你习惯了“组件文件放哪、API 函数怎么命名、Store 什么时候该抽离”这套约定之后其实是把一部分决策成本前置了写代码的时候反而更省脑力。还有一个细节是ESLint 配了vue/multi-word-component-names规则避免所有组件都叫 index.vue。因为一旦每个目录下都是 index.vue编辑器标签页上会显示一堆同名的文件根本分不清哪个是哪个。2.3 内置工具函数与请求层设计rea 内置了几个高频使用的工具函数统一放在 utils 目录里。包括日期格式化、URL 参数解析、防抖节流、深拷贝、手机号脱敏这类常见的杂货函数。这些函数不追求大而全而是够用、稳定、有单测覆盖。它们的价值在于团队里遇到同一个需求不需要每个人都重新写一遍也不会出现三个人写出来的格式化结果不一样。请求层是 rea 里相对重的一部分。基于 Axios 封装了一个默认实例统一处理了几件事请求超时设置默认 10 秒。BaseURL 从环境变量读取区分开发、测试、生产环境。请求拦截器统一加上 token响应拦截器统一处理业务错误码。网络错误、超时、业务异常分别给用户不同的提示。响应拦截器这里有一点经验想分享后端的返回结构通常是{ code, message, data }拦截器里不要只判断 HTTP 状态码还要判断业务状态码。如果接口返回的 code 不是 0说明业务层面出了错这时候应该把 message 提示给用户并且 reject 这个 Promise让调用方可以走 catch 分支做后续处理。这套逻辑在 rea 里是天然内置的写业务的人不需要关心。登录失效的处理也内置了后端返回 401 的时候统一清空本地 token 并跳转到登录页。这个逻辑放在拦截器里比在每个页面里单独判断要干净得多也避免了一不小心遗漏某个接口没处理导致体验不一致。3. 实操从零跑通一个 rea 项目3.1 初始化项目与依赖安装前面讲了设计这部分直接动手。先说怎么用 rea 起一个项目。假设你已经拿到 rea 的仓库地址第一步是克隆git clone 你的仓库地址 my-project cd my-project克隆之后第一步是删掉旧的项目元信息把自己的 Git 仓库初始化起来rm -rf .git git init然后安装依赖npm installrea 默认使用 npm 作为包管理器。理论上 pnpm 和 yarn 也能兼容但我建议一个项目从小到大都用同一个包管理器避免锁文件混乱导致的依赖版本漂移问题。如果团队已经统一用 pnpm那也可以用 pnpm install然后把 lock 文件提交进仓库。装完依赖之后先看几个关键的配置文件是否正常npm run dev如果一切正常终端会出现本地访问地址浏览器打开就能看到 rea 自带的欢迎页。这个页面里放了一些简单的示例代码比如一个接口请求的 demo、一个路由跳转的 demo方便验证基建是否真的跑通了。到这里一个空项目就成了。接下来要做的就是在这个骨架上填自己的业务。3.2 配置文件逐项解读rea 的配置集中在项目根目录这里挑几个关键的给大家解读一下。首先是vite.config.js。它负责开发服务器配置、插件注册、路径别名设置、构建相关的公共配置。核心配置是这样的import { defineConfig } from vite import vue from vitejs/plugin-vue import path from node:path export default defineConfig({ plugins: [vue()], resolve: { alias: { : path.resolve(__dirname, src), }, }, server: { port: 5173, proxy: { /api: { target: http://your-dev-server.com, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), }, }, }, build: { chunkSizeWarningLimit: 1024, rollupOptions: { output: { manualChunks: { vue: [vue, vue-router, pinia], vendor: [axios], }, }, }, }, })几个地方解释一下。alias配置解决了相对路径地狱的问题以后在代码里写/就是指向 src不需要去数层级。server.proxy是开发环境的代理配置。前后端分离开发的时候前端拿不到后端的直连地址通过代理绕过本地跨域限制同时把/api前缀去掉让请求路径更像线上的真实地址。build.rollupOptions.output.manualChunks是做分包策略。默认情况下 Vite 会把所有第三方依赖打进同一个文件里这会导致首屏加载缓慢。我把 Vue 全家桶和 Axios 这类大库拆成独立 chunk利用浏览器的并发加载能力同时利用缓存让这类不常变化的库不会因为业务代码更新而失效。然后是.env.development和.env.production文件。环境变量管理很直观# .env.development VITE_APP_TITLErea 开发环境 VITE_APP_BASE_API/api# .env.production VITE_APP_TITLErea 生产环境 VITE_APP_BASE_APIhttps://your-prod-domain.com/api请求层的 baseURL 从import.meta.env.VITE_APP_BASE_API读取这样不同环境下构建请求地址会自动跟着切换不用手动去改代码。注意Vite 只暴露以VITE_开头的环境变量到前端代码里其他变量是拿不到的。如果你需要自定义变量前缀必须是VITE_这是 Vite 为了安全特意做的限制。3.3 核心模块实现要点配置文件搞定之后骨架就活起来了。但这还不够真正日常开发里接触最多的几个模块——路由、状态管理、请求层才是决定项目好不好用的地方。路由模块使用 Vue Router 4采用懒加载方式导入页面组件。所谓懒加载就是页面组件不在应用启动时一次性全部加载而是等浏览器访问到某个路径时才去请求对应的 JS 文件。写法很简单把普通 import 换成一个箭头函数返回 import{ path: /, component: () import(/layouts/DefaultLayout.vue), children: [ { path: , name: Home, component: () import(/views/home/HomePage.vue), }, { path: user, name: User, component: () import(/views/user/UserList.vue), }, ], }路由懒加载的意义在首屏性能上体现得很直接。应用如果有一百个页面一次性全部打包进主 bundle可能好几 MB而用了懒加载之后用户访问哪个页面就加载哪个页面的代码首屏只加载一屏所需的文件。状态管理用 Pinia 的写法非常直接。定义一个 storeimport { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , userInfo: null, }), actions: { setToken(token) { this.token token localStorage.setItem(token, token) }, async fetchUserInfo() { const res await getUserInfo() this.userInfo res.data return res }, logout() { this.token this.userInfo null localStorage.removeItem(token) } } })这里要特别说一个实践中常见的误区不是所有数据都要放进 store。如果一个数据只有一个组件在使用那它就应该是这个组件内部的状态放进 store 反而是负担。store 适合放跨组件共享的数据比如用户登录信息、购物车列表、全局配置项。如果不管什么数据都往 store 里塞最终 store 文件会膨胀到一个难以阅读的巨型对象。请求层在代码层面的封装分成两层。第一层是 axios 实例的基本配置第二层是具体业务 API 的封装。实例配置已经讲过了业务 API 封装的写法是import request from /utils/request export function getUserList(params) { return request.get(/user/list, { params }) } export function createUser(data) { return request.post(/user, data) }每个函数对应一个后端接口函数名起到文档的作用。调用方不需要知道接口的具体路径也不需要关心 request 内部做了什么处理直接拿到 Promise使用 async/await 读取结果即可。这个模式在 code review 的时候非常讨喜因为业务代码里不再散落着 axios.get 之类的基础调用错误处理流程也统一了。3.4 脚本命令设计一键开发、构建、预览脚本命令是很容易被忽略但实用性很高的一环。rea 在 package.json 里设计了几个标准化脚本{ scripts: { dev: vite, build: vite build, preview: vite preview, lint: eslint src --fix, format: prettier --write \src/**/*.{js,ts,vue,json}\ } }这几个命令的用途npm run dev开发模式启动本地开发服务器带热更新。npm run build打包生产代码默认输出到 dist 目录。npm run preview本地预览打包产物。注意直接双击 dist/index.html 往往不能运行因为资源文件用的是绝对路径必须起一个静态服务器才能看到效果。preview 命令就是干这个的。npm run lint自动检查并修复 src 目录下的规范问题。npm run format执行格式化统一代码样式。可能有人觉得lint和format有点多余但实际上配合编辑器保存自动执行这两件事可以非常好地保证提交代码的质量。提交前本地跑一遍 lint很多低级错误在 CI 之前就被拦截了。4. 常见问题与排查记录4.1 依赖版本冲突版本范围带来的坑第一次在团队内部推广 rea 时最常遇到的坑是依赖版本冲突。现象是这样新成员 clone 项目后npm install安装失败报错信息形如EINTEGRITY或者ERESOLVE。原因绝大多数是 lock 文件与 package.json 版本范围不一致或者 Node 版本不同导致依赖的二进制编译产物不兼容。排查思路是这样首先检查 Node 版本是否满足 package.json 中engines字段的要求。rea 默认要求 Node 18 及以上。如果本机版本过旧建议用 Node 版本管理工具切换不要硬装高版本依赖跑到旧环境里。其次如果 lock 文件损坏或过期可以删掉node_modules和package-lock.json后重新安装rm -rf node_modules package-lock.json npm install但这里要提醒一句:只有在确认 lock 文件确实有问题时才用这个方法。平时开发中 lock 文件应当作为项目的一部分正常提交而不是每次安装都重新生成。因为 lock 文件存在的意义就是锁定每一层的依赖版本防止团队成员在不同时间 install 出不一样的依赖树。还有一种情况是 npm 7 以后对 peerDependencies 的解析变得严格如果依赖之间存在 peer 冲突npm 会直接报错而不是像 npm 6 那样自动装一个兼容版本。处理方式通常是升级冲突的依赖包到互相兼容的版本。4.2 热更新失效改了代码页面没反应开发的时候如果改了代码页面没有自动刷新很影响效率这也是排查频率比较高的一个典型问题。我在实际使用中遇到过一种情况在vite.config.js里配置了server.proxy而后端服务临时挂了页面发请求直接报 502。这时候改动代码热更新有时候会延迟甚至失效。排查下来发现原因是 Vite 依赖预构建缓存出了问题。简单地讲Vite 在首次启动时会扫描并预构建 node_modules 里用到的依赖生成一份缓存放到node_modules/.vite目录。如果某些依赖更新了或者缓存损坏热更新就会异常。解决方案很简单关掉 dev server把缓存目录删掉重启rm -rf node_modules/.vite npm run dev另外还有一个不太显眼的原因项目中有人不小心把需要监听的文件写到了.gitignore覆盖的目录里导致 Vite 没有监听那个文件的变化。Vite 的默认监听规则会排除node_modules和.git如果你把业务代码乱放到 node_modules 内部那是神仙也难救。遇到热更新问题别急着改配置先看命令行里有没有报错输出。Vite 的热更新出错通常会在终端里打出具体的模块路径沿着路径去查对应文件往往比盲试更高效。4.3 打包体积失控与首屏性能优化跑两个版本之后一个绕不开的问题是打包体积。尤其是项目里引了很多 UI 组件库和第三方库之后dist 目录的体积会肉眼可见地膨胀。rea 在构建配置里做了基础的分包但实际业务中往往还需要进一步优化。我先说三个最有效的调整。第一个是按需引入组件库。很多组件库支持全量引入和按需引入两种模式。全量引入会把所有组件都打进包哪怕只用了 Button 和 Input。按需引入可以让打包器只打包你在代码里真正用到的组件体积减少幅度经常以 MB 计。具体怎么做不同组件库都有对应的插件这里就不展开了。第二个是资源压缩和图片处理。开发阶段为了追求速度图片很少特意压缩但到了生产环境一张几 MB 的图片会直接拖垮首屏。通常做法是图片先做无损压缩再通过构建工具生成不同尺寸的产物配合loadinglazy属性让非首屏图片懒加载。第三个是路由懒加载的落地。前面讲了懒加载的写法但实际项目里常见的问题是某个页面引了一个巨大的第三方组件导致该页面首次进入时依然要加载很多不必要的东西。这时候可以考虑把这个大组件通过动态 import 的方式从页面模块里拆出来按住用户触发到某个操作时才加载。构建产物的分析可以用vite build完成后观察终端输出的报告也可以引入rollup-plugin-visualizer生成一个可交互的组件体积分析页面。通过分析图能快速定位哪个依赖的体积异常从而针对性地做替换或拆分。4.4 代理配置无效请求地址没走代理开发环境里还经常遇到一个问题明明配了 proxy浏览器里请求/api/xxx却直接打到本地或者一直 404。常见的排查点有两个。一是看环境变量VITE_APP_BASE_API是否真的生效。如果.env.development里配置的 baseURL 是http://your-dev-server.com而不是/api那请求就不会经过 Vite 代理而是直接跨域请求后端这时候改 proxy 配置自然没有意义。要让代理生效请求路径必须是相对路径比如/api/user/list。二是看代理的 rewrite 规则。很多后端接口的路径本身没有/api前缀而前端请求带上了这就需要在代理里把前缀去掉。如果 rewrite 没配对后端就会收到一个带/api的陌生路径返回 404。比如后端接口地址是/user/list而前端请求路径是/api/user/list那 proxy 里要加一条rewrite: (path) path.replace(/^\/api/, ),配置完之后记得重启 dev server。proxy 配置的改动不会触发热更新必须重启进程才生效。这个点我踩过不止一次改完配置发现不起作用最后重启好了才发现是忘了重启。5. 扩展思路与个人适配心得5.1 从模板到工作流的延伸rea 本来只是一个项目模板用着用着发现它还可以往工作流方向延伸。比如接入 CI 后每个 PR 触发自动 lint 和 build语法错误在合入前就被拦截。配合 swagger 自动生成接口类型定义API 层不再需要手写接口字段更新后重新生成就行。这些虽然不在 rea 代码库本身但 rea 把目录分好了接这些工具很方便。再比如主题定制。rea 的 styles 目录里把颜色、间距、字体这类设计变量抽到了独立的 scss 文件里换主题的时候只改变量不用满项目去找硬编码的颜色值。实际维护过一个换肤需求之后我才真心觉得这个抽离做得值。手机端适配方面rea 默认使用 rem viewport 的方案入口文件里有一段根据屏幕宽度动态设置根字体大小的逻辑。新项目如果确定是纯桌面端可以把这段逻辑删掉用 px 就行。这也就是模板的定位——给你一个默认方案但允许你按项目形态取舍。5.2 后续可以再加的东西每个团队情况不一样rea 也不可能覆盖所有需求。我自己的经验是不要试图把模板做到大而全加一个工具之前要先想清楚这是每个项目都会用到 ? 还是只有少数项目用得上后者不应该进模板而应该做成文档或扩展包。目前我手里后续想加的东西大概有几个单元测试模板Vitest Testing Library 的基础配置给工具函数和关键组件补一层测试保障。模拟接口方案开发时如果后端还没就绪内置一个 Mock 层让前端先跑起来。日志与错误监控的接入示例生产环境出现前端报错能第一时间定位现场。多语言国际化的最小骨架预留 i18n 的目录和切换逻辑方便有出海需求的项目快速扩展。这些内容如果逐一展开每一块都够写一篇单独的文章。rea 作为一个项目的起点它真正想传达的思路是不要应付“能跑就行”的最低标准把开始时的每一步都走踏实后面写业务的时候会省下大量跟基建缠斗的时间。从我个人的实际体验来看花一天时间认真整理一套模板远比每次新项目都从零开始配工程要划算。如果你手头也有一堆旧项目不妨抽个时间把它们的公共部分抽出来做一套属于自己和团队的 rea。最开始可能不确定哪些该进模板哪些不该进没关系先跑两个项目回头再调整慢慢就会找到适合自己团队的那个平衡点。最后分享一个我自己的经验模板里的注释不要写太多但要把该写的约定写清楚。比如“这个文件放什么”“这个函数什么时候不要用”一两句话点到为止比塞一整片文档强。代码该自解释的地方自己解释人该沟通的地方用人话沟通。希望这套 rea 的思路能给你一点启发。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑