Vue3 工程构建

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进行包管理和安装,

  1. pnpm 的 node_modules 布局使用符号链接来创建依赖项的嵌套结构。node_modules 中每个包的每个文件都是来自内容可寻址存储的硬链接。(避免重复安装
  2. pnpm 是默认支持 monorepo 多项目管理(多项目管理
  3. pnpm 使用链接仅将项目的直接依赖 项添加到模块目录的根目录中(幽灵依赖
bash 复制代码
npm i -g pnpm

项目生产依赖

在项目生产环境中的依赖,主要考虑项目性质以及UI设计:(由于vue3只能在现代浏览器下运行。所以应该从兼容现代浏览器的版本开始,不需要兼容ie版本)

  1. 项目使用vue3 应该配套使用状态管理库pinia 以及路由管理vue-router
  2. 项目组件库为ant-design-vue ,需要安装icon图标ant-design/icons-vue ,以及统一使用less作为css预处理器
  3. css工程化统一使用tailwindcsspostcssautoprefixer自动补齐css前缀。
  4. 使用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:合并 browsernodees2020 全局变量,避免误报。
  • 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 全局。
  • 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.jsontsconfig.node.json
  • 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 的全局(如 describeitexpect 等)。
  • 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> 内容进行缩进。
  • 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 使用不同的 printWidthproseWrap
      • *.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.jsontsconfig.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

    • 限制可用的提交类型,例如:featfixdocsstylerefactortestchoreperfbuildcirevertother 等。
  • type-case / type-empty

    • 类型必须小写,且不能为空。
  • subject-empty / subject-min-length / subject-max-length

    • 提交描述必填,长度在 3~100 字符之间,且不允许以句号结尾(subject-full-stop 规则)。
  • scope-enum

    • 可选的作用域列表,如 componentpagelayoutrouterstoreapiutilsstylestypesconfigdepsother 等,帮助约束"这个提交主要改了哪一类东西"。

日常使用建议

  • 开发过程中:
    • 经常本地执行 pnpm lintpnpm 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,不在此处压缩空白字符(交由构建工具处理)。

总结

  • 开发环境: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 风格。
  • 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,如 describeitexpect 等,无需手动 import。
  • 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.vueviews/user/components/UserTable.vue
  • src/layouts/
    • 布局组件:如后台管理系统的主框架(侧边栏 + 顶栏 + 内容区)、登录页布局等。
    • 常见约定:DefaultLayout.vueAuthLayout.vue 等。
  • src/router/
    • 路由相关配置。
    • 一般包含:index.ts(创建 router 实例)、按模块拆分的路由配置文件(如 modules/user.ts)。
  • src/stores/
    • 状态管理(例如 Pinia)相关的 store 定义。
    • 每个业务领域一个 store 文件,如:userStore.tsappStore.ts 等。
  • src/services/
    • 与后端交互的服务层代码(API 请求封装)。
    • 推荐:
      • request.ts:封装 Axios/Fetch,统一处理请求、响应、错误。
      • 按业务模块拆分 API 文件,如:user.tsauth.tssystem.ts
  • src/utils/
    • 工具函数库,纯函数、与业务相对无关的通用逻辑。
    • 可按功能再拆分:date.tsformat.tsvalidator.ts 等。
  • src/hooks/
    • 组合式函数(Composition API Hooks),抽离可复用的状态逻辑。
    • 命名建议以 use 开头,例如:useRequestusePaginationuseDialog 等。
  • src/types/
    • TypeScript 类型定义,接口类型、全局类型、枚举等。
    • 可以按模块管理:user.d.tsauth.d.tsapi.d.ts 等。
  • src/constants/
    • 项目中用到的常量定义,如枚举值、字典、配置项、路由名称常量等。
    • 例如:route-names.tsstorage-keys.tsbusiness.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.tsuser.tsdashboard.ts
  • src/stores/modules/
    • 按业务模块拆分的 store 文件,例如:system.tsuser.tsapp.ts 等。
  • src/services/modules/
    • 按业务模块拆分的 API 服务文件,例如:system.tsuser.tsauth.ts 等。

约定与建议

  • 页面(views)和业务组件的目录结构,尽量与路由、业务模块保持一致,便于查找和重构。
  • 通用组件、hooks、utils、services 等尽量保持"可复用、低耦合",避免将具体页面逻辑写进去。
  • 新增目录或模块时,优先考虑是否属于现有模块,尽量保持目录层级清晰、简洁。
相关推荐
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Vue的租房App为例,包含答辩的问题和答案
前端·javascript·vue.js
wuhen_n5 小时前
副作用的概念与effect基础:Vue3响应式系统的核心
前端·javascript·vue.js
张3蜂5 小时前
Vue.js-知识体系
前端·javascript·vue.js
wuhen_n5 小时前
effect函数的完整实现与追踪:深入Vue3响应式核心
前端·javascript·vue.js
滕青山6 小时前
HTML编码/解码 核心JS实现
前端·javascript·vue.js
RunsenLIu6 小时前
智慧房屋租赁管理系统
前端·javascript·vue.js
phltxy6 小时前
快速上手 ElementPlus:核心用法精讲
前端·javascript·vue.js
一直都在5726 小时前
SpringBoot+Vue+Netty+WebSocket+WebRTC 实现视频聊天
vue.js·spring boot·websocket