Skip to content

代码质量保障体系

介绍

代码质量保障体系是软件工程中确保代码质量、可维护性和团队协作效率的关键实践。RuoYi-Plus-UniApp 全栈项目采用了完整的代码质量保障工具链,通过自动化工具和标准化流程,在多个层面保证代码质量。

核心价值:

  • 统一代码风格 - 通过 ESLint、Prettier、EditorConfig 等工具统一团队代码风格,减少代码审查中的风格争议,提高代码可读性
  • 提前发现问题 - 在开发阶段通过静态分析工具自动检测潜在问题,包括语法错误、类型错误、代码异味等,降低生产环境故障率
  • 自动化检查 - 集成到 Git Hooks 和 CI/CD 流程中,在提交、推送、合并等关键节点自动执行质量检查,建立质量门禁
  • 提升开发效率 - 通过自动格式化、自动修复功能减少手动调整代码的时间,开发者可以专注于业务逻辑实现
  • 知识沉淀 - 通过规则配置和文档积累团队的最佳实践,新成员可以快速了解项目的代码规范和质量标准
  • 持续改进 - 通过质量度量指标监控代码质量趋势,识别问题模块,指导重构和优化工作

工具链架构:

代码质量保障体系
├── 编辑器层
│   └── EditorConfig - 统一编辑器基础配置(缩进、换行符等)
├── 代码规范层
│   ├── ESLint - JavaScript/TypeScript/Vue 代码规范检查
│   ├── Prettier - 代码格式化工具
│   └── TypeScript - 类型检查
├── 自动化层
│   ├── Git Hooks - 提交前自动检查
│   └── Lint-staged - 仅检查暂存区文件
├── 持续集成层
│   ├── GitHub Actions / GitLab CI - 自动化检查流程
│   └── 代码审查 - Pull Request 质量门禁
└── 度量分析层
    ├── 代码覆盖率 - 测试覆盖率统计
    └── 技术债务 - 代码质量趋势分析

项目覆盖范围:

项目ESLintPrettierEditorConfigTypeScript
plus-ui (管理端前端)
plus-uniapp (移动端)
plus-app (App端)
ruoyi-admin (后端)

技术栈版本:

工具plus-uiplus-uniapp
ESLint9.21.09.21.0
Prettier3.5.23.x
TypeScript~5.8.35.7.2
Vue3.5.133.4.21

EditorConfig 统一编辑器配置

介绍

EditorConfig 帮助开发团队在不同的编辑器和 IDE 之间维护一致的编码风格。它是代码质量保障的第一层,在编辑器层面统一基础配置。

核心优势:

  • 跨编辑器支持 - 支持几乎所有主流编辑器(VS Code、WebStorm、Sublime Text 等)
  • 优先级最高 - 编辑器直接读取,优先级高于编辑器自身配置
  • 配置简单 - 使用 INI 格式,配置项清晰易懂
  • 自动应用 - 打开文件时自动应用,无需手动触发

项目配置

项目根目录的 .editorconfig 文件:

ini
# http://editorconfig.org
root = true

# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true

[*.{json,yml,yaml}]
indent_size = 2

