Vue3 工程构建
概述
Vue项目在搭建初期应当设定好目标、规范以及结构,以便后期扩展,避免结构混乱,代码难读、难以修改。
文档以vue3和ant-design-vue组件库为例,从零搭建项目,项目依赖如下:
生产依赖
json
{
"dependencies": {
"@ant-design/icons-vue": "7.0.1",
"ant-design-vue": "4.2.2",
"autoprefixer": "10.4.20",
"axios": "1.7.7",
"less": "4.2.0",
"mockjs": "1.1.0",
"pinia": "2.3.1",
"postcss": "8.5.3",
"tailwindcss": "3.4.17",
"vue": "3.5.27",
"vue-router": "4.4.5"
},
}
开发依赖
json
{
"devDependencies": {
"@commitlint/config-conventional": "19.7.1",
"@eslint/js": "^9.39.2",
"@types/node": "22.12.0",
"@typescript-eslint/eslint-plugin": "8.26.1",
"@typescript-eslint/parser": "8.26.1",
"@vitejs/plugin-vue": "5.0.4",
"@vue/test-utils": "2.4.6",
"@vue/tsconfig": "^0.8.1",
"commitlint": "19.7.1",
"cssnano": "^7.1.2",
"eslint": "9.17.0",
"eslint-plugin-vue": "9.28.0",
"globals": "^17.3.0",
"happy-dom": "20.5.0",
"husky": "9.1.7",
"lint-staged": "15.2.10",
"prettier": "3.5.3",
"stylelint": "16.12.0",
"stylelint-order": "^7.0.1",
"typescript": "5.9.3",
"unplugin-auto-import": "^21.0.0",
"unplugin-vue-components": "^31.0.0",
"vite": "5.4.21",
"vitest": "2.1.8",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "2.2.8"
}
}
pnpm安装依赖
首先项目建议使用pnpm进行包管理和安装,
- pnpm 的
node_modules布局使用符号链接来创建依赖项的嵌套结构。node_modules中每个包的每个文件都是来自内容可寻址存储的硬链接。(避免重复安装) - pnpm 是默认支持 monorepo 多项目管理(多项目管理)
- pnpm 使用链接仅将项目的直接依赖 项添加到模块目录的根目录中(幽灵依赖)
bash
npm i -g pnpm
项目生产依赖
在项目生产环境中的依赖,主要考虑项目性质以及UI设计:(由于vue3只能在现代浏览器下运行。所以应该从兼容现代浏览器的版本开始,不需要兼容ie版本)
- 项目使用vue3 应该配套使用状态管理库pinia 以及路由管理vue-router
- 项目组件库为ant-design-vue ,需要安装icon图标ant-design/icons-vue ,以及统一使用less作为css预处理器
- css工程化统一使用tailwindcss 和postcss 。autoprefixer自动补齐css前缀。
- 使用axios 请求接口,mockjs可以模拟接口数据。方便前期没有接口条件下开发。
项目开发环境vite以及vite相关配置
项目直接使用vite构建vue3+typescript项目
js
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
import { resolve, extname } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 自动导入常用 API(无需手写 import)
imports: ['vue', 'vue-router', 'pinia'],
resolvers: [
AntDesignVueResolver({
importStyle: 'less',
}),
],
dts: 'src/auto-imports.d.ts', // 生成 `auto-imports.d.ts` 全局 API 类型声明,支持 IDE 代码提示
}),
Components({
resolvers: [
AntDesignVueResolver({
importStyle: 'less', // 这里设置为 'less',以便在使用组件时自动引入对应的 Less 样式文件
}),
],
dts: 'src/components.d.ts', // 生成 `components.d.ts` 全局组件类型声明,支持 IDE 代码提示
}),
],
resolve: {
alias: {
'@': resolve(__dirname, 'src'), // 设置 '@' 代表 'src' 目录,方便在项目中使用绝对路径导入模块
},
},
css: {
preprocessorOptions: {
// 配置 Less 预处理器选项
less: {
javascriptEnabled: true, // 允许在 Less 文件中使用 JavaScript 表达式,这对于 Ant Design Vue 的样式定制非常重要
},
},
},
// 开发服务器配置
server: {
port: 3000,
open: true,
cors: true,
},
build: {
target: 'es2020', // 设置构建目标为 ES2020,利用现代浏览器的特性提升性能
outDir: 'dist',
sourcemap: false,
rollupOptions: {
output: {
// 按类型分类输出文件
entryFileNames: 'assets/js/[name]-[hash].js',
chunkFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: function (assetInfo) {
var _a;
var ext = extname((_a = assetInfo.name) !== null && _a !== void 0 ? _a : '');
if (ext === '.css') {
return 'assets/css/[name]-[hash][extname]';
}
return 'assets/[name]-[hash][extname]';
},
// 将核心依赖单独拆分成独立 chunk,方便 CDN 长效缓存
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
antd: ['ant-design-vue', '@ant-design/icons-vue'],
vendor: ['axios'],
},
},
},
},
});
代码规范eslint
ESLint 用于统一代码风格和查找潜在问题。本项目使用 JS/TS/Vue 分块配置,并根据不同文件类型启用对应规则。
js
//eslint.config.js
import js from '@eslint/js';
import globals from 'globals';
import vuePlugin from 'eslint-plugin-vue';
import vueParser from 'vue-eslint-parser';
import tseslint from '@typescript-eslint/eslint-plugin';
import tsparser from '@typescript-eslint/parser';
const isProd = process.env.NODE_ENV === 'production';
export default [
// ==================== 基础配置 ====================
{
// 全局忽略的文件和目录
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/coverage/**',
'**/public/**',
'**/*.min.js',
'**/*.d.ts',
'**/package-lock.json',
'**/pnpm-lock.yaml',
'**/yarn.lock',
'**/vite.config.d.ts',
'**/vitest.config.d.ts',
],
},
// ==================== JavaScript 通用配置 ====================
{
files: ['**/*.{js,mjs,cjs}'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.browser,
...globals.node,
...globals.es2020,
},
},
rules: {
...js.configs.recommended.rules,
// 自定义规则
'no-console': isProd ? ['warn', { allow: ['warn', 'error'] }] : 'off',
'no-debugger': isProd ? 'warn' : 'off',
'no-alert': 'warn',
'no-unused-vars': 'off', // 由 TypeScript 处理
'prefer-const': 'error',
eqeqeq: ['error', 'always'],
curly: ['error', 'all'],
},
},
// ==================== TypeScript 配置 ====================
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
// 使用应用和 Node 两个 tsconfig,提升类型检查覆盖面
project: ['./tsconfig.app.json', './tsconfig.node.json'],
},
globals: {
...globals.browser,
...globals.node,
...globals.es2020,
},
},
plugins: {
'@typescript-eslint': tseslint,
},
rules: {
...tseslint.configs.recommended.rules,
// TypeScript 特定规则
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/ban-ts-comment': 'warn',
'@typescript-eslint/no-empty-function': 'warn',
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-inferrable-types': 'warn',
'@typescript-eslint/consistent-type-imports': [
'warn',
{
prefer: 'type-imports',
disallowTypeAnnotations: false,
},
],
},
},
// ==================== Vue 3 文件配置 ====================
{
files: ['**/*.vue'],
plugins: {
vue: vuePlugin,
},
languageOptions: {
// 使用官方 Vue 解析器对象,支持 <template> + <script setup>
parser: vueParser,
parserOptions: {
parser: tsparser,
ecmaVersion: 'latest',
sourceType: 'module',
extraFileExtensions: ['.vue'],
project: ['./tsconfig.app.json', './tsconfig.node.json'],
},
globals: {
...globals.browser,
},
},
rules: {
// 继承 Vue 3 推荐规则
...vuePlugin.configs['vue3-recommended'].rules,
// ========== Vue 3 自定义规则 ==========
// 1. 组件命名规则(针对 Vue 3 单文件组件)
'vue/multi-word-component-names': [
'error',
{
ignores: [
'index', // index.vue
'App', // App.vue
'404', // 404.vue
'[id]', // 动态路由组件
'[...all]', // 动态路由组件
'Layout', // Layout.vue
'Default', // Default.vue
'Main', // Main.vue
],
},
],
// 2. 组件属性换行规则(针对 Ant Design Vue 属性多的特点)
'vue/max-attributes-per-line': [
'error',
{
singleline: 5, // Ant Design 组件通常属性较多,放宽到5个
multiline: {
max: 1,
},
},
],
// 3. Ant Design Vue 组件名特殊处理(关键配置)
'vue/component-name-in-template-casing': [
'error',
'PascalCase',
{
registeredComponentsOnly: false,
ignores: [
// Ant Design Vue 组件前缀 (a-)
'/^a-/', // a-button, a-input, a-modal
'/^A[A-Z]/', // AButton, AInput
// Vue 内置组件
'router-view',
'router-link',
'transition',
'transition-group',
'keep-alive',
'component',
'slot',
'template',
// 常见第三方组件
'icon',
'icons',
],
},
],
// 4. 其他 Vue 规则调整
'vue/require-default-prop': 'off', // 不要求必须默认值
'vue/no-v-html': 'warn', // 警告使用 v-html
'vue/prop-name-casing': ['error', 'camelCase'], // props 使用驼峰
'vue/attribute-hyphenation': ['error', 'always'], // 属性使用连字符
// 5. 模板内容换行
'vue/html-closing-bracket-newline': [
'error',
{
singleline: 'never',
multiline: 'always',
},
],
// 7. 顺序规则(可选,使代码更整洁)
'vue/attributes-order': [
'error',
{
order: [
'DEFINITION', // is, v-is
'LIST_RENDERING', // v-for
'CONDITIONALS', // v-if, v-else-if, v-else, v-show, v-cloak
'RENDER_MODIFIERS', // v-once, v-pre
'GLOBAL', // id
'UNIQUE', // ref, key, v-slot, v-model
'SLOT', // v-slot
'TWO_WAY_BINDING', // v-model
'OTHER_DIRECTIVES', // v-custom-directive
'OTHER_ATTR', // 其他属性
'EVENTS', // v-on
'CONTENT', // v-text, v-html
],
},
],
},
},
// ==================== 测试文件特殊配置 ====================
{
files: ['**/__tests__/**/*.{js,ts,vue}', '**/*.test.{js,ts,vue}', '**/*.spec.{js,ts,vue}'],
plugins: {
'@typescript-eslint': tseslint,
},
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: {
// 使用 Vitest 全局变量
...globals.vitest,
},
},
rules: {
'no-console': 'off',
'no-debugger': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
// ==================== 配置文件特殊处理 ====================
{
files: ['**/vite.config.{js,ts}', '**/vitest.config.{js,ts}', '**/eslint.config.{js,mjs}', '**/*.config.{js,ts}'],
plugins: {
'@typescript-eslint': tseslint,
},
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: {
...globals.node,
},
},
rules: {
'no-console': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
},
},
];
全局忽略配置
- ignores:全局忽略的目录和文件模式,如:
**/node_modules/**、**/dist/**、**/build/**:依赖与构建输出目录。**/coverage/**:测试覆盖率报告。**/public/**:静态资源目录。**/*.min.js、**/*.d.ts:压缩 JS 与类型声明文件。- 各类锁文件和 *.config.d.ts 类型声明文件等。
JavaScript 通用配置
- files:
**/*.{js,mjs,cjs}- 对所有 JS 文件启用该配置。
- languageOptions:
- ecmaVersion:
latest,使用最新 ECMAScript 语法。 - sourceType:
module,按 ES Module 解析。 - globals:合并
browser、node、es2020全局变量,避免误报。
- ecmaVersion:
- rules:
...js.configs.recommended.rules:继承官方 ESLint 推荐规则。no-console:生产环境下仅允许console.warn/console.error,开发环境关闭。no-debugger:生产环境警告,开发环境关闭。no-alert:使用alert时给出警告。no-unused-vars:关闭,由 TypeScript 规则接管。prefer-const:推荐使用 const。eqeqeq:强制使用===/!==。curly:要求所有控制语句使用大括号。
TypeScript 配置
- files:
**/*.{ts,tsx} - languageOptions:
- parser:
@typescript-eslint/parser,支持 TS 语法。 - parserOptions.project:
./tsconfig.app.json,./tsconfig.node.json,启用基于项目的类型信息检查。 - globals:同样合并 browser/node/es2020 全局。
- parser:
- plugins:
@typescript-eslint:启用 TS 专用规则。
- rules(节选):
- 基于
@typescript-eslint官方 recommended 规则。 @typescript-eslint/no-explicit-any:对any给出警告。@typescript-eslint/no-unused-vars:检查未使用变量,可通过_前缀忽略。@typescript-eslint/ban-ts-comment:限制// @ts-ignore等用法。@typescript-eslint/no-non-null-assertion:对!非空断言警告。@typescript-eslint/explicit-function-return-type:关闭强制显式返回类型。@typescript-eslint/consistent-type-imports:推荐使用type导入形式。
- 基于
Vue 3 文件配置
- files:
**/*.vue - plugins:
vue:Vue 官方 ESLint 插件。
- languageOptions:
- parser:
vue-eslint-parser,支持<template>+<script setup>。 - parserOptions.parser:内部再使用 TS 解析器,支持 TypeScript。
- parserOptions.project:同样引用
tsconfig.app.json和tsconfig.node.json。
- parser:
- rules(节选):
...vuePlugin.configs['vue3-recommended'].rules:继承 Vue3 推荐规则集。vue/multi-word-component-names:强制组件名多词,忽略特定名称(如 App、Layout、index 等)。vue/max-attributes-per-line:单行最多 5 个属性,多行时每行 1 个,方便 Ant Design Vue 组件阅读。vue/component-name-in-template-casing:模板中组件名强制 PascalCase,但对a-button等 Ant Design 组件和部分内置组件放宽。vue/require-default-prop:关闭 props 强制默认值。vue/no-v-html:对v-html给出警告。vue/prop-name-casing:props 必须使用 camelCase。vue/attribute-hyphenation:模板属性使用连字符形式。vue/html-closing-bracket-newline:多行标签关闭时必须换行。vue/attributes-order:规范属性书写顺序(如定义、条件、事件等)。
测试文件特殊配置
- files:
**/__tests__/**/*.{js,ts,vue}**/*.test.{js,ts,vue}**/*.spec.{js,ts,vue}
- languageOptions.globals:
- 使用
globals.vitest,注入 Vitest 的全局(如describe、it、expect等)。
- 使用
- rules:
no-console/no-debugger:在测试中关闭限制。- 放宽 TypeScript 关于 any 和非空断言的限制,方便编写测试用例。
配置文件自身的特殊处理
- files:
vite.config.{js,ts}、vitest.config.{js,ts}、eslint.config.{js,mjs}以及*.config.{js,ts}。 - languageOptions:仅使用 Node 环境的全局变量。
- rules:
no-console:关闭,允许在配置文件中打印调试信息。@typescript-eslint/no-unused-vars:降级为 warn,避免轻微未使用变量导致出错。
.prettierrc代码自动格式化
本项目使用 Prettier 统一代码格式,配置文件为 .prettierrc,并通过 npm script 与 lint-staged 集成,在保存/提交时代码会被自动格式化。
json
//.prettierrc
{
"printWidth": 150,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid",
"vueIndentScriptAndStyle": true,
"htmlWhitespaceSensitivity": "css",
"endOfLine": "lf"
}
通过vscode插件Prettier - Code formatter对代码进行自动格式化。能够更专注于代码逻辑书写。
.prettierrc 关键选项
- printWidth:
150- 每行最大字符数,超过会自动换行。
- tabWidth:
4- 一个缩进级别使用 4 个空格。
- useTabs:
false- 使用空格而不是制表符进行缩进。
- semi:
true- 语句末尾总是添加分号。
- singleQuote:
true- 使用单引号代替双引号。
- trailingComma:
"es5"- 在 ES5 允许的地方(对象、数组等)尽量保留尾随逗号。
- bracketSpacing:
true- 对象字面量的大括号两侧保留空格,例如
{ foo: bar }。
- 对象字面量的大括号两侧保留空格,例如
- arrowParens:
"avoid"- 能省略箭头函数参数括号时就省略,例如
x => x + 1。
- 能省略箭头函数参数括号时就省略,例如
- vueIndentScriptAndStyle:
true- 在 .vue 文件中对
<script>和<style>内容进行缩进。
- 在 .vue 文件中对
- htmlWhitespaceSensitivity:
"css"- 按 CSS 的规则处理 HTML 空白字符,避免过度压缩影响布局。
- endOfLine:
"lf"- 统一使用 LF 换行符,有利于跨平台一致性。
其他常用配置(可选)
- singleAttributePerLine
- 默认:
false - 作用:在 HTML / Vue / JSX 标签中,每个属性是否独占一行。属性较多的组件,设为
true可读性更好,但文件会更长。
- 默认:
- jsxSingleQuote
- 默认:
false - 作用:控制 JSX/TSX 内是否也使用单引号。若项目希望"所有地方都统一用单引号",可以设为
true。
- 默认:
- quoteProps
- 默认:
"as-needed" - 常用值:
"as-needed"(默认,仅在需要时加引号)、"consistent"(同一对象内保持一致)、"preserve"(保留原样)。 - 作用:控制对象属性名(key)是否加引号,可根据团队对 JSON/对象风格的偏好统一约定。
- 默认:
- bracketSameLine
- 默认:
false - 作用:控制多行 JSX/HTML 标签的闭合
>是否与最后一行内容在同一行。不同团队习惯不同,可按团队偏好统一。
- 默认:
- proseWrap
- 默认:
"preserve" - 常用值:
"always"、"never"、"preserve"。 - 作用:控制 Markdown 文本是否在
printWidth处自动换行。文档较多的项目,若希望 diff 更细致、行宽统一,可考虑设为"always"。
- 默认:
- embeddedLanguageFormatting
- 默认:
"auto" - 常用值:
"auto"、"off"。 - 作用:是否格式化字符串模板或文件中嵌入的代码块(如 Markdown 里的代码块、内联脚本等)。若不希望被自动改动,可设为
"off"。
- 默认:
- requirePragma
- 默认:
false - 作用:只有在文件头部包含特定注释(例如
@format)时才会被 Prettier 格式化。通常用于大型旧项目的"渐进式接入"。
- 默认:
- insertPragma
- 默认:
false - 作用:在被 Prettier 格式化过的文件头部自动插入
@format注释,常配合requirePragma使用。
- 默认:
- rangeStart / rangeEnd
- 默认:
0/Infinity - 作用:仅格式化文件的某一段范围,一般通过 CLI 或编辑器集成设置,适用于"只格式化选中区域"的场景。
- 默认:
- overrides
- 类型:数组
- 作用:按文件匹配规则(
files/excludeFiles)为不同类型文件指定不同的 Prettier 配置,例如:- 对
*.md使用不同的printWidth或proseWrap; - 对
*.json关闭某些影响可读性的规则等。
- 对
- plugins
- 类型:数组
- 作用:引入第三方 Prettier 插件,例如:
- 排序 import、属性或 Tailwind 类名;
- 支持额外的语法/语言。
- 仅在确有需求时再引入,避免增加不必要的依赖和格式化开销。
与脚本命令的关系
format脚本:prettier --write .- 手动运行时,会对整个项目文件执行一次格式化。
与 ESLint / Stylelint / lint-staged 的协同
- 在
.lintstagedrc中:- 对
*.{js,ts,vue}文件:先用eslint --cache --fix --max-warnings=0再用prettier --write,先修复语法/风格问题,再统一格式。 - 对
*.{css,less,scss}:先用stylelint --cache --fix检查样式规范,再用 Prettier 格式化。 - 对
*.{json,md,html,yml,yaml}:直接使用prettier --write进行格式化。
- 对
- 这样可以保证:
- ESLint/Stylelint 负责"代码/样式是否合理、有没有问题";
- Prettier 负责"长什么样、缩进和空格如何对齐"。
typescript 配置
本项目使用多层 tsconfig 管理不同环境和用途的 TypeScript 配置:
- tsconfig.json:顶层工程引用文件。
- tsconfig.app.json:应用源码相关配置。
- tsconfig.node.json:Node 环境下的 TS 配置(主要用于 vite.config.ts、vitest.config.ts 等)。
tsconfig.json
jsonc
{
"files": [],
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
}
- files:空数组
- 顶层不直接编译任何文件,仅作为引用入口。
- references:
- 引用
tsconfig.app.json与tsconfig.node.json,组成 TS 的多工程(project references)结构,便于增量构建和工具支持。
- 引用
tsconfig.app.json
json
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
extends
"@vue/tsconfig/tsconfig.dom.json"- 基于 Vue 官方推荐的 DOM 环境配置,自动包含适合 Vue 3 Web 应用的编译选项和 lib 设置。
compilerOptions(节选)
- target:
"ES2020"- 输出目标为 ES2020,支持较新的 JS 特性。
- useDefineForClassFields:
true- 使用符合 TC39 标准的类字段语义。
- lib:
["ES2020", "DOM", "DOM.Iterable"]- 包含 ES2020 和 DOM 相关的类型声明。
- module:
"ESNext"- 使用 ESNext 模块系统,交给打包工具处理。
- skipLibCheck:
true- 跳过库声明文件的类型检查,提高编译速度。
- moduleResolution:
"bundler"- 使用适合打包工具(如 Vite)的模块解析策略。
- allowImportingTsExtensions:
true- 允许显式导入
.ts扩展名文件。
- 允许显式导入
- resolveJsonModule:
true- 允许导入 JSON 文件,并生成对应的类型。
- isolatedModules:
true- 强制每个文件都可单独编译,有利于配合 Babel/Vite 使用。
- noEmit:
true- 不输出编译结果文件,仅做类型检查。
- jsx:
"preserve"- 保留 JSX,交给后续工具处理(如果使用 JSX/TSX)。
- strict:
true- 开启严格模式,包含多项严格类型检查选项。
- noUnusedLocals / noUnusedParameters:
true- 禁止未使用的本地变量和参数。
- noFallthroughCasesInSwitch:
true- 阻止 switch 语句的 case 贯穿错误。
- baseUrl:
"."- 以项目根目录为基础路径。
- paths:
"@/*": ["src/*"]- 对应 Vite 中的
@别名,使 TS 能理解@/xxx导入路径。
include
"src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"- 指明应用代码中需要被 TS 类型系统分析的文件范围。
references
- 引用
./tsconfig.node.json- 让应用配置依赖 Node 配置,便于统一工程结构和增量编译。
tsconfig.node.json
json
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts", "vitest.config.ts"]
}
compilerOptions
- composite:
true- 表明该配置参与 TS 的工程引用(project references),允许生成增量信息。
- skipLibCheck:
true- 跳过声明文件检查,加快编译。
- module:
"ESNext"- 使用 ESNext 模块系统。
- moduleResolution:
"bundler"- 适配 Vite 等打包工具的解析方式。
- allowSyntheticDefaultImports:
true- 允许对仅有
export =的模块使用默认导入,兼容 CommonJS 包。
- 允许对仅有
- strict:
true- 同样开启严格类型检查。
include
["vite.config.ts", "vitest.config.ts"]- 仅对这两个 Node 环境运行的配置文件进行类型检查,保证其类型安全和智能提示。
TypeScript 工具链与脚本(补充)
vue-tsc:
markdown
- 专门针对 Vue 3 项目(包括 `.vue` 文件)进行类型检查的工具。
- 在 package.json 中通过脚本:
- `type-check`: `vue-tsc -b`
- `build`: `vue-tsc -b && vite build`
- 先基于 tsconfig 工程配置做一遍完整类型检查,再进入 Vite 构建流程,避免类型错误进入打包阶段。
ESLint + @typescript-eslint/*:
markdown
- ESLint 使用 `@typescript-eslint/parser` 与 `@typescript-eslint/eslint-plugin` 读取 tsconfig 中的编译选项和类型信息,对 TS/TSX 代码做更精细的规则检查。
- 通过 parserOptions.project 指向 `tsconfig.app.json` / `tsconfig.node.json`,确保类型感知规则(如 no-unused-vars、no-explicit-any 等)能够发挥作用。
Vitest 与 TS: - vitest.config.ts 通过 mergeConfig 复用 Vite 配置,使测试文件也能使用同样的别名与 TS 配置。 - 测试代码本身的类型检查依托于上述 tsconfig 和 vue-tsc/TypeScript 工具链。
Git Hooks & 提交规范配置说明
本项目通过 Husky、lint-staged 和 Commitlint 组成一套 Git 提交前检查与提交消息规范校验流程。
Husky 目录结构
.husky/pre-commit- 提交前(pre-commit hook)执行。
.husky/commit-msg- 输入提交信息后、真正写入提交之前执行。
package.json 中:
"prepare": "husky"- 安装依赖后会自动初始化 Husky(创建 .husky 目录),确保 Git Hooks 生效。
pre-commit 钩子:代码质量检查
文件:.husky/pre-commit
内容:
sh
# 严格模式:提交前必须通过 lint-staged 检查
pnpm lint-staged
含义:
- 在执行
git commit时,只对暂存区(staged)的文件运行 lint-staged。 - 如果 lint-staged 中配置的命令有失败,则本次提交会被中断,强制开发者先修复问题。
lint-staged 配置:只检查改动文件
文件:.lintstagedrc
核心规则:
jsonc
{
"*.{js,ts}": ["eslint --cache --fix --max-warnings=0", "prettier --write"],
"*.vue": ["eslint --cache --fix --max-warnings=0", "prettier --write"],
"*.{css,less,scss}": ["stylelint --cache --fix", "prettier --write"],
"*.{json,md,html,yml,yaml}": "prettier --write",
"src/**/components/**/*.{vue,js,ts}": ["eslint --cache --fix --max-warnings=5"],
"src/**/antd/**/*.{vue,js,ts}": ["eslint --cache --fix --max-warnings=5"],
}
说明:
*.{js,ts}/*.vue- 先使用 ESLint(带缓存、自动修复、并将允许的 warning 数量控制为 0)。
- 再使用 Prettier 统一代码格式。
*.{css,less,scss}- 使用 Stylelint 进行样式规范检查并自动修复,然后交给 Prettier 统一格式。
*.{json,md,html,yml,yaml}- 仅用 Prettier 进行格式化,保证缩进与风格统一。
src/**/components/**/*.{vue,js,ts}与src/**/antd/**/*.{vue,js,ts}- 对关键目录(组件、Ant Design 相关目录)额外运行一次 ESLint,并允许少量 warning(max-warnings=5),强调这里的代码质量。
commit-msg 钩子:提交信息规范
文件:.husky/commit-msg
内容:
sh
# 严格模式:提交信息必须符合规范,否则中断提交
pnpm commitlint --edit "$1"
含义:
- 在编写完 commit message 后,使用 Commitlint 对提交信息进行校验。
- 如果不符合规范(例如类型不合法、描述太短等),提交会被中止,需要修改提交说明后重试。
Commitlint 规则
文件:.commitlintrc.cjs
核心规则:
cjs
// .commitlintrc.cjs
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// 1. 提交类型(必需)
'type-enum': [
2,
'always',
[
'feat', // 新功能
'fix', // Bug修复
'docs', // 文档更新
'style', // 代码格式(空格、分号等,不影响功能)
'refactor', // 重构(既不是新功能也不是bug修复)
'test', // 测试相关
'chore', // 构建过程或辅助工具变动
'perf', // 性能优化
'build', // 构建系统或外部依赖变更
'ci', // CI配置变更
'revert', // 回滚提交
'other', // 其他类型
],
],
// 2. 类型必须小写
'type-case': [2, 'always', 'lower-case'],
// 3. 类型不能为空
'type-empty': [2, 'never'],
// 4. 主题(描述)不能为空
'subject-empty': [2, 'never'],
// 5. 主题不以句号结尾
'subject-full-stop': [2, 'never', '.'],
// 6. 主题最少3个字符
'subject-min-length': [2, 'always', 3],
// 7. 主题最多100个字符(建议一行能显示完整)
'subject-max-length': [2, 'always', 100],
// 8. 作用域(可选)
'scope-enum': [
2,
'always',
[
'component', // 组件
'page', // 页面
'layout', // 布局
'router', // 路由
'store', // 状态管理(Pinia)
'api', // API接口
'utils', // 工具函数
'styles', // 样式
'types', // TypeScript类型
'config', // 配置
'deps', // 依赖更新
'other', // 其他(不属于以上分类的提交)
],
],
},
};
关键点:
-
extends:
['@commitlint/config-conventional']- 基于社区常用的 Conventional Commits 规范。
-
type-enum- 限制可用的提交类型,例如:
feat、fix、docs、style、refactor、test、chore、perf、build、ci、revert、other等。
- 限制可用的提交类型,例如:
-
type-case/type-empty- 类型必须小写,且不能为空。
-
subject-empty/subject-min-length/subject-max-length- 提交描述必填,长度在 3~100 字符之间,且不允许以句号结尾(
subject-full-stop规则)。
- 提交描述必填,长度在 3~100 字符之间,且不允许以句号结尾(
-
scope-enum- 可选的作用域列表,如
component、page、layout、router、store、api、utils、styles、types、config、deps、other等,帮助约束"这个提交主要改了哪一类东西"。
- 可选的作用域列表,如
日常使用建议
- 开发过程中:
- 经常本地执行
pnpm lint、pnpm format保持代码整洁。
- 经常本地执行
- 提交代码时:
- 按照约定的格式书写提交信息,例如:
feat(component): 新增用户列表组件fix(api): 修复登录接口返回值解析错误
- pre-commit 和 commit-msg 钩子会自动帮你做最后一层把关。
- 按照约定的格式书写提交信息,例如:
postcss.config.cjs 配置说明
PostCSS 用于在构建过程中对 CSS 进行各种自动化处理。本配置主要启用了 Tailwind CSS、Autoprefixer 以及在生产环境使用的 cssnano 压缩。
plugins 插件配置
cjs
// postcss.config.cjs
module.exports = {
plugins: {
// Tailwind CSS 插件
tailwindcss: {
config: './tailwind.config.js', // 指定 Tailwind 配置文件路径
},
// Autoprefixer 自动添加浏览器前缀
autoprefixer: {
overrideBrowserslist: [
'last 2 versions', // 支持最近2个版本的浏览器
'> 1%', // 全球使用率 > 1% 的浏览器
'ios >= 8', // iOS 8+
'android >= 4.4', // Android 4.4+
'not ie <= 11', // 不支持 IE 11 及以下
'not dead', // 不包含已死亡的浏览器
],
grid: true, // 为 IE 启用 CSS Grid 前缀
flexbox: true, // 为旧版浏览器添加 Flexbox 前缀
remove: false, // 不删除过时的前缀
},
// 可选:CSS 压缩(生产环境)
...(process.env.NODE_ENV === 'production'
? {
cssnano: {
preset: [
'default',
{
discardComments: { removeAll: true }, // 删除所有注释
normalizeWhitespace: false, // 不压缩空格(由构建工具处理)
},
],
},
}
: {}),
},
};
tailwindcss
- 作用:启用 Tailwind CSS,按需生成原子化工具类样式。
- config:
'./tailwind.config.js'- 指定 Tailwind 的配置文件路径,统一管理扫描范围、主题颜色等。
autoprefixer
- 作用:自动为 CSS 添加浏览器前缀,提升兼容性。
- overrideBrowserslist:浏览器兼容策略列表:
last 2 versions:支持最近 2 个版本的各主流浏览器。> 1%:全球使用率大于 1% 的浏览器。ios >= 8:支持 iOS 8 及以上版本。android >= 4.4:支持 Android 4.4 及以上版本。not ie <= 11:排除 IE11 及以下版本。not dead:排除已经停止维护的"死亡"浏览器。
- grid:
true- 为 IE 等浏览器添加 CSS Grid 前缀。
- flexbox:
true- 为旧版浏览器添加 Flexbox 前缀。
- remove:
false- 不删除已有的旧前缀,避免影响兼容性。
cssnano(仅生产环境)
- 作用:在生产环境中对 CSS 进行压缩和优化,减小包体积。
- 条件启用:
process.env.NODE_ENV === 'production'时才添加该插件。 - preset:
'default':使用 cssnano 默认优化策略。- 配置对象:
- discardComments.removeAll:
true,删除所有 CSS 注释。 - normalizeWhitespace:
false,不在此处压缩空白字符(交由构建工具处理)。
- discardComments.removeAll:
总结
- 开发环境:Tailwind + Autoprefixer,便于快速开发与兼容性处理。
- 生产环境:在上述基础上额外启用 cssnano,进一步压缩 CSS 提高加载性能。
tailwind.config.js 配置说明
该文件定义了 Tailwind CSS 的扫描范围、主题扩展以及与 Ant Design Vue 的配合策略。
js
// tailwind.config.js(精简版)
module.exports = {
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {
// Ant Design 主题颜色
colors: {
primary: '#1890ff',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d',
info: '#13c2c2',
},
// 字体
fontFamily: {
sans: ['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif'],
},
// 圆角
borderRadius: {
ant: '6px',
},
// 阴影
boxShadow: {
ant: '0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05)',
},
},
// 响应式断点
screens: {
xs: '480px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
},
},
// 关键:禁用 preflight 避免与 Ant Design 冲突
corePlugins: {
preflight: false,
},
plugins: [],
};
content 扫描范围
['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}']- Tailwind 会在这些文件中扫描类名,只生成实际用到的工具类,减少最终 CSS 体积。
theme 主题配置
extend 扩展主题
-
colors:
- primary:
#1890ff - success:
#52c41a - warning:
#faad14 - error:
#f5222d - info:
#13c2c2 - 这些颜色与 Ant Design 的主题色保持一致,方便统一 UI 风格。
- primary:
-
fontFamily.sans:
['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif']- 定义无衬线字体的优先级列表,提升跨平台字体一致性。
-
borderRadius.ant:
6px,用于配合 Ant Design 的默认圆角风格,可在项目中通过自定义类统一使用。
-
boxShadow.ant:
- 一组与 Ant Design 近似的阴影配置,使自定义元素与 AntD 组件视觉统一。
screens 断点定义
- xs:
480px - sm:
640px - md:
768px - lg:
1024px - xl:
1280px - 2xl:
1536px
这些断点用于响应式布局,如 md:w-1/2 表示在 md 及以上宽度时占一半宽度。
corePlugins 内置插件控制
- preflight:
false- 关闭 Tailwind 的预设 CSS 重置(preflight),以避免与 Ant Design 自身的样式重置产生冲突。
- 通过此设置,可以让 Ant Design 的默认样式在项目中保持预期行为。
plugins 自定义插件
- 当前为
[](空数组)。- 需要时可以在此添加社区或自定义的 Tailwind 插件,例如表单、美化滚动条等。
vitest.config.ts 配置说明
Vitest 是与 Vite 深度集成的测试框架。本配置基于 Vite 配置进行扩展,使测试环境与实际构建环境保持一致。
js
import { mergeConfig } from 'vitest/config';
import viteConfig from './vite.config';
export default mergeConfig(viteConfig, {
test: {
globals: true,
environment: 'happy-dom',
include: ['src/**/*.{test,spec}.{js,ts,vue}'],
},
});
mergeConfig 与基础配置
import { mergeConfig } from 'vitest/config'- 用于在 Vitest 配置中复用并扩展 Vite 的配置。
import viteConfig from './vite.config'- 引入项目的 Vite 配置,保证测试环境的别名、插件等与开发/构建保持一致。
export default mergeConfig(viteConfig, { ... })- 使用 mergeConfig 将 Vitest 相关配置与 Vite 基础配置合并。
test 选项
- globals:
true- 启用全局测试 API,如
describe、it、expect等,无需手动 import。
- 启用全局测试 API,如
- environment:
'happy-dom'- 使用 happy-dom 提供类似浏览器的 DOM 环境,适合测试 Vue 组件与涉及 DOM 操作的逻辑。
- include:
['src/**/*.{test,spec}.{js,ts,vue}']- 指定测试文件匹配模式:
- 位于 src 目录及其子目录中。
- 文件名包含
.test.或.spec.。 - 支持 js/ts/vue 等扩展名。
- 指定测试文件匹配模式:
与 ESLint 中测试配置的关系
- ESLint 的测试规则块会为这些测试文件注入 Vitest 的全局变量,防止
describe等被报未定义。 - Vitest 配置中的 include 对测试执行范围负责,两者配合保证测试既能正确运行又能通过 lint 检查。
项目目录结构说明
本文档说明本项目主要目录的作用和推荐使用方式,便于团队成员快速理解和扩展。
目录结构总览
text
├─ src/
│ ├─ assets/
│ │ ├─ images/
│ │ └─ styles/
│ ├─ components/
│ │ ├─ common/
│ │ └─ business/
│ ├─ layouts/
│ ├─ router/
│ │ └─ modules/
│ ├─ services/
│ │ └─ modules/
│ ├─ stores/
│ │ └─ modules/
│ ├─ hooks/
│ ├─ utils/
│ ├─ types/
│ ├─ constants/
│ ├─ tests/
│ │ ├─ unit/
│ │ └─ components/
│ ├─ App.vue
│ └─ main.ts
├─ public/
├─ doc/
│ ├─ project-structure.md
│ └─ ...(其他配置/规范文档)
├─ package.json
├─ vite.config.ts / vite.config.js
├─ vitest.config.ts / vitest.config.js
├─ tsconfig*.json
├─ eslint.config.js
├─ tailwind.config.js
└─ .prettierrc
根目录
src/- 前端应用的主要源码目录。
public/- 静态公共资源目录,打包时会原样拷贝到构建结果中。
doc/- 项目文档目录(工程规范、配置说明、目录结构等)。
src 目录
src/main.ts- 应用入口文件,创建 Vue 应用实例,注册路由、状态管理、全局组件等。
src/App.vue- 根组件,一般只负责基本布局容器和路由出口等。
src/assets/- 静态资源:图片、图标、全局样式等。
- 建议按类型或业务拆分子目录,例如:
images/、styles/等。
src/components/- 可复用的通用组件。
- 可再划分:
common/(基础通用)、business/(跨页面业务组件)等。
src/views/- 页面级组件(通常与路由一一对应)。
- 每个页面一个目录,内部放该页面的子组件,例如:
views/user/List.vue、views/user/components/UserTable.vue。
src/layouts/- 布局组件:如后台管理系统的主框架(侧边栏 + 顶栏 + 内容区)、登录页布局等。
- 常见约定:
DefaultLayout.vue、AuthLayout.vue等。
src/router/- 路由相关配置。
- 一般包含:
index.ts(创建 router 实例)、按模块拆分的路由配置文件(如modules/user.ts)。
src/stores/- 状态管理(例如 Pinia)相关的 store 定义。
- 每个业务领域一个 store 文件,如:
userStore.ts、appStore.ts等。
src/services/- 与后端交互的服务层代码(API 请求封装)。
- 推荐:
request.ts:封装 Axios/Fetch,统一处理请求、响应、错误。- 按业务模块拆分 API 文件,如:
user.ts、auth.ts、system.ts。
src/utils/- 工具函数库,纯函数、与业务相对无关的通用逻辑。
- 可按功能再拆分:
date.ts、format.ts、validator.ts等。
src/hooks/- 组合式函数(Composition API Hooks),抽离可复用的状态逻辑。
- 命名建议以
use开头,例如:useRequest、usePagination、useDialog等。
src/types/- TypeScript 类型定义,接口类型、全局类型、枚举等。
- 可以按模块管理:
user.d.ts、auth.d.ts、api.d.ts等。
src/constants/- 项目中用到的常量定义,如枚举值、字典、配置项、路由名称常量等。
- 例如:
route-names.ts、storage-keys.ts、business.ts。
src/tests/- 单元测试与组件测试目录,使用 Vitest 运行。
- 推荐按类型再分:
tests/unit/(工具函数、逻辑单元)和tests/components/(组件相关测试)。 - 测试文件命名建议:
*.test.ts或*.spec.ts,Vitest 已在vitest.config.ts中配置include: ['src/**/*.{test,spec}.{js,ts,vue}'],会自动匹配。
常见子目录约定
以下为项目中推荐使用的一些二级子目录,实际可根据业务扩展或精简:
src/assets/images/- 存放图片、图标等静态资源,可按业务或功能再拆子目录。
src/assets/styles/- 全局样式、Tailwind 扩展、主题相关样式等。
src/components/common/- 基础通用组件,如按钮封装、表格封装、对话框封装等,可在多个业务模块中复用。
src/components/business/- 跨页面的业务组件,例如"用户选择器"、"部门树选择"等。
src/views/dashboard/- 仪表盘 / 概览页相关的页面组件。
src/router/modules/- 路由模块配置文件,按业务模块拆分,例如:
system.ts、user.ts、dashboard.ts。
- 路由模块配置文件,按业务模块拆分,例如:
src/stores/modules/- 按业务模块拆分的 store 文件,例如:
system.ts、user.ts、app.ts等。
- 按业务模块拆分的 store 文件,例如:
src/services/modules/- 按业务模块拆分的 API 服务文件,例如:
system.ts、user.ts、auth.ts等。
- 按业务模块拆分的 API 服务文件,例如:
约定与建议
- 页面(views)和业务组件的目录结构,尽量与路由、业务模块保持一致,便于查找和重构。
- 通用组件、hooks、utils、services 等尽量保持"可复用、低耦合",避免将具体页面逻辑写进去。
- 新增目录或模块时,优先考虑是否属于现有模块,尽量保持目录层级清晰、简洁。