1. 引言:为什么要从基础环境讲起
这篇系列的目标是记录我如何从 0 构建起 vueweb-demo-admin 项目,并细致拆解每一个模块、每一个关键配置。第一天先从最容易忽略但决定项目质量的"环境搭建"入手,帮助同学掌握从 Node 到质量校验的完整流程。
我的目标是:
- 梳理项目选型决策,让前端小白知道为什么选 Vue 3 + Vite + Pinia。
- 告诉你我如何组合 Vite 插件链,把组件、图标、路由、样式都管起来。
- 展示自动化脚本与钩子,确保团队开发时无脑执行即可达成统一规范。
这系列会持续更新,从目录/布局、组件库、权限、测试、部署,到自动化脚本、性能优化。今天的篇章是"基础准备",你只需耐心读完,环境就可以跑起来。
注:系列还会覆盖「接口封装与范型设计、请求/响应拦截、vue-request 最佳实践、API 自动化脚本与持续集成、性能与体验优化」等主题;本文聚焦把环境"一次性跑通",为后续章节打牢地基。
2. 技术栈拉通(表格说明)
| 维度 | 内容 | 说明 |
|---|---|---|
| Node 版本 | `^20.19.0 | |
| 包管理器 | pnpm(v9+) | 安装快速、软链接能力好,适合有多个项目的开发者。此外 pnpm 的 node-linker=hoisted 提升 IDE 兼容性。 |
| 核心框架 | Vue 3 + Pinia + Vue Router + TypeScript | 全面选用 Composition + TS,开箱即用的类型支持。 |
| UI、请求、状态 | Element Plus、axios/vue-request、pinia-plugin-persistedstate | 组件库 + 请求封装 + 状态持久化组合,满足中后台快开发需求。 |
| 工具链 | Vite + Vitest + Playwright + ESLint + Prettier + Oxlint + TailwindCSS | 从构建到测试再到格式校验,构成"开发→CI"的闭环。 |
2.1 0-1 安装步骤(命令+路径)
- Node 版本 & 工具 :使用
nvm install 20.19 && nvm use 20.19(或volta install node@20.19) 并执行node -v验证。 - 克隆项目 + 安装依赖 :如果你还没仓库,
git clone <repo>,进入根目录后运行pnpm install。依赖会写入package.json,pnpm-lock.json 和 node_modules 会自动生成。 - 配置全局工具(可选) :安装 Husky
pnpm dlx husky-init && pnpm install(仓库已有prepare脚本会自动生效),确保.husky/目录含pre-commit触发lint-staged。这里说明一下为什么使用Husky,因为Husky是 Git 钩子管理工具,用于在 Git 操作时自动执行脚本 。最常用的是 pre-commit 钩子,在提交代码前自动执行代码检查(lint、format 等),确保提交的代码符合规范
husky 作用 Git 钩子管理工具,在 Git 操作时自动执行脚本。本项目中用于在提交代码前自动执行 lint-staged(prettier + eslint + oxlint),确保提交的代码符合规范。
.husky 文件位置 项目根目录下的 .husky/ 文件夹,当前包含:

- pre-commit:提交前钩子,执行 npx lint-staged
- _/:Husky 配置目录
这个过程的目录影响:
你的项目根目录
├── package.json # scripts + deps
├── pnpm-lock.yaml # 中台依赖锁
├── .husky/ # hook 配置
└── node_modules/ # pnpm 安装的依赖(gitignore)
2.2 为什么要按这个顺序
- Node 版本:中后台项目依赖较多,较新版本能保证 vite+esbuild 兼容,通过
.nvmrc/.node-version明确版本。 - pnpm:npm/yarn 在依赖冲突时容易"吃"空间,pnpm 通过 symlink 保留一致性,尤其配合 CI 速度明显。
- Husky + lint-staged:让格式化/静态检查在提交阶段发生,避免"别人没看见 lint 报错"的低效回滚。
3. Vue 3 项目从 0 到 1 初始化
vueweb-demo-admin 是一个 Vue 3 + Vite + TypeScript 的中后台模板,下面是严格一步到位的初始化流程,确保你克隆后可以直接运行:
-
全局准备 :确认 Node 版本正确(
>=20.19),执行nvm install 20.19 && nvm use 20.19,然后node -v/pnpm -v。 -
脚手架创建 (仅当你还没项目):
bashpnpm create vite@latest vueweb-demo-admin -- --template vue-ts cd vueweb-demo-admin该命令会自动生成
package.json、vite.config.ts、tsconfig.json、src/main.ts等基础文件;如果你已经克隆项目略过该步。 -
安装依赖 (克隆后或者刚创建):
bashpnpm install pnpm dlx husky install # 确保 husky hooks 正常 pnpm run prepare # 触发 package.json 中的 husky 安装(pre-commit) -
确认源码入口 :
src/main.ts:挂载 Pinia、路由、Element Plus、全局样式。src/App.vue:放置<router-view />,需要引入src/layouts/MainLayout.vue。src/components/:存放通用控件;配合unplugin-vue-components实现自动注册。
-
配置 tsconfig + alias :初始
tsconfig.app.json中已经指定@/*,并在vite.config.ts里用fileURLToPath(new URL('./src', import.meta.url))映射,写法示例在后文。 -
添加常用目录 (若脚手架没有):
bashmkdir -p src/assets/styles src/layouts src/common/hooks touch src/assets/styles/element/variables.scss这些目录搭配
vite.config.ts里的scss.additionalData可以注入全局变量。
根目录可视化结构图

- .husky/:Git 钩子,当前启用 pre-commit 触发 lint-staged,提交前自动执行 prettier/eslint/oxlint。
- .trae/:文档与规则存放(如项目规则、实现文档),用于沉淀过程与规范。
- .windsurf/:工作流与文章草稿等辅助资料(本文档所在目录)。
- dist/ :构建产物目录,
pnpm run build后生成,用于部署。 - e2e/:端到端测试目录(Playwright),覆盖关键用户路径与回归场景。
- node_modules/:依赖安装目录(由 pnpm 管理,已被 .gitignore 忽略)。
- plugins/ :Vite 自定义插件或项目插件放置区,如
vite-plugin-router-named-map.ts。 - public/:静态资源根目录,构建时原样拷贝(如 favicon、静态脚本/样式)。
- scripts/ :自动化脚本目录,如
build.sh(部署脚本)、generator-api.js(根据 OpenAPI 生成类型)。 - src/:业务源码目录(组件、路由、状态、样式、页面等),开发的主要战场。
- auto-imports.d.ts / components.d.ts:由 AutoImport/Components 插件生成的类型声明,支持自动导入与按需注册。
- eslint.config.ts / .prettierrc.json / .oxlintrc.json:代码质量与风格校验配置,保证团队规范统一。
- playwright.config.ts / vitest.config.ts:E2E 与单元测试配置文件。
- tsconfig.json *:TypeScript 编译配置(含 app/node/vitest 等子配置),
paths与 Vite alias 对齐。 - vite.config.ts:Vite 核心配置(插件链、别名、构建选项等)。
- package.json / pnpm-lock.yaml:脚本与依赖清单;锁定依赖版本,确保多人环境一致。
3.1 为什么按这个节点顺序
pnpm create vite生成基础项目,后续都围绕src/main.ts、插件组合进行扩展。pnpm install+husky install让依赖与 git hook 立刻就位,避免后续开发时lint-staged失效。- 手动创建
assets/styles、common/hooks等目录是为了配合后续的组件/样式体系,从根目录开始把结构整理好,避免后续代码把文件散落。
配置图示
Node 20+
↓
pnpm install + husky 安装
↓
┌────────────┬──────────────┬────────────────┐
│ Vite (插件) │ TypeScript │ ESLint/Prettier │
│ (vue/jsx, │ strict + │ lint-staged │
│ auto import,│ paths, │ + oxlint │
│ svg icons) │ alias) │ │
└────────────┴──────────────┴────────────────┘
↓
Scripts:dev / build / test / lint / format / deploy
3. Vite 插件链拆解 vite.config.ts
ts
plugins: [
vue(),
vueJsx(),
vueDevTools(),
tailwindcss(),
AutoImport({ resolver: ElementPlusResolver({ importStyle: 'sass' }) }),
Components({ resolver: ElementPlusResolver({ importStyle: 'sass' }) }),
GenerateComponentName(...),
routerNamedMapPlugin(),
createSvgIconsPlugin({ iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')] }),
]
AutoImport+Components:按需/自动注册 Element Plus+常用方法,提高组件开发效率。GenerateComponentName+routerNamedMapPlugin:多布局全局统一命名,方便调试和定位目录。svg-icons:集中管理所有 svg,避免运行时动态请求。tailwindcss+ SCSSadditionalData:支持设计系统变量、暗夜主题、动画样式、和 Element 基础样式同步。
源码 .vue/.ts/.tsx
vue
vueJsx
vueDevTools
tailwindcss
AutoImport
ElementPlusResolver
Components
ElementPlusResolver
GenerateComponentName
routerNamedMapPlugin
createSvgIconsPlugin
构建输出
插件说明
- vue / vueJsx:处理 .vue 单文件组件与 JSX 语法
- vueDevTools:开发时提供 Vue DevTools 调试支持
- tailwindcss:编译 TailwindCSS 类名,支持设计系统与暗夜主题
- AutoImport:自动导入 Element Plus 组件与常用 API(ref、computed 等)
- Components:按需注册 Element Plus 组件,减少手动 import
- GenerateComponentName:多布局全局统一命名,方便调试
- routerNamedMapPlugin:路由命名映射,提升路由可维护性
- createSvgIconsPlugin:集中管理 SVG 图标,打包为 Sprite
4. TypeScript 配置说明 tsconfig.app.json
json
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"strict": true,
"skipLibCheck": true,
"noImplicitAny": false,
"strictNullChecks": false,
"paths": { "@/*": ["./src/*"] }
}
}
- 继承 Vue 官方配置,默认包含 DOM typings。
strict: true强制检查,但为了兼容第三方库把noImplicitAny/strictNullChecks适当关闭(实际运行时用 Pinia + API typing 补足)。paths+ Vite alias 统一@访问src。
TS 配置与 Vite Alias 关系图
tsconfig.app.json
paths: @/* → ./src/*
TypeScript
编译时解析
vite.config.ts
resolve.alias: @ → /src
Vite
构建时解析
代码中
import '@/store'
实际路径
./src/store
配置对应关系
- tsconfig.app.json :
paths: { "@/*": ["./src/*"] }- TypeScript 编译时识别@别名 - vite.config.ts :
resolve.alias: { '@': '/src' }- Vite 构建时解析@别名 - 代码中使用 :
import '@/store'- 统一使用@访问 src 目录下的文件
5. 自动化脚本 & 代码质量屏障 package.json
-
dev:启动 Vite 开发服务器。 -
build:调用type-check与build-only,确保构建前类型通过。 -
test:unit/test:e2e:Vitest + Playwright 保证单元与端到端覆盖。 -
lint/format:统一 ESLint、Prettier、oxlint。但我们还在lint-staged+husky里继续发力:lint-staged:
*.{vue,ts,js,tsx,jsx}:
1. prettier --write
2. eslint --fix --cache
3. oxlint --fix
这意味着每次 git commit 都会先执行上面三步,CI 环境只需跑一次 pnpm run lint 即可。
发布图/流程建议
图 1:Git 提交流程与代码质量检查
git add
git commit
husky pre-commit 钩子触发
lint-staged 执行
prettier --write
eslint --fix --cache
oxlint --fix
提交成功
说明 :每次 git commit 都会自动触发 husky pre-commit 钩子,执行 lint-staged 进行 prettier、eslint、oxlint 三步检查,确保代码质量。
图 2:开发到部署脚本流程
UAT 环境
生产环境
开发阶段
pnpm run dev
构建阶段
pnpm run build
部署环境
pnpm run deploy:uat
scripts/build.sh
pnpm run deploy:prod
VERSION=prod scripts/build.sh
构建产物
部署上线
说明:
pnpm run dev:启动开发服务器pnpm run build:构建生产版本(包含类型检查)pnpm run deploy:uat:使用scripts/build.sh部署到 UAT 环境pnpm run deploy:prod:设置VERSION=prod后使用scripts/build.sh部署到生产环境
5.1 自动化脚本配置路径
- scripts 位于
package.json根目录,自动可见;build.sh位于scripts/build.sh(用来区分 UAT/PROD)。 lint-staged是package.json里的字段,配合husky目录里pre-commit统一激活。可以打开.husky/pre-commit查看pnpm run lint-staged的命令链。- TypeScript 类型检查依赖
tsconfig.app.json(位于项目根),vue-tsc与vite build会引用这份配置。
这些文件位置意味:
root/
├── package.json # 脚本入口 + lint-staged
├── scripts/build.sh # 发布脚本(prod/uat)
├── .husky/pre-commit # 启动 lint-staged
├── tsconfig.app.json # Vue + TS 约束
└── vite.config.ts # 插件 + alias 配置
5.2 作用说明
build.sh:打包前先加载VERSION/NODE_ENV,再调用pnpm run build:prod,兼容多环境部署。lint-staged⇒prettier + eslint + oxlint:三个工具顺序由轻到重,prettier先格式,eslint再审,最后oxlint为代码规范(例如空格/格式)。tsconfig+type-check:提前通过vue-tsc,避免构建时报错,尤其面向 Pinia 状态、接口类型。
5.3 API 请求层实践:封装范型 + 拦截器
中后台系统的"数据层韧性"很重要,建议将 axios 封装成统一的请求实例,配合 TypeScript 范型定义响应体,集中处理鉴权、错误与日志。
ts
// api/http.ts(示例)
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
// 统一后端响应结构(可根据你的后端调整)
export interface ApiResp<T = unknown> {
code: number
message: string
data: T
}
const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000,
})
// 请求拦截:注入 Token、时区(使用北京时间)、traceId 等
http.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) config.headers.Authorization = `Bearer ${token}`
config.headers['X-Timezone'] = 'Asia/Shanghai'
return config
})
// 响应拦截:统一 code 处理、错误提示、数据解包
http.interceptors.response.use(
(resp: AxiosResponse<ApiResp>) => {
const { code, data, message } = resp.data ?? {}
if (code === 0) return data as any
// 可以在此处统一上报/弹窗
return Promise.reject(new Error(message || '请求失败'))
},
(error) => {
// 这里可增加网络错误、超时、重试等策略
return Promise.reject(error)
}
)
// 具备范型的请求函数:返回严格类型
export function request<T = unknown>(config: AxiosRequestConfig) {
return http.request<any, T>(config)
}
// 业务 API 示例
export function getUserDetail(id: string) {
return request<{ id: string; name: string }>({ url: `/user/${id}`, method: 'get' })
}
要点:
- 范型解包 :
request<any, T>让调用方拿到"已解包"的T,无需在业务层再访问resp.data。 - 拦截器单一职责:请求阶段处理"上下文信息"(token、时区)、响应阶段处理"协议一致性"(code、message、data)。
- 错误策略可插拔:例如在响应拦截统一注入"未登录跳转"、"权限不足提示"、"网络重试"等。
5.4 vue-request 使用指南(配合范型 + 缓存/并行/轮询)
vue-request 提供请求状态管理与自动重试、轮询、缓存等能力,配合上面的 request<T> 更丝滑:
ts
// views/User.vue(示例)
import { useRequest } from 'vue-request'
import { getUserDetail } from '@/api/http'
const { data, loading, error, refresh } = useRequest(() => getUserDetail('10001'), {
manual: false, // 进入页面自动请求
cacheKey: 'user#10001', // 简易缓存提升二次进入性能
staleTime: 5 * 60 * 1000,
retryCount: 2, // 弱网自动重试
})
// data.value 将是 { id: string; name: string }
常用技巧:
- 依赖变更自动刷新 :将
() => getUserDetail(userId.value)作为service,结合watch/computed动态刷新。 - 并行与依赖请求 :用多个
useRequest,或在onSuccess链式触发下一个。 - 表单提交 :
useRequest(service, { manual: true })+run(params)配合按钮提交,具备 loading 与错误态。
5.5 scripts 目录导览:自动化更新 API 与发布
项目根目录下的 scripts/ 用于承载"能让团队复用的自动化动作"。
-
scripts/generator-api.js :根据 OpenAPI/Swagger 生成 TypeScript 类型映射(ApiType)与接口声明(interface),输出到
src/types/api.d.ts,避免手写类型与路径出错。-
适用场景:后端频繁变更接口;希望前端类型自动对齐,减少回归成本。
-
基本用法:
bashpnpm run gen:api # package.json 中配置的命令(示例),执行后会在约定目录生成/更新 API 文件 -
推荐接入方式:
- 在 CI 或本地 pre-commit 前运行一次,保障分支内类型正确。
- 生成位置与命名规范清晰,配合
@/api/*的 barrel 导出,减少业务 import 路径复杂度。
-
-
scripts/build.sh:统一 UAT/PROD 打包逻辑,注入环境变量与版本号,保证构建产物一致性并可追溯。
最佳实践:
- 所有脚本在
package.json的scripts中提供"易记名",并配上简短注释; - 输出日志中包含开始/结束/关键产物路径,方便排障;
- 失败时返回非 0 状态码,让 CI 能够感知并中断。
6. 今日总结 & 明日预告
- 今天掌握了 Node + pnpm + Vite + TypeScript + ESLint + Prettier 三位一体的开发基础环境;
- 了解了插件流程、自动注册、svg 管理与全局样式方案;
- 配置了自动化脚本与 git 钩子,让开发流程可重复可复用。
明日预告:下篇继续拆解项目目录与路由结构,讲如何做多布局与权限路由守卫;欢迎评论「你最想要的下一期模块」并留下你的点赞/收藏!