[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

配置说明:

配置项说明
roottrue标记为根配置文件,编辑器停止向上查找
indent_stylespace使用空格缩进而非制表符
indent_size4默认缩进4个空格(后端 Java 代码)
charsetutf-8文件编码为 UTF-8
end_of_linelf使用 Unix 风格换行符(LF)
trim_trailing_whitespacetrue自动删除行尾空白字符
insert_final_newlinetrue文件末尾插入空行

特殊文件类型:

ini
# JSON/YAML 使用2空格缩进(前端主流约定)
[*.{json,yml,yaml}]
indent_size = 2

# Markdown 保留尾部空白和换行(Markdown 语法需要)
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

编辑器支持

VS Code:

默认支持,无需插件。如需查看生效状态,查看状态栏:

  • 左下角显示: 空格: 4空格: 2
  • 换行符: LFCRLF

WebStorm/IntelliJ IDEA:

默认支持,设置路径:

Settings > Editor > Code Style > Enable EditorConfig support

验证配置:

  1. 打开任意 .java 文件,按 Tab 键应插入 4 个空格
  2. 打开任意 .json 文件,按 Tab 键应插入 2 个空格
  3. 保存文件时自动删除行尾空白字符

Prettier 代码格式化

介绍

Prettier 是一个代码格式化工具,支持 JavaScript、TypeScript、Vue、CSS、JSON 等多种语言。它通过固定的格式化规则消除团队中的代码风格争议。

核心特性:

  • 固定格式 - 格式化规则固定,不提供过多配置项,减少团队讨论成本
  • 自动格式化 - 保存时自动格式化,或通过命令行批量格式化
  • 广泛支持 - 支持 20+ 种编程语言和标记语言
  • 集成简单 - 与 ESLint、编辑器、Git Hooks 无缝集成

plus-ui 配置

管理端前端 .prettierrc.js 配置:

javascript
// Prettier 配置文件
// 详细文档:https://prettier.io/docs/en/options.html

export default {
  // 单行最大宽度,超过此宽度的行将被换行
  // 设置为150字符,适合宽屏显示器
  printWidth: 150,

  // 缩进宽度,使用多少个空格表示一个缩进级别
  // 设置为标准的2个空格
  tabWidth: 2,

  // 是否使用制表符(tab)缩进
  // false表示使用空格而不是制表符
  useTabs: false,

  // 每行结束是否添加分号
  // false = 不添加分号(依赖 ASI - 自动分号插入)
  // 现代 JavaScript 趋势,代码更简洁
  semi: false,

  // 是否使用单引号
  // true表示优先使用单引号而不是双引号
  singleQuote: true,

  // 对象属性名是否使用引号
  // "preserve"表示保持原样,不强制添加或删除引号
  quoteProps: 'preserve',

  // JSX中是否使用单引号
  // false表示在JSX中使用双引号(即使singleQuote为true)
  jsxSingleQuote: false,

  // 多行结构的闭合括号是否与最后一行在同一行
  // false表示闭合括号另起一行
  bracketSameLine: false,

  // 对象、数组等结构中最后一项后是否添加逗号
  // "none"表示不添加尾随逗号
  trailingComma: 'none',

  // 括号内部是否添加空格
  // true表示在大括号内添加空格,如{ foo: bar }
  bracketSpacing: true,

  // 嵌入的代码块格式化方式
  // "auto"表示自动检测并格式化嵌入的代码
  embeddedLanguageFormatting: 'auto',

  // 箭头函数参数是否总是用括号包裹
  // "always"表示即使只有一个参数也使用括号,如(x) => x
  arrowParens: 'always',

  // 是否仅格式化包含特定注释的文件
  // false表示格式化所有文件,不需要特定的格式化注释
  requirePragma: false,

  // 是否在格式化后的文件顶部插入特定注释
  // false表示不添加特定注释
  insertPragma: false,

  // 如何处理markdown等文本的换行
  // "preserve"表示保持原文本的换行方式不变
  proseWrap: 'preserve',

  // HTML空白符敏感度
  // "css"表示根据CSS display属性决定空白处理方式
  htmlWhitespaceSensitivity: 'css',

  // 是否缩进Vue文件中的<script>和<style>标签
  // false表示不缩进这些标签内部的代码
  vueIndentScriptAndStyle: false,

  // 行尾换行符类型
  // "auto"表示保留文件原有的换行符类型
  endOfLine: 'auto'
}

plus-uniapp 配置

移动端 .prettierrc.cjs 配置:

javascript
// Prettier 代码格式化配置文件
// @see https://prettier.io/docs/en/options

module.exports = {
  // 使用单引号而不是双引号
  // 示例:'hello' 而不是 "hello"
  // 减少视觉噪音,在 JavaScript 中更常见
  singleQuote: true,

  // 单行最大字符数,超过会自动换行
  // 100 字符是现代编辑器的较好平衡点
  // 相比默认的 80 字符,能减少不必要的换行
  printWidth: 100,

  // 每行结束是否添加分号
  // false = 不添加分号(依赖 ASI - 自动分号插入)
  // 现代 JavaScript 趋势,代码更简洁
  semi: false,

  // 行尾换行符处理方式
  // 'auto' = 让 Prettier 自动检测并保持现有文件的换行符
  // 避免跨平台开发时的换行符冲突问题
  endOfLine: 'auto',

  // 缩进宽度(空格数)
  // 2 个空格是前端开发的主流选择
  tabWidth: 2,

  // 是否使用制表符(Tab)进行缩进
  // false = 使用空格缩进,更一致跨编辑器显示
  useTabs: false,

  // 尾随逗号配置
  // 'all' = 在所有可能的地方添加尾随逗号
  // 好处:版本控制时 diff 更清晰,添加新项时不需要修改上一行
  // 示例:{ a: 1, b: 2, } 和 [1, 2, 3,]
  trailingComma: 'all',

  // HTML 空白字符敏感度
  // 'ignore' = 忽略 HTML 中的空白字符格式化
  // 避免在 Vue 模板或 HTML 中出现意外的空白字符变化
  // 对于组件库开发特别重要
  htmlWhitespaceSensitivity: 'ignore',

  // 针对 JSON 文件的特殊配置
  overrides: [
    {
      files: '*.json',
      options: {
        // JSON 文件不使用尾随逗号
        // JSON 标准不支持尾随逗号,会导致解析错误
        trailingComma: 'none',
      },
    },
  ],
}

配置差异对比

配置项plus-uiplus-uniapp说明
printWidth150100plus-ui 适配宽屏,plus-uniapp 移动端代码较短
trailingComma'none''all'plus-uniapp 使用现代 ES 规范,支持尾随逗号
quoteProps'preserve'默认plus-ui 保留对象属性引号原样
jsxSingleQuotefalse默认plus-ui 明确 JSX 使用双引号
bracketSameLinefalse默认plus-ui 明确闭合括号换行
arrowParens'always'默认plus-ui 强制箭头函数参数使用括号
htmlWhitespaceSensitivity'css''ignore'plus-uniapp 忽略 HTML 空白,避免 UniApp 组件问题

使用方式

1. 命令行格式化

bash
# 格式化所有文件
pnpm prettier

# 等价于
pnpm prettier --write .

# 检查文件格式(不修改)
pnpm prettier --check .

# 格式化指定文件
prettier --write src/components/Button.vue

# 格式化指定目录
prettier --write src/components/**/*.vue

2. VS Code 集成

安装插件:

Prettier - Code formatter (esbenp.prettier-vscode)

配置 .vscode/settings.json:

json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

3. 保存时自动格式化

VS Code 用户设置:

File > Preferences > Settings
搜索 "Format On Save"
勾选 "Editor: Format On Save"

忽略文件

创建 .prettierignore 文件:

# 构建产物
dist
dist-ssr
*.local

# 依赖
node_modules
.pnpm-store

# 自动生成文件
auto-imports.d.ts
components.d.ts
uni-pages.d.ts

# 配置文件
.env
.env.local
.env.*.local

# 锁定文件
pnpm-lock.yaml
package-lock.json

ESLint 代码规范检查

介绍

ESLint 是 JavaScript 生态系统中最流行的静态代码分析工具,用于识别和报告代码中的模式和问题。本项目使用 ESLint 9.x 扁平化配置(Flat Config)。

核心功能:

  • 代码质量检查 - 检测潜在错误、代码异味、不安全的用法
  • 代码风格统一 - 强制执行团队约定的代码风格
  • 自动修复 - 部分规则支持自动修复,提高开发效率
  • 可扩展性 - 通过插件支持 TypeScript、Vue、React 等框架
  • 渐进式采用 - 可以逐步启用规则,不影响现有代码

plus-ui ESLint 配置

管理端前端 eslint.config.ts:

typescript
import pluginVue from 'eslint-plugin-vue'

// 导入全局变量定义
import globals from 'globals'

// 导入 Prettier ESLint 插件,用于代码格式化
import prettier from 'eslint-plugin-prettier'

// 导入 Vue TypeScript 配置辅助函数和预设配置
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'

// 导入跳过 Prettier 格式化的配置
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

// 使用 Vue TypeScript 配置辅助函数定义 ESLint 配置
export default defineConfigWithVueTs(
  // 指定需要检查的文件类型
  {
    name: 'app/files-to-lint',
    // 检查 JavaScript、TypeScript 和 Vue 文件
    files: ['**/*.{js,cjs,ts,mts,tsx,vue}']
  },

  // 指定需要忽略的文件和目录
  {
    name: 'app/files-to-ignore',
    ignores: [
      // 忽略构建输出目录
      '**/dist/**',
      // 忽略 SSR 构建输出目录
      '**/dist-ssr/**',
      // 忽略测试覆盖率报告目录
      '**/coverage/**',
      // 忽略所有语言文件
      '**/locales/**/*.ts'
    ]
  },

  // 配置语言选项
  {
    languageOptions: {
      // 添加浏览器环境的全局变量(如 window, document 等)
      globals: globals.browser
    }
  },

  // 使用 Vue 插件的基本规则配置 - Vue 的基本规则集,检查常见错误
  pluginVue.configs['flat/essential'],

  // 使用 Vue TypeScript 推荐配置 - TypeScript 的推荐规则集
  vueTsConfigs.recommended,

  // 使用 Prettier 配置但跳过格式化相关规则(避免与 ESLint 规则冲突)
  skipFormatting,

  // 自定义规则配置
  {
    // 启用的插件
    plugins: {
      // 启用 Prettier 插件
      prettier
    },

    // 自定义规则设置
    rules: {
      // ================================
      // TypeScript 相关规则
      // ================================

      // 允许空函数
      '@typescript-eslint/no-empty-function': 'off',

      // 允许使用 any 类型
      '@typescript-eslint/no-explicit-any': 'off',

      // 关闭未使用变量的警告
      '@typescript-eslint/no-unused-vars': 'off',

      // 允许将 this 赋值给变量
      '@typescript-eslint/no-this-alias': 'off',

      // 允许使用空对象类型 {}
      '@typescript-eslint/no-empty-object-type': 'off',

      // 允许未使用的表达式
      '@typescript-eslint/no-unused-expressions': 'off',

      // 允许单词组件名
      'vue/multi-word-component-names': 'off',

      // 关闭 defineProps 验证
      'vue/valid-define-props': 'off',

      // 允许 v-model 带参数
      'vue/no-v-model-argument': 'off',

      // 允许使用 arguments 对象
      'prefer-rest-params': 'off',

      // 强制代码符合 Prettier 格式
      'prettier/prettier': 'error'
    }
  }
)

配置解析:

  1. 文件检查范围:

    • 包含: **/*.{js,cjs,ts,mts,tsx,vue}
    • 排除: dist/dist-ssr/coverage/locales/
  2. 预设配置:

    • pluginVue.configs['flat/essential'] - Vue 基础规则
    • vueTsConfigs.recommended - TypeScript 推荐规则
    • skipFormatting - 跳过 Prettier 格式化规则
  3. 自定义规则:

    • 放宽 TypeScript 严格性(允许 any、空函数等)
    • 放宽 Vue 规范性(允许单词组件名、defineProps)
    • 集成 Prettier(强制执行格式化)

plus-uniapp ESLint 配置

移动端 eslint.config.mjs(部分):

javascript
// ESLint 配置文件 - 基于 @uni-helper/eslint-config,专门为 uni-app 项目优化
import uniHelper from '@uni-helper/eslint-config'

export default uniHelper({
  // ===== 功能开关配置 =====

  // 启用 UnoCSS 支持,提供 UnoCSS 相关的 ESLint 规则和自动修复
  unocss: true,

  // 启用 Vue 3 支持,包含 Vue 3 组合式 API、SFC 等相关规则
  vue: true,

  // 禁用 Markdown 文件检查,uni-app 项目通常不需要对 .md 文件进行 lint
  markdown: false,

  // ===== 忽略文件配置 =====
  ignores: [
    // uni-app 插件目录,包含第三方插件,不需要检查
    'src/uni_modules/',

    // 构建输出目录
    'dist',

    // unplugin-auto-import 生成的类型文件,每次自动导入变化时都会重新生成
    'auto-imports.d.ts',

    // vite-plugin-uni-pages 生成的类型文件,根据页面结构动态生成
    'uni-pages.d.ts',

    // 插件自动生成的配置文件
    'src/pages.json', // 页面路由配置
    'src/manifest.json', // 应用配置清单
  ],

  // ===== 自定义规则配置 =====
  rules: {
    // 允许使用 console 语句,开发环境下经常需要调试输出
    'no-console': 'off',

    // 关闭未使用变量检查,开发过程中可能临时定义变量
    'no-unused-vars': 'off',

    // 允许未使用的 ref 引用,Vue 3 中的 ref 可能在模板中使用但代码中未直接引用
    'vue/no-unused-refs': 'off',

    // 关闭 unused-imports 插件的未使用变量检查,避免与其他规则冲突
    'unused-imports/no-unused-vars': 'off',

    // 允许无限制的 eslint-disable 注释,在某些复杂场景下可能需要大范围禁用规则
    'eslint-comments/no-unlimited-disable': 'off',

    // 关闭参数名称检查,JSDoc 参数名与实际参数名不一致时不报错
    'jsdoc/check-param-names': 'off',

    // 不强制要求返回值描述,简单函数的返回值可能不需要详细描述
    'jsdoc/require-returns-description': 'off',

    // 允许空对象类型,TypeScript 中有时需要定义空接口作为基础类型
    'ts/no-empty-object-type': 'off',

    // 允许扩展原生对象,某些情况下可能需要为原生对象添加方法(谨慎使用)
    'no-extend-native': 'off',

    // 关闭 Vue 块顺序检查
    'vue/block-order': 'off',

    // 关闭函数风格配置
    'func-style': 'off',
    'antfu/top-level-function': 'off',

    // 关闭 Vue 3 define-macros 的顺序检查
    'vue/define-macros-order': 'off',

    // 关闭代码风格检查(由 Prettier 处理)
    'style/brace-style': 'off',
    'style/quote-props': 'off',
    'style/operator-linebreak': 'off',
    'antfu/if-newline': 'off',
    'style/arrow-parens': 'off',
    'style/indent-binary-ops': 'off',
    'vue/operator-linebreak': 'off',
    'vue/html-indent': 'off',
    'style/indent': 'off',
    'style/member-delimiter-style': 'off',
    'style/comma-dangle': 'off',
    'format/prettier': 'off',
    'vue/singleline-html-element-content-newline': 'off',
    'perfectionist/sort-named-imports': 'off',
    'perfectionist/sort-imports': 'off',
  },

  // ===== 格式化配置 =====
  formatters: {
    // 启用 CSS 格式化,格式化 CSS、LESS、SCSS 文件及 Vue 文件中的 <style> 块
    css: true,

    // 启用 HTML 格式化,格式化 HTML 文件和 Vue 文件中的模板部分
    html: true,
  },
})

配置特点:

  1. 基于 @uni-helper/eslint-config:

    • 预配置了 UniApp 项目常用规则
    • 集成了 Vue 3、TypeScript、UnoCSS 支持
  2. 宽松的规则:

    • 允许 console、未使用变量(开发阶段更灵活)
    • 关闭大部分代码风格规则(交给 Prettier)
  3. UniApp 特定优化:

    • 忽略 uni_modules/ 第三方插件
    • 忽略自动生成的 pages.jsonmanifest.json

使用方式

1. 命令行检查

bash
# 检查所有文件
pnpm lint:eslint

# 等价于
pnpm eslint --max-warnings=0 --timeout=60000

# 自动修复
pnpm lint:eslint:fix

# 等价于
pnpm eslint --fix --timeout=60000

# 检查指定文件
eslint src/components/Button.vue

# 检查指定目录
eslint src/components/**/*.vue

2. VS Code 集成

安装插件:

ESLint (dbaeumer.vscode-eslint)

配置 .vscode/settings.json:

json
{
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue"
  ],
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.format.enable": false
}

