代码质量保障体系
介绍
代码质量保障体系是软件工程中确保代码质量、可维护性和团队协作效率的关键实践。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 质量门禁
└── 度量分析层
├── 代码覆盖率 - 测试覆盖率统计
└── 技术债务 - 代码质量趋势分析项目覆盖范围:
| 项目 | ESLint | Prettier | EditorConfig | TypeScript |
|---|---|---|---|---|
| plus-ui (管理端前端) | ✅ | ✅ | ✅ | ✅ |
| plus-uniapp (移动端) | ✅ | ✅ | ✅ | ✅ |
| plus-app (App端) | ✅ | ✅ | ✅ | ✅ |
| ruoyi-admin (后端) | ❌ | ❌ | ✅ | ❌ |
技术栈版本:
| 工具 | plus-ui | plus-uniapp |
|---|---|---|
| ESLint | 9.21.0 | 9.21.0 |
| Prettier | 3.5.2 | 3.x |
| TypeScript | ~5.8.3 | 5.7.2 |
| Vue | 3.5.13 | 3.4.21 |
EditorConfig 统一编辑器配置
介绍
EditorConfig 帮助开发团队在不同的编辑器和 IDE 之间维护一致的编码风格。它是代码质量保障的第一层,在编辑器层面统一基础配置。
核心优势:
- 跨编辑器支持 - 支持几乎所有主流编辑器(VS Code、WebStorm、Sublime Text 等)
- 优先级最高 - 编辑器直接读取,优先级高于编辑器自身配置
- 配置简单 - 使用 INI 格式,配置项清晰易懂
- 自动应用 - 打开文件时自动应用,无需手动触发
项目配置
项目根目录的 .editorconfig 文件:
# 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配置说明:
| 配置项 | 值 | 说明 |
|---|---|---|
root | true | 标记为根配置文件,编辑器停止向上查找 |
indent_style | space | 使用空格缩进而非制表符 |
indent_size | 4 | 默认缩进4个空格(后端 Java 代码) |
charset | utf-8 | 文件编码为 UTF-8 |
end_of_line | lf | 使用 Unix 风格换行符(LF) |
trim_trailing_whitespace | true | 自动删除行尾空白字符 |
insert_final_newline | true | 文件末尾插入空行 |
特殊文件类型:
# JSON/YAML 使用2空格缩进(前端主流约定)
[*.{json,yml,yaml}]
indent_size = 2
# Markdown 保留尾部空白和换行(Markdown 语法需要)
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false编辑器支持
VS Code:
默认支持,无需插件。如需查看生效状态,查看状态栏:
- 左下角显示:
空格: 4或空格: 2 - 换行符:
LF或CRLF
WebStorm/IntelliJ IDEA:
默认支持,设置路径:
Settings > Editor > Code Style > Enable EditorConfig support验证配置:
- 打开任意
.java文件,按 Tab 键应插入 4 个空格 - 打开任意
.json文件,按 Tab 键应插入 2 个空格 - 保存文件时自动删除行尾空白字符
Prettier 代码格式化
介绍
Prettier 是一个代码格式化工具,支持 JavaScript、TypeScript、Vue、CSS、JSON 等多种语言。它通过固定的格式化规则消除团队中的代码风格争议。
核心特性:
- 固定格式 - 格式化规则固定,不提供过多配置项,减少团队讨论成本
- 自动格式化 - 保存时自动格式化,或通过命令行批量格式化
- 广泛支持 - 支持 20+ 种编程语言和标记语言
- 集成简单 - 与 ESLint、编辑器、Git Hooks 无缝集成
plus-ui 配置
管理端前端 .prettierrc.js 配置:
// 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 配置:
// 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-ui | plus-uniapp | 说明 |
|---|---|---|---|
printWidth | 150 | 100 | plus-ui 适配宽屏,plus-uniapp 移动端代码较短 |
trailingComma | 'none' | 'all' | plus-uniapp 使用现代 ES 规范,支持尾随逗号 |
quoteProps | 'preserve' | 默认 | plus-ui 保留对象属性引号原样 |
jsxSingleQuote | false | 默认 | plus-ui 明确 JSX 使用双引号 |
bracketSameLine | false | 默认 | plus-ui 明确闭合括号换行 |
arrowParens | 'always' | 默认 | plus-ui 强制箭头函数参数使用括号 |
htmlWhitespaceSensitivity | 'css' | 'ignore' | plus-uniapp 忽略 HTML 空白,避免 UniApp 组件问题 |
使用方式
1. 命令行格式化
# 格式化所有文件
pnpm prettier
# 等价于
pnpm prettier --write .
# 检查文件格式(不修改)
pnpm prettier --check .
# 格式化指定文件
prettier --write src/components/Button.vue
# 格式化指定目录
prettier --write src/components/**/*.vue2. VS Code 集成
安装插件:
Prettier - Code formatter (esbenp.prettier-vscode)配置 .vscode/settings.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.jsonESLint 代码规范检查
介绍
ESLint 是 JavaScript 生态系统中最流行的静态代码分析工具,用于识别和报告代码中的模式和问题。本项目使用 ESLint 9.x 扁平化配置(Flat Config)。
核心功能:
- 代码质量检查 - 检测潜在错误、代码异味、不安全的用法
- 代码风格统一 - 强制执行团队约定的代码风格
- 自动修复 - 部分规则支持自动修复,提高开发效率
- 可扩展性 - 通过插件支持 TypeScript、Vue、React 等框架
- 渐进式采用 - 可以逐步启用规则,不影响现有代码
plus-ui ESLint 配置
管理端前端 eslint.config.ts:
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'
}
}
)配置解析:
文件检查范围:
- 包含:
**/*.{js,cjs,ts,mts,tsx,vue} - 排除:
dist/、dist-ssr/、coverage/、locales/
- 包含:
预设配置:
pluginVue.configs['flat/essential']- Vue 基础规则vueTsConfigs.recommended- TypeScript 推荐规则skipFormatting- 跳过 Prettier 格式化规则
自定义规则:
- 放宽 TypeScript 严格性(允许
any、空函数等) - 放宽 Vue 规范性(允许单词组件名、defineProps)
- 集成 Prettier(强制执行格式化)
- 放宽 TypeScript 严格性(允许
plus-uniapp ESLint 配置
移动端 eslint.config.mjs(部分):
// 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,
},
})配置特点:
基于
@uni-helper/eslint-config:- 预配置了 UniApp 项目常用规则
- 集成了 Vue 3、TypeScript、UnoCSS 支持
宽松的规则:
- 允许
console、未使用变量(开发阶段更灵活) - 关闭大部分代码风格规则(交给 Prettier)
- 允许
UniApp 特定优化:
- 忽略
uni_modules/第三方插件 - 忽略自动生成的
pages.json、manifest.json
- 忽略
使用方式
1. 命令行检查
# 检查所有文件
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/**/*.vue2. VS Code 集成
安装插件:
ESLint (dbaeumer.vscode-eslint)配置 .vscode/settings.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 | 错误级别 | 显示错误并阻止构建 |
示例:
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(核心配置):
{
"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(核心配置):
{
"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"]
}类型检查命令
# 类型检查
pnpm type-check
# 等价于
pnpm vue-tsc --noEmit
# 观察模式(实时检查)
pnpm vue-tsc --noEmit --watch常见类型错误
1. 缺少类型定义
// ❌ 错误:隐式 any 类型
function add(a, b) {
return a + b
}
// ✅ 正确:显式类型
function add(a: number, b: number): number {
return a + b
}2. 属性不存在
// ❌ 错误:属性 '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. 类型不匹配
// ❌ 错误:类型 '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 配置示例
安装:
pnpm add -D husky
pnpm husky install配置 package.json:
{
"scripts": {
"prepare": "husky install"
}
}创建 pre-commit Hook:
npx husky add .husky/pre-commit "pnpm lint-staged"生成 .husky/pre-commit 文件:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm lint-stagedLint-staged
Lint-staged 仅对暂存区的文件执行检查,提高检查速度。
安装:
pnpm add -D lint-staged配置 package.json:
{
"lint-staged": {
"*.{js,ts,jsx,tsx,vue}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,less,vue}": [
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
}
}工作流程:
- 开发者执行
git commit - Git 触发
pre-commitHook - Husky 执行
lint-staged - Lint-staged 对暂存区文件执行 ESLint 和 Prettier
- 如果检查失败,提交被阻止
- 如果检查通过,提交成功
Commitlint 提交信息规范
安装:
pnpm add -D @commitlint/cli @commitlint/config-conventional配置 commitlint.config.js:
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:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'提交信息格式:
<type>(<scope>): <subject>
<body>
<footer>示例:
feat(user): 添加用户登录功能
- 实现登录表单
- 添加登录 API
- 集成 JWT 认证
Closes #123CI/CD 集成
GitHub Actions 配置
创建 .github/workflows/lint.yml:
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-checkGitLab CI 配置
创建 .gitlab-ci.yml:
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: 1GitLab 设置:
Settings > Repository > Protected Branches
Allowed to merge: Developers + Maintainers
Allowed to push: No one
Require approval: 1代码质量度量
质量指标
| 指标 | 说明 | 目标值 | 工具 |
|---|---|---|---|
| ESLint 错误数 | ESLint 报告的错误数量 | 0 | ESLint |
| ESLint 警告数 | ESLint 报告的警告数量 | < 10 | ESLint |
| 代码覆盖率 | 测试覆盖的代码比例 | > 80% | Vitest |
| 类型覆盖率 | TypeScript 类型覆盖比例 | > 90% | TypeScript |
| 代码重复率 | 重复代码占比 | < 5% | jscpd |
| 圈复杂度 | 函数复杂度 | < 10 | ESLint complexity |
ESLint 报告
生成 HTML 报告:
# 安装报告生成器
pnpm add -D eslint-formatter-html
# 生成报告
pnpm eslint src --format html --output-file eslint-report.html生成 JSON 报告:
pnpm eslint src --format json --output-file eslint-report.json代码覆盖率
Vitest 配置 vitest.config.ts:
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
}
}
}
})运行覆盖率测试:
pnpm vitest run --coverage持续监控
1. 趋势分析
定期收集质量指标,绘制趋势图:
# 每周统计 ESLint 错误数
pnpm eslint src --format json | jq '.[] | .errorCount' | awk '{s+=$1} END {print s}'2. 质量仪表板
使用 SonarQube 或 Code Climate 等工具建立质量仪表板。
SonarQube 配置 sonar-project.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运行扫描:
sonar-scanner最佳实践
1. 渐进式采用
问题: 老项目引入 ESLint 可能产生大量错误,影响开发进度。
方案:
// eslint.config.js
export default [
{
rules: {
// 第一阶段:仅启用错误级别规则
'no-undef': 'error',
'no-unused-vars': 'error',
// 第二阶段:将警告规则升级为错误
// 'no-console': 'error',
// 第三阶段:启用所有推荐规则
// ...推荐配置
}
}
]步骤:
- 第1周:启用基础错误规则,修复全部错误
- 第2-3周:启用警告规则,逐步修复警告
- 第4周:启用完整推荐配置
- 持续:根据团队反馈调整规则
2. 自定义规则配置
根据项目特点调整规则:
// 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. 忽略文件合理配置
原则:
- 自动生成的文件必须忽略
- 第三方代码必须忽略
- 测试覆盖率报告必须忽略
// eslint.config.js
ignores: [
'**/dist/**',
'**/node_modules/**',
'**/coverage/**',
'**/*.d.ts',
'**/auto-imports.d.ts',
'**/components.d.ts'
]4. 规则禁用注释
临时禁用规则(谨慎使用):
// 禁用下一行
// 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. 团队规范同步
确保团队成员使用相同配置:
// .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 检查慢的解决方案:
// eslint.config.js
export default [
{
// 1. 减少检查文件范围
files: ['src/**/*.{js,ts,vue}'],
// 2. 增加忽略目录
ignores: [
'**/dist/**',
'**/node_modules/**',
'**/*.min.js'
]
}
]命令行优化:
# 使用缓存(默认启用)
pnpm eslint --cache src
# 增加超时时间(大项目)
pnpm eslint --timeout 60000 src
# 并行检查(实验性)
pnpm eslint --max-warnings 0 src7. 代码审查清单
在 Code Review 时检查:
- [ ] 代码符合 ESLint 规范
- [ ] 代码已通过 Prettier 格式化
- [ ] 没有被注释掉的代码
- [ ] 没有
console.log、debugger - [ ] TypeScript 类型定义完整
- [ ] 变量和函数命名语义化
- [ ] 复杂逻辑有注释说明
- [ ] 没有硬编码的配置项
常见问题
1. ESLint 和 Prettier 冲突
问题描述:
ESLint 报告格式错误,但 Prettier 格式化后又产生新的 ESLint 错误,形成循环冲突。
原因分析:
- ESLint 的格式化规则与 Prettier 的格式化规则不一致
- 常见冲突规则:
semi、quotes、indent、comma-dangle等
解决方案:
方案1:使用 eslint-config-prettier(推荐)
pnpm add -D eslint-config-prettier// eslint.config.js
import prettier from 'eslint-config-prettier'
export default [
// 其他配置...
prettier, // 放在最后,关闭所有与 Prettier 冲突的规则
]方案2:使用 @vue/eslint-config-prettier(Vue 项目)
// eslint.config.js
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
export default [
// 其他配置...
skipFormatting, // 跳过格式化相关规则
]验证冲突:
# 检查 ESLint 配置中是否有与 Prettier 冲突的规则
npx eslint-config-prettier src/main.ts2. TypeScript 类型错误但代码正常运行
问题描述:
TypeScript 报告类型错误,但代码在浏览器中正常运行。
原因分析:
- TypeScript 是编译时检查,JavaScript 是运行时执行
- 类型错误不影响代码运行,但可能导致潜在问题
解决方案:
情况1:第三方库缺少类型定义
# 安装类型定义包
pnpm add -D @types/lodash如果没有官方类型定义,创建 src/types/lodash.d.ts:
declare module 'lodash' {
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait?: number
): T
}情况2:动态属性访问
// ❌ 错误:元素隐式具有 "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 类型
// 临时方案:使用 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 安装
# 查看 .husky 目录是否存在
ls -la .husky
# 重新安装 Husky
pnpm husky install步骤2:检查 Hook 文件权限
# 赋予执行权限
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg步骤3:手动测试 Hook
# 手动执行 pre-commit
.husky/pre-commit
# 如果报错,检查脚本内容是否正确
cat .husky/pre-commit步骤4:检查 Git 配置
# 查看 Git Hooks 路径
git config core.hooksPath
# 应输出:.husky
# 如果不正确,重新设置
git config core.hooksPath .husky步骤5:跳过 Hooks(紧急情况)
# 临时跳过 pre-commit
git commit --no-verify -m "紧急修复"
# 不推荐经常使用,应该修复 Hooks 问题4. ESLint 检查速度慢
问题描述:
项目文件较多时,ESLint 检查耗时过长,影响开发体验。
原因分析:
- 检查文件范围过大
- 没有使用缓存
- 规则过于复杂
解决方案:
方案1:启用缓存
# ESLint 默认启用缓存,缓存文件:.eslintcache
# 查看缓存是否生效
pnpm eslint --cache --cache-location .eslintcache src
# 清除缓存(如果缓存损坏)
rm -f .eslintcache方案2:减少检查范围
// eslint.config.js
export default [
{
files: ['src/**/*.{js,ts,vue}'], // 仅检查 src 目录
ignores: [
'**/dist/**',
'**/node_modules/**',
'**/*.min.js',
'**/vendor/**' // 排除第三方代码
]
}
]方案3:仅检查变更文件
# 使用 Lint-staged 仅检查暂存区文件
pnpm lint-staged
# 使用 Git 获取变更文件
git diff --name-only --diff-filter=ACMR | grep -E '\.(js|ts|vue)$' | xargs eslint方案4:调整规则
// 禁用耗时的规则
rules: {
// 禁用 import 排序(耗时)
'import/order': 'off',
// 禁用复杂度检查(耗时)
'complexity': 'off'
}方案5:使用 ESLint 扁平化配置(Flat Config)
ESLint 9.x 的扁平化配置性能更好:
// eslint.config.js (新格式,性能更好)
export default [
// 配置...
]
// .eslintrc.js (旧格式,性能较差)
module.exports = {
// 配置...
}5. Prettier 格式化破坏代码
问题描述:
Prettier 格式化后,代码逻辑被破坏或格式不符合预期。
原因分析:
- Prettier 固定格式可能与个人习惯不一致
- 某些特殊情况需要保留原格式
解决方案:
方案1:调整 Prettier 配置
// .prettierrc.js
module.exports = {
// 增加单行宽度,减少换行
printWidth: 120,
// 保留尾随逗号
trailingComma: 'es5',
// 使用单引号
singleQuote: true
}方案2:使用格式化忽略注释
// 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 空白
// .prettierrc.js
module.exports = {
// 忽略 HTML 空白,避免 Vue 模板格式化问题
htmlWhitespaceSensitivity: 'ignore'
}6. CI/CD 检查失败但本地通过
问题描述:
本地执行 pnpm lint:eslint 通过,但 CI/CD 流程中 ESLint 检查失败。
原因分析:
- Node 版本不一致
- 依赖版本不一致
- 缓存问题
解决方案:
步骤1:统一 Node 版本
在 CI/CD 配置中指定 Node 版本:
# .github/workflows/lint.yml
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18.18.0' # 与本地一致步骤2:锁定依赖版本
# 使用 pnpm lockfile
pnpm install --frozen-lockfile# CI/CD 配置
- name: Install dependencies
run: pnpm install --frozen-lockfile # 严格按照 lockfile 安装步骤3:清除缓存
# 本地清除缓存
rm -rf node_modules .eslintcache
pnpm install
# CI/CD 清除缓存(GitHub Actions)
- name: Clear cache
run: |
rm -rf node_modules
rm -f .eslintcache步骤4:检查环境变量
# 本地和 CI 可能使用不同的环境变量
# CI 通常使用 production 环境
# package.json
"lint:eslint": "NODE_ENV=development eslint src"步骤5:本地模拟 CI 环境
# 使用 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:识别高频错误规则
# 统计 ESLint 错误规则
pnpm eslint src --format json | jq '.[].messages[] | .ruleId' | sort | uniq -c | sort -rn | head -20步骤2:调整规则级别
// eslint.config.js
rules: {
// 将错误降级为警告
'no-console': 'warn', // 从 'error' 改为 'warn'
// 关闭不适用的规则
'@typescript-eslint/no-explicit-any': 'off',
// 自定义规则选项
'max-len': ['warn', { code: 120, ignoreStrings: true }]
}步骤3:逐步启用规则
// 第一阶段:仅启用错误级别规则
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 | 禁止 console | 3 | 2 | 启用(warn) |
@typescript-eslint/no-explicit-any | 禁止 any | 1 | 4 | 关闭 |
总结
代码质量保障体系是软件工程的重要基础设施,本文档详细介绍了 RuoYi-Plus-UniApp 项目在前端和移动端的完整质量保障方案。
核心工具链:
- EditorConfig - 编辑器层面统一基础配置
- Prettier - 代码格式化,消除风格争议
- ESLint - 代码规范检查和自动修复
- TypeScript - 类型安全和智能提示
- Git Hooks - 提交前自动检查
- 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
└── 人工审查最佳实践建议:
- 渐进式采用 - 老项目逐步启用规则,避免一次性产生大量错误
- 团队共识 - 规则配置应基于团队讨论和投票,避免个人偏好主导
- 自动化优先 - 尽可能使用自动化工具,减少人工检查成本
- 持续优化 - 定期回顾规则配置,根据团队反馈调整
- 文档先行 - 规则变更前先更新文档,确保团队成员了解
通过完整的代码质量保障体系,项目可以维持高水平的代码质量,减少 bug,提高团队协作效率,降低维护成本。这是一个持续改进的过程,需要团队全员参与和长期坚持。
