用 Vite + Vue3 + UnoCSS 搭建企业级项目模板(一):基础骨架
发布时间:2026/10/14 15:44:21 锦皓数字建站
:基础骨架`)
栏目01 · 前端工程化 | 1 月 · 前端基础与 Vue3 入门 | 选题 6上个月组里并行起了两个新项目都是中后台系统。我的做法很传统把老项目整个复制过来改名删页面开工。结果第二个项目上线前联调接口请求怎么都不对排查了一下午才发现——老项目里三年前留下的 mock 拦截器还挂在main.ts里把/api前缀的请求全劫持到本地假数据了。当天我就决定花两天时间沉淀一个干净的项目模板以后新项目从模板拉不从老项目复制。这篇记录搭骨架的过程重点是踩坑。先说错误的做法第一次搭我直接npm create vuelatest把所有选项全勾上TypeScript、Router、Pinia、ESLint、Prettier、Vitest 全要。生成之后光删演示代码就删了二十分钟components里一堆TheWelcome.vue、WelcomeItem.vuerouter 挂着两个示例路由store 里躺着 counter。更麻烦的是这些脚手架默认配置并不都是我想在公司项目里要的——比如 Prettier 和 ESLint 的冲突规则默认勾选的组合跑起来会有格式打架的告警。所以后来我改成最小化起步模板里只放每个项目 100% 会用到的东西Router 都不放反正有些纯展示页用不上加上也就一行命令的事。最终定稿的版本node 22.12.0npm 10.9.0npmcreate vuelatest enterprise-template# 交互选项TypeScript - Yes其余全部 Nocdenterprise-templatenpminstallnpmi-Dunocss66.4.0 unocss/eslint-plugin66.4.0环境核对npx vite--version# vite/7.1.4node-econsole.log(require(vue/package.json).version)# 3.5.13node-econsole.log(require(unocss/package.json).version)# 66.4.0最终目录结构刻意保持扁平src/ ├── api/# 按后端模块拆一个文件对应一组接口├── assets/ ├── components/ ├── composables/# 组合式函数和组件严格分开├── router/ ├── styles/# 全局样式UnoCSS 之外的少量补充├── types/# 公共类型api 类型放 api/ 里就近维护├── utils/ ├── views/ ├── App.vue └── main.tscomposables/单独拎出来说一句见过太多项目把useXxx散落在utils/里新人根本不知道哪些是响应式的哪些不是。约定先行这比任何 lint 规则都管用。踩坑一路径别名配了一半等于没配别名是每个项目的标配。我在vite.config.ts里配好// vite.config.tsimport{fileURLToPath,URL}fromnode:urlimport{defineConfig}fromviteimportvuefromvitejs/plugin-vueexportdefaultdefineConfig({plugins:[vue()],resolve:{alias:{:fileURLToPath(newURL(./src,import.meta.url)),},},})npm run dev一切正常页面渲染、路由跳转都对。我顺手在src/api/user.ts里写了import type { UserInfo } from /types/user浏览器里跑得好好的就提交了。第二天 CI 挂了。vue-tsc --noEmit输出src/api/user.ts(3,21): error TS2307: Cannotfindmodule/types/useror its correspondingtypedeclarations.我一开始以为是 vue-tsc 的 bug——dev 环境明明是好的。查了半天 Stack Overflow 才想明白Vite 和 TypeScript 是两套独立的路径解析系统。dev 阶段 Vite 的 esbuild 转译根本不做类型检查别名解析走的是 Vite 自己的 resolve而vue-tsc走的是 tsconfig 的 paths 配置。我只配了前者。修法是在tsconfig.app.json里补上vue-ts 模板把 tsconfig 拆成了三个paths要写在tsconfig.app.json不是根上那个只做 references 的壳// tsconfig.app.json{compilerOptions:{baseUrl:.,paths:{/*:[./src/*]}}}这次教训我记到现在别信本地能跑就是对的类型检查是独立的一道闸必须让它过。踩坑二UnoCSS 装了但没生效而且不报错UnoCSS 接入就三步我照文档抄的// vite.config.ts 补充importUnoCSSfromunocss/viteexportdefaultdefineConfig({plugins:[vue(),UnoCSS()],})// uno.config.tsimport{defineConfig,presetUno,presetAttributify}fromunocssexportdefaultdefineConfig({presets:[presetUno(),presetAttributify()],})然后我在页面里写div classflex items-center gap-2刷新——样式完全没生效。诡异的是控制台没有任何报错Vite 启动日志也正常。我当时的第一反应是版本兼容问题差点 downgrade。后来在 DevTools 的 Elements 面板里发现 class 名明明挂在 DOM 上但 Computed Styles 里对应规则一条都没有——说明 CSS 文件压根没被加载。回头逐行对照文档才发现main.ts里少了这行// main.ts这个 import 不能少importvirtual:uno.css// 如果想统一 reset再加按需importunocss/reset/tailwind.cssvirtual:uno.css是 UnoCSS 注入的虚拟模块插件负责生成这些原子类但不会自动注入到你的应用里。不 import 就是不加载且全程静默。这个坑的隐蔽之处在于它违反直觉——一般依赖装完插件配完就该工作这里偏偏缺一步手动引入。后来我把这三步固化成模板新人再没踩过。环境变量与类型声明企业项目离不开多环境。约定.env.development本地、.env.production生产变量必须VITE_开头。很多模板会漏掉类型声明导致import.meta.env.VITE_API_BASE在 TS 里报隐式 any。补一个声明文件// src/types/env.d.ts/// reference typesvite/client /interfaceImportMetaEnv{readonlyVITE_API_BASE:stringreadonlyVITE_UPLOAD_URL:string}interfaceImportMeta{readonlyenv:ImportMetaEnv}这样写错变量名比如手滑打成VITE_API_BAE在编码阶段就会标红而不是跑到运行时才发现接口 404。这是我认为 TS 在中后台项目里性价比最高的一处应用成本五行代码。效果与代价模板落地后这两个月的效果新项目从拉模板到能写业务代码从原来的半天缩短到 20 分钟以内大部分时间花在跟后端对环境变量上。代价是模板本身成了要维护的东西——上周 UnoCSS 升 minor 版本动了 preset 的默认行为模板里的uno.config.ts跟着改了一行。模板不是一次性的这点要有预期。还有几块没做husky commitlint 没接公司目前 git hook 管理不统一接了反而跟部分老仓库的流程打架、CI 流水线还是每个项目手配的。这些留到下一篇写规范层的时候一起整理。代码我传了内部 GitLab就不贴仓库地址了。如果你在搭类似模板时有更顺手的组织方式评论区聊。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。