3. 保存时自动修复

VS Code 会在保存时自动执行 ESLint 修复(配合上述设置)。

ESLint 规则级别

级别说明效果
"off"0关闭规则不检查
"warn"1警告级别显示警告但不阻止构建
"error"2错误级别显示错误并阻止构建

示例:

javascript
rules: {
  // 关闭规则
  'no-console': 'off',

  // 警告级别
  'no-debugger': 'warn',

  // 错误级别
  'prettier/prettier': 'error',

  // 带选项的规则
  'semi': ['error', 'never'],
  'quotes': ['error', 'single', { 'avoidEscape': true }]
}

TypeScript 类型检查

介绍

TypeScript 提供静态类型检查,在编译时发现类型错误。本项目前端和移动端都使用 TypeScript 开发。

核心优势:

  • 类型安全 - 编译时发现类型错误,减少运行时错误
  • 智能提示 - IDE 提供精准的代码补全和错误提示
  • 重构支持 - 类型系统支持安全重构
  • 文档作用 - 类型定义即文档,提高代码可读性

TypeScript 配置

plus-ui tsconfig.json(核心配置):

json
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "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/**/*.tsx", "src/**/*.vue"],
  "exclude": ["node_modules", "dist"]
}

plus-uniapp tsconfig.json(核心配置):

json
{
  "extends": "@vue/tsconfig/tsconfig.json",
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "preserve",
    "jsxImportSource": "vue",
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "types": ["@dcloudio/types"],

    /* 路径别名 */
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@wd/*": ["src/wd/*"]
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "src/**/*.d.ts"
  ],
  "exclude": ["node_modules", "dist"]
}

类型检查命令

bash
# 类型检查
pnpm type-check

# 等价于
pnpm vue-tsc --noEmit

# 观察模式(实时检查)
pnpm vue-tsc --noEmit --watch

常见类型错误

1. 缺少类型定义

typescript
// ❌ 错误:隐式 any 类型
function add(a, b) {
  return a + b
}

// ✅ 正确:显式类型
function add(a: number, b: number): number {
  return a + b
}

2. 属性不存在

typescript
// ❌ 错误:属性 'name' 在类型 'User' 上不存在
interface User {
  id: number
}
const user: User = { id: 1 }
console.log(user.name) // 错误

// ✅ 正确:添加属性定义
interface User {
  id: number
  name: string
}
const user: User = { id: 1, name: 'John' }
console.log(user.name) // 正确

3. 类型不匹配

typescript
// ❌ 错误:类型 'string' 不能赋值给类型 'number'
const age: number = '18' // 错误

// ✅ 正确:类型一致
const age: number = 18 // 正确

自动化检查流程

Git Hooks

Git Hooks 是 Git 提供的钩子机制,可以在特定的 Git 操作前后执行自定义脚本。常用的 Hooks 工具有 Husky 和 simple-git-hooks。

常用 Hooks:

Hook触发时机常见用途
pre-commit执行 git commit检查代码格式、ESLint 检查
commit-msg提交信息写入后验证提交信息格式
pre-push执行 git push运行测试、类型检查

Husky 配置示例

安装:

bash
pnpm add -D husky
pnpm husky install

配置 package.json:

json
{
  "scripts": {
    "prepare": "husky install"
  }
}

创建 pre-commit Hook:

bash
npx husky add .husky/pre-commit "pnpm lint-staged"

生成 .husky/pre-commit 文件:

bash
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm lint-staged

Lint-staged

Lint-staged 仅对暂存区的文件执行检查,提高检查速度。

安装:

bash
pnpm add -D lint-staged

配置 package.json:

json
{
  "lint-staged": {
    "*.{js,ts,jsx,tsx,vue}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss,less,vue}": [
      "prettier --write"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  }
}

工作流程:

  1. 开发者执行 git commit
  2. Git 触发 pre-commit Hook
  3. Husky 执行 lint-staged
  4. Lint-staged 对暂存区文件执行 ESLint 和 Prettier
  5. 如果检查失败,提交被阻止
  6. 如果检查通过,提交成功

Commitlint 提交信息规范

安装:

bash
pnpm add -D @commitlint/cli @commitlint/config-conventional

配置 commitlint.config.js:

javascript
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',     // 新功能
        'fix',      // 修复 bug
        'docs',     // 文档变更
        'style',    // 代码格式(不影响代码运行的变动)
        'refactor', // 重构
        'perf',     // 性能优化
        'test',     // 测试
        'build',    // 构建系统或外部依赖的变更
        'ci',       // CI 配置文件和脚本的变更
        'chore',    // 其他不修改 src 或测试文件的变更
        'revert'    // 回滚 commit
      ]
    ],
    'subject-case': [0] // 允许任意大小写
  }
}

创建 commit-msg Hook:

bash
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

提交信息格式:

<type>(<scope>): <subject>

<body>

<footer>

示例:

feat(user): 添加用户登录功能

- 实现登录表单
- 添加登录 API
- 集成 JWT 认证

Closes #123

CI/CD 集成

GitHub Actions 配置

创建 .github/workflows/lint.yml:

yaml
name: Lint

on:
  push:
    branches: [main, dev]
  pull_request:
    branches: [main, dev]

jobs:
  lint:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run ESLint
        run: pnpm lint:eslint

      - name: Run Prettier check
        run: pnpm prettier --check .

      - name: Run TypeScript check
        run: pnpm type-check

GitLab CI 配置

创建 .gitlab-ci.yml:

yaml
image: node:18

stages:
  - lint
  - test
  - build

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .pnpm-store

before_script:
  - corepack enable
  - pnpm config set store-dir .pnpm-store
  - pnpm install --frozen-lockfile

lint:eslint:
  stage: lint
  script:
    - pnpm lint:eslint
  only:
    - merge_requests
    - main
    - dev

lint:prettier:
  stage: lint
  script:
    - pnpm prettier --check .
  only:
    - merge_requests
    - main
    - dev

lint:typescript:
  stage: lint
  script:
    - pnpm type-check
  only:
    - merge_requests
    - main
    - dev

质量门禁策略

1. Pull Request 门禁

要求 PR 合并前必须通过:

  • ESLint 检查无错误
  • Prettier 格式检查通过
  • TypeScript 类型检查通过
  • 代码审查至少 1 人通过

2. 分支保护规则

GitHub 设置:

Settings > Branches > Branch protection rules

✅ Require status checks to pass before merging
  ✅ lint-eslint
  ✅ lint-prettier
  ✅ lint-typescript

✅ Require pull request reviews before merging
  Minimum: 1

GitLab 设置:

Settings > Repository > Protected Branches

Allowed to merge: Developers + Maintainers
Allowed to push: No one
Require approval: 1

代码质量度量

质量指标

指标说明目标值工具
ESLint 错误数ESLint 报告的错误数量0ESLint
ESLint 警告数ESLint 报告的警告数量< 10ESLint
代码覆盖率测试覆盖的代码比例> 80%Vitest
类型覆盖率TypeScript 类型覆盖比例> 90%TypeScript
代码重复率重复代码占比< 5%jscpd
圈复杂度函数复杂度< 10ESLint complexity

ESLint 报告

生成 HTML 报告:

bash
# 安装报告生成器
pnpm add -D eslint-formatter-html

# 生成报告
pnpm eslint src --format html --output-file eslint-report.html

生成 JSON 报告:

bash
pnpm eslint src --format json --output-file eslint-report.json

代码覆盖率

Vitest 配置 vitest.config.ts:

typescript
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      exclude: [
        'node_modules/',
        'dist/',
        '**/*.spec.ts',
        '**/*.test.ts'
      ],
      thresholds: {
        statements: 80,
        branches: 80,
        functions: 80,
        lines: 80
      }
    }
  }
})

运行覆盖率测试:

bash
pnpm vitest run --coverage

持续监控

1. 趋势分析

定期收集质量指标,绘制趋势图:

bash
# 每周统计 ESLint 错误数
pnpm eslint src --format json | jq '.[] | .errorCount' | awk '{s+=$1} END {print s}'

2. 质量仪表板

使用 SonarQube 或 Code Climate 等工具建立质量仪表板。

SonarQube 配置 sonar-project.properties:

properties
sonar.projectKey=ruoyi-plus-uniapp
sonar.projectName=RuoYi-Plus-UniApp
sonar.projectVersion=5.5.0
sonar.sources=src
sonar.exclusions=**/node_modules/**,**/dist/**
sonar.typescript.lcov.reportPaths=coverage/lcov.info

运行扫描:

bash
sonar-scanner

最佳实践

1. 渐进式采用

问题: 老项目引入 ESLint 可能产生大量错误,影响开发进度。

方案:

javascript
// eslint.config.js
export default [
  {
    rules: {
      // 第一阶段:仅启用错误级别规则
      'no-undef': 'error',
      'no-unused-vars': 'error',

      // 第二阶段:将警告规则升级为错误
      // 'no-console': 'error',

      // 第三阶段:启用所有推荐规则
      // ...推荐配置
    }
  }
]

步骤:

  1. 第1周:启用基础错误规则,修复全部错误
  2. 第2-3周:启用警告规则,逐步修复警告
  3. 第4周:启用完整推荐配置
  4. 持续:根据团队反馈调整规则

2. 自定义规则配置

根据项目特点调整规则:

javascript
// plus-ui 项目特点:
// - 管理端,允许使用 console 调试
// - 使用 any 类型降低迁移成本
// - 单词组件名(如 Menu、Table)

rules: {
  'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  '@typescript-eslint/no-explicit-any': 'off',
  'vue/multi-word-component-names': 'off'
}

3. 忽略文件合理配置

原则:

  • 自动生成的文件必须忽略
  • 第三方代码必须忽略
  • 测试覆盖率报告必须忽略
javascript
// eslint.config.js
ignores: [
  '**/dist/**',
  '**/node_modules/**',
  '**/coverage/**',
  '**/*.d.ts',
  '**/auto-imports.d.ts',
  '**/components.d.ts'
]

4. 规则禁用注释

临时禁用规则(谨慎使用):

javascript
// 禁用下一行
// eslint-disable-next-line no-console
console.log('调试信息')

// 禁用整个文件
/* eslint-disable no-console */
console.log('文件级别禁用')

// 禁用代码块
/* eslint-disable no-console */
console.log('代码块禁用')
/* eslint-enable no-console */

// 禁用特定规则并说明原因
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 第三方库返回类型为 any
const data: any = await fetchData()

5. 团队规范同步

确保团队成员使用相同配置:

json
// .vscode/settings.json (提交到仓库)
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "typescript",
    "vue"
  ]
}

// .vscode/extensions.json (推荐插件)
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "Vue.volar"
  ]
}

6. 性能优化

ESLint 检查慢的解决方案:

javascript
// eslint.config.js
export default [
  {
    // 1. 减少检查文件范围
    files: ['src/**/*.{js,ts,vue}'],

    // 2. 增加忽略目录
    ignores: [
      '**/dist/**',
      '**/node_modules/**',
      '**/*.min.js'
    ]
  }
]

命令行优化:

bash
# 使用缓存(默认启用)
pnpm eslint --cache src

# 增加超时时间(大项目)
pnpm eslint --timeout 60000 src

# 并行检查(实验性)
pnpm eslint --max-warnings 0 src

7. 代码审查清单

在 Code Review 时检查:

  • [ ] 代码符合 ESLint 规范
  • [ ] 代码已通过 Prettier 格式化
  • [ ] 没有被注释掉的代码
  • [ ] 没有 console.logdebugger
  • [ ] TypeScript 类型定义完整
  • [ ] 变量和函数命名语义化
  • [ ] 复杂逻辑有注释说明
  • [ ] 没有硬编码的配置项

常见问题

1. ESLint 和 Prettier 冲突

问题描述:

ESLint 报告格式错误,但 Prettier 格式化后又产生新的 ESLint 错误,形成循环冲突。

原因分析:

  • ESLint 的格式化规则与 Prettier 的格式化规则不一致
  • 常见冲突规则:semiquotesindentcomma-dangle

解决方案:

方案1:使用 eslint-config-prettier(推荐)

bash
pnpm add -D eslint-config-prettier
javascript
// eslint.config.js
import prettier from 'eslint-config-prettier'

export default [
  // 其他配置...
  prettier, // 放在最后,关闭所有与 Prettier 冲突的规则
]

方案2:使用 @vue/eslint-config-prettier(Vue 项目)

javascript
// eslint.config.js
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

export default [
  // 其他配置...
  skipFormatting, // 跳过格式化相关规则
]

验证冲突:

bash
# 检查 ESLint 配置中是否有与 Prettier 冲突的规则
npx eslint-config-prettier src/main.ts

2. TypeScript 类型错误但代码正常运行

问题描述:

TypeScript 报告类型错误,但代码在浏览器中正常运行。

原因分析:

  • TypeScript 是编译时检查,JavaScript 是运行时执行
  • 类型错误不影响代码运行,但可能导致潜在问题

解决方案:

情况1:第三方库缺少类型定义

bash
# 安装类型定义包
pnpm add -D @types/lodash

如果没有官方类型定义,创建 src/types/lodash.d.ts:

typescript
declare module 'lodash' {
  export function debounce<T extends (...args: any[]) => any>(
    func: T,
    wait?: number
  ): T
}

情况2:动态属性访问

typescript
// ❌ 错误:元素隐式具有 "any" 类型
const obj: Record<string, string> = { name: 'John' }
console.log(obj[key]) // key 是动态的

// ✅ 方案1:使用类型断言
console.log((obj as Record<string, string>)[key])

// ✅ 方案2:使用索引签名
interface User {
  [key: string]: string
  name: string
}

情况3:宽松 any 类型

typescript
// 临时方案:使用 any(不推荐)
const data: any = await fetchData()

// 推荐方案:定义准确的类型
interface ApiResponse {
  code: number
  data: User[]
  message: string
}
const data: ApiResponse = await fetchData()

3. Git Hooks 不生效

问题描述:

配置了 Husky 和 Lint-staged,但 git commit 时没有执行检查。

原因分析:

  • Husky 未正确安装
  • Hook 文件权限问题
  • Git 版本过低

解决方案:

步骤1:检查 Husky 安装

bash
# 查看 .husky 目录是否存在
ls -la .husky

# 重新安装 Husky
pnpm husky install

步骤2:检查 Hook 文件权限

bash
# 赋予执行权限
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg

步骤3:手动测试 Hook

bash
# 手动执行 pre-commit
.husky/pre-commit

# 如果报错,检查脚本内容是否正确
cat .husky/pre-commit

步骤4:检查 Git 配置

bash
# 查看 Git Hooks 路径
git config core.hooksPath

# 应输出:.husky

# 如果不正确,重新设置
git config core.hooksPath .husky

步骤5:跳过 Hooks(紧急情况)

bash
# 临时跳过 pre-commit
git commit --no-verify -m "紧急修复"

# 不推荐经常使用,应该修复 Hooks 问题

4. ESLint 检查速度慢

问题描述:

项目文件较多时,ESLint 检查耗时过长,影响开发体验。

原因分析:

  • 检查文件范围过大
  • 没有使用缓存
  • 规则过于复杂

解决方案:

方案1:启用缓存

bash
# ESLint 默认启用缓存,缓存文件:.eslintcache

# 查看缓存是否生效
pnpm eslint --cache --cache-location .eslintcache src

# 清除缓存(如果缓存损坏)
rm -f .eslintcache

方案2:减少检查范围

javascript
// eslint.config.js
export default [
  {
    files: ['src/**/*.{js,ts,vue}'], // 仅检查 src 目录
    ignores: [
      '**/dist/**',
      '**/node_modules/**',
      '**/*.min.js',
      '**/vendor/**' // 排除第三方代码
    ]
  }
]

方案3:仅检查变更文件

bash
# 使用 Lint-staged 仅检查暂存区文件
pnpm lint-staged

# 使用 Git 获取变更文件
git diff --name-only --diff-filter=ACMR | grep -E '\.(js|ts|vue)$' | xargs eslint

方案4:调整规则

javascript
// 禁用耗时的规则
rules: {
  // 禁用 import 排序(耗时)
  'import/order': 'off',

  // 禁用复杂度检查(耗时)
  'complexity': 'off'
}

方案5:使用 ESLint 扁平化配置(Flat Config)

ESLint 9.x 的扁平化配置性能更好:

javascript
// eslint.config.js (新格式,性能更好)
export default [
  // 配置...
]

// .eslintrc.js (旧格式,性能较差)
module.exports = {
  // 配置...
}

5. Prettier 格式化破坏代码

问题描述:

Prettier 格式化后,代码逻辑被破坏或格式不符合预期。

原因分析:

  • Prettier 固定格式可能与个人习惯不一致
  • 某些特殊情况需要保留原格式

解决方案:

方案1:调整 Prettier 配置

javascript
// .prettierrc.js
module.exports = {
  // 增加单行宽度,减少换行
  printWidth: 120,

  // 保留尾随逗号
  trailingComma: 'es5',

  // 使用单引号
  singleQuote: true
}

方案2:使用格式化忽略注释

javascript
// prettier-ignore
const matrix = [
  [1, 0, 0],
  [0, 1, 0],
  [0, 0, 1]
]

// prettier-ignore
const longString = "这是一段很长的字符串,Prettier 会自动换行,但我想保持单行"

方案3:忽略特定文件

# .prettierignore
# 第三方库
src/vendor/**

# 自动生成文件
src/assets/icons/**

# 特殊格式文件
src/data/config.json

方案4:保留 HTML 空白

javascript
// .prettierrc.js
module.exports = {
  // 忽略 HTML 空白,避免 Vue 模板格式化问题
  htmlWhitespaceSensitivity: 'ignore'
}

6. CI/CD 检查失败但本地通过

问题描述:

本地执行 pnpm lint:eslint 通过,但 CI/CD 流程中 ESLint 检查失败。

原因分析:

  • Node 版本不一致
  • 依赖版本不一致
  • 缓存问题

解决方案:

步骤1:统一 Node 版本

在 CI/CD 配置中指定 Node 版本:

yaml
# .github/workflows/lint.yml
- name: Setup Node.js
  uses: actions/setup-node@v3
  with:
    node-version: '18.18.0' # 与本地一致

步骤2:锁定依赖版本

bash
# 使用 pnpm lockfile
pnpm install --frozen-lockfile
yaml
# CI/CD 配置
- name: Install dependencies
  run: pnpm install --frozen-lockfile # 严格按照 lockfile 安装

步骤3:清除缓存

bash
# 本地清除缓存
rm -rf node_modules .eslintcache
pnpm install

# CI/CD 清除缓存(GitHub Actions)
- name: Clear cache
  run: |
    rm -rf node_modules
    rm -f .eslintcache

步骤4:检查环境变量

bash
# 本地和 CI 可能使用不同的环境变量
# CI 通常使用 production 环境

# package.json
"lint:eslint": "NODE_ENV=development eslint src"

步骤5:本地模拟 CI 环境

bash
# 使用 Docker 模拟 CI 环境
docker run --rm -v $(pwd):/app -w /app node:18 sh -c "
  corepack enable &&
  pnpm install --frozen-lockfile &&
  pnpm lint:eslint
"

7. ESLint 规则过于严格

问题描述:

ESLint 规则过于严格,影响开发效率,大量合理的代码被标记为错误。

原因分析:

  • 使用了过于严格的预设配置
  • 团队实际需求与预设规则不匹配

解决方案:

步骤1:识别高频错误规则

bash
# 统计 ESLint 错误规则
pnpm eslint src --format json | jq '.[].messages[] | .ruleId' | sort | uniq -c | sort -rn | head -20

步骤2:调整规则级别

javascript
// eslint.config.js
rules: {
  // 将错误降级为警告
  'no-console': 'warn', // 从 'error' 改为 'warn'

  // 关闭不适用的规则
  '@typescript-eslint/no-explicit-any': 'off',

  // 自定义规则选项
  'max-len': ['warn', { code: 120, ignoreStrings: true }]
}

步骤3:逐步启用规则

javascript
// 第一阶段:仅启用错误级别规则
rules: {
  'no-undef': 'error',
  'no-unused-vars': 'error'
}

// 第二阶段:添加警告级别规则
rules: {
  'no-undef': 'error',
  'no-unused-vars': 'error',
  'no-console': 'warn' // 新增
}

// 第三阶段:升级警告为错误
rules: {
  'no-undef': 'error',
  'no-unused-vars': 'error',
  'no-console': 'error' // 升级
}

步骤4:团队投票决定

在团队会议中讨论争议规则,投票决定是否启用:

规则说明赞成反对结果
no-console禁止 console32启用(warn)
@typescript-eslint/no-explicit-any禁止 any14关闭

总结

代码质量保障体系是软件工程的重要基础设施,本文档详细介绍了 RuoYi-Plus-UniApp 项目在前端和移动端的完整质量保障方案。

核心工具链:

  1. EditorConfig - 编辑器层面统一基础配置
  2. Prettier - 代码格式化,消除风格争议
  3. ESLint - 代码规范检查和自动修复
  4. TypeScript - 类型安全和智能提示
  5. Git Hooks - 提交前自动检查
  6. CI/CD - 持续集成质量门禁

关键配置文件:

项目根目录
├── .editorconfig              # EditorConfig 配置
├── .prettierrc.js             # Prettier 配置
├── .prettierignore            # Prettier 忽略文件
├── eslint.config.ts           # ESLint 配置(Flat Config)
├── tsconfig.json              # TypeScript 配置
├── .husky/                    # Git Hooks
│   ├── pre-commit            # 提交前检查
│   └── commit-msg            # 提交信息检查
├── .github/workflows/         # GitHub Actions
│   └── lint.yml              # 自动化检查流程
└── package.json
    ├── scripts                # 检查命令
    └── lint-staged            # 暂存区文件检查

质量保障流程:

开发阶段
├── 编辑器实时提示(ESLint + TypeScript)
├── 保存自动格式化(Prettier)
└── 保存自动修复(ESLint)

提交阶段
├── pre-commit Hook
│   ├── Lint-staged 检查暂存区文件
│   ├── ESLint 自动修复
│   └── Prettier 自动格式化
└── commit-msg Hook
    └── Commitlint 检查提交信息

推送阶段
└── pre-push Hook
    ├── 单元测试
    └── 类型检查

合并阶段
├── CI/CD 自动检查
│   ├── ESLint 检查
│   ├── Prettier 检查
│   ├── TypeScript 检查
│   └── 单元测试
└── Code Review
    └── 人工审查

最佳实践建议:

  1. 渐进式采用 - 老项目逐步启用规则,避免一次性产生大量错误
  2. 团队共识 - 规则配置应基于团队讨论和投票,避免个人偏好主导
  3. 自动化优先 - 尽可能使用自动化工具,减少人工检查成本
  4. 持续优化 - 定期回顾规则配置,根据团队反馈调整
  5. 文档先行 - 规则变更前先更新文档,确保团队成员了解

通过完整的代码质量保障体系,项目可以维持高水平的代码质量,减少 bug,提高团队协作效率,降低维护成本。这是一个持续改进的过程,需要团队全员参与和长期坚持。