前端代码质量门禁
前端代码质量面临的问题
项目中经常会遇到由于前端代码基础质量不高造成工作反复、后期维护难的问题。 这些问题涉及HTML、 CSS/LESS/SASS、JS/TS,大致可分为 4 类:
- 代码风格
- 语法规范
- 潜在语法与逻辑风险
- 潜在安全风险
问题示例
- HTML 风格问题
<!--错误 table 没有使用th做表头-->
<table>
<tr>
<td>Name</td>
<td>Age</td>
</tr>
<tr>
<td>John Doe</td>
<td>24</td>
</tr>
<tr>
<td>Alice Doe</td>
<td>54</td>
</tr>
</table>
<!-- 正确 -->
<table>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr>
<td>John Doe</td>
<td>24</td>
</tr>
<tr>
<td>Alice Doe</td>
<td>54</td>
</tr>
</table>
- HTML 书写问题
<!-- href 属性应该放置链接 不是图片 -->
<a href="image.png">...</a>
<!-- 正确 -->
<a href="page.html">...</a>
- CSS/LESS/SASS 风格问题
/* 十六进制颜色写法不统一 */
a {
color: #f0a;
color: #FF00aa;
color: #FF00AA;
color: #ff00aa;
}
/* 嵌套层数太多 */
.a .b .c .d .e .f {
color: #fff000;
}
- CSS/LESS/SASS 书写问题
/* 忘记书写通用字体 */
a {
font-family: Arial,
}
a {
font-family: Arial, sans-serif;
}
/* calc函数内表达式无效 */
.foo {
width: calc();
}
/* 重复的字体 */
a {
font-family: serif, serif;
}
- JS/TS 风格问题
// 方法排列没有顺序
// 正确排序方式
export class CustomersComponent implements OnInit, OnDestroy {
@Select(CustomersState.getItems) items$: Observable<CustomersModel[]>;
keywordControl = new FormControl(this.route.snapshot.queryParams.keyword || '');
private destroy = new Subject();
// ...
constructor(
private
route: ActivatedRoute
,
) {
//code
}
// ...
ngOnInit()
{
// code
}
// ...
private
getCurrentQueryParams()
{
// code
}
}
// 错误
export class CustomersComponent implements OnInit, OnDestroy {
@Select(CustomersState.getItems) items$: Observable<CustomersModel[]>;
keywordControl = new FormControl(this.route.snapshot.queryParams.keyword || '');
private destroy = new Subject();
// ...
constructor(
private
route: ActivatedRoute
,
) {
//code
}
// ...
private
getCurrentQueryParams()
{
// code
}
// ...
ngOnInit()
{
// code
}
}
- JS/TS 书写问题
// 1. 局部变量覆盖全局变量 b() 作用域中的 a 覆盖了全局环境中的 a。这会混淆读者并且在 b 中不能获取全局变量
var a = 3;
function b() {
var a = 10;
}
// 2. 回调函数未使用箭头函数造成 this 指向错误
foo(function () {
console.log(this.a);
});
foo(() => console.log(this.a;
))
;
// 3. 未使用 === 造成逻辑错误
let x = '42';
if (x === 42) {
}
前端代码质量门禁
通过建立前端代码质量门禁,提升代码质量,达到质量内建的效果,从而从根本上解决上述的问题。
质量门禁指标
定义 3 个质量门禁指标:
指标名 | 目标值 | 指标说明 |
---|---|---|
可靠性 | A级 | 根据项目bug数给与的项目评级: A = 0 Bug 最高等级A,表示代码无bug B = at least 1 Minor Bug 代码只要有一个次要bug,等级就为B C = at least 1 Major Bug 只要包含一个重要bug,等级将为C D = at least 1 Critical Bug 只要有一个严重bug,等级评估为D E = at least 1 Blocker Bug 只要有一个最高等级的阻断级别的bug,可靠性评估为E,最低级别。 |
安全性 | A级 | 根据项目安全漏洞给与的项目评级: A = 0 Vulnerability 没有漏洞时,项目评估为最高级别A B = at least 1 Minor Vulnerability 只要包含一个次要漏洞,项目评估为级别B C = at least 1 Major Vulnerability 只要包含一个重要漏洞,项目评估为级别C D = at least 1 Critical Vulnerability 只要包含一个严重漏洞,评估为D E = at least 1 Blocker Vulnerability 只要包含一个阻断漏洞,项目评估为最低级别E |
可维护性 | A级 | 根据项目技术债务比率而给与的项目评级: A = 0 - 0.05,即在0到5%之间 B = 0.06 - 0.1,即在6%到10%之间 C = 0.11 - 0.2,即在11%到20%之间 D = 0.21 - 0.5,即在21%到50%之间 E = 0.51 - 1,即超过50% 注:1.开发成本与修复成本的比率。该比率=修复成本/开发成本,也=修复成本/(代码行数*每行代码开发成本) 2.每行代码开发成本为0.06天。 |
检查规则项
4款工具共提供了约 850+ 项规则,考虑到投入产出比,我们整理了更为常用的400+ 项规则用于支撑3个质量门禁指标,并通过 stylelint、ESLint、Prettier、SonarQube 落地实施。
语言名称 | Stylelint | ESlint | Prettier | SonarQube | 小计 |
---|---|---|---|---|---|
HTML | 0 | 0 | 0 | 29 | 29 |
CSS/LESS/SCSS | 33 | 0 | 0 | 22 | 55 |
JS/TS | 0 | 21 | 0 | 298 | 319 |
总计 | 33 | 21 | 0 | 349 | 403 |
Stylelint 检查规则项 (33项)
ESlint 检查规则项 (21项)
code | 规则描述 | Angular | Vue | React |
---|---|---|---|---|
@typescript-eslint/no-namespace | 禁止使用自定义TypeScript模块和名称空间 | ✓ | ✓ | ✓ |
@typescript-eslint/no-explicit-any | 禁止使用该any类型 | ✓ | ✓ | ✓ |
@typescript-eslint/dot-notation | 尽可能执行点符号 | ✓ | ✓ | ✓ |
@typescript-eslint/no-shadow | 禁止在外部范围中声明的阴影变量中声明变量 | ✓ | ✓ | ✓ |
@typescript-eslint/member-ordering | 需要一致的成员声明顺序 | ✓ | ✓ | ✓ |
@typescript-eslint/semi | 要求使用分号代替 | ✓ | ✓ | ✓ |
@typescript-eslint/no-extra-semi | 禁止不必要的分号 | ✓ | ✓ | ✓ |
@typescript-eslint/no-inferrable-types | 禁止声明显示类型 | ✓ | ✓ | ✓ |
@typescript-eslint/member-delimiter-style | 接口和类型文字需要特定的成员定界符样式 | ✓ | ✓ | ✓ |
@typescript-eslint/no-unused-expressions | 禁止未使用的表达式 | ✓ | ✓ | ✓ |
@typescript-eslint/no-extra-parens | 禁止不必要的括号 | ✓ | ✓ | ✓ |
@typescript-eslint/naming-convention | 对代码库中的所有内容强制执行命名约定 | ✓ | ✓ | ✓ |
@typescript-eslint/no-empty-function | 禁止空函数 | ✓ | ✓ | ✓ |
no-extra-boolean-cast | 禁止不必要的布尔类型转换 | ✓ | ✓ | ✓ |
prefer-const | 要求使用 const 声明那些声明后不再被修改的变量 | ✓ | ✓ | ✓ |
no-empty | 禁止空块语句 | ✓ | ✓ | ✓ |
eqeqeq | 要求使用 === 和 !== | ✓ | ✓ | ✓ |
object-shorthand | 要求对象字面量简写语法 | ✓ | ✓ | ✓ |
prefer-arrow-callback | 要求使用箭头函数作为回调 | ✓ | ✓ | ✓ |
guard-for-in | 需要约束 for-in (guard-for-in) | ✓ | ✓ | ✓ |
no-underscore-dangle | 禁止标识符中有悬空下划线 | ✓ | ✓ | ✓ |
Sonar 检查规则项 (349项)
SonarQube 使用每种语言的默认的 Quality Profiles
配置文件对代码进行静态分析
语言名称 | 规则数量 |
---|---|
HTML | 29 |
CSS/LESS/SCSS | 22 |
JS | 153 |
TS | 145 |
具体规则可以在SonarQube中的Quality Profiles
菜单中查看
工具安装与配置
Stylelint 安装
1. 安装 Stylelint
npm install stylelint --save-dev
// 或
yarn add stylelint -D
2. 安装基本配置
npm install stylelint-config-recommended --save-dev
// 或
yarn add stylelint-config-recommended -D
Stylelint 配置
1. 创建 .stylelintrc.js 配置文件
2. 配置 规则
// .stylelintrc.js
module.exports = {
extends: [
// 基本推荐规则继承,继承社区推荐的规则
'stylelint-config-recommended'
],
rules: {
// 添加基于项目自定义的常见错误
}
};
3. 添加校验命令
// package.json
"styleCheck": "yarn stylelint 'src/**/*.{html,css,less,vue,js,jsx,ts,tsx}'",
4. 编辑器安装扩展插件
- VSCode 客户端之后,安装以下扩展插件
插件名称 | 插件说明 |
---|---|
vscode-stylelint | Visual Studio Code扩展,可使用stylelint扩展到CSS / SCSS / Less |
- Webstorm 进行以下配置
ESlint 安装
npm install eslint --save-dev
// 或
yarn add eslint -D
ESlint 配置
1. 创建 .eslintrc.js配置文件
2. 配置基础规则
// .eslintrc.js
module.exports = {
root: true,
rules: {
//配置基本规则要求
}
};
3. 添加校验命令
// package.json
"eslint": "eslint src/ --ext .js,.jsx,.ts,.tsx",
4. 编辑器安装扩展插件
- VSCode 客户端之后,安装以下扩展插件
插件名称 | 插件说明 |
---|---|
VS Code ESlint extension | VS Code ESLint扩展 |
- Webstorm 进行以下配置
SonarLint 配置
SonarLint是SonarSource发布的扩展,可帮助您在以JavaScript,TypeScript,Python,Java,HTML和PHP编写代码时检测并修复质量问题。
1.添加插件
插件名称 | 插件说明 |
---|---|
SonarLint | SonarLint for Visual Studio 扩展 |
注意:SonarLint需要安装JDK。Java 8 or more recent is required to run. Please download and install a recent JRE.
如果您没有安装所需的JDK,则会显示一条通知,显示。单击获取Java运行时环境以下载并安装所需的JDK。
2. 添加SonarQube配置
在编辑器菜单中一次点击 code > 首选项 > 设置 > 用户, 在设置中找到SonarLint点击在 setting.json中添加以下配置
"sonarlint.connectedMode.connections.sonarqube": [
{
"serverUrl": "sonarqube服务器地址",
"token": "用户token"
},
],
3.在sonarqube服务器创建项目
4.配置项目绑定
在编辑器菜单中一次点击 code > 首选项 > 设置 > 工作区, 在设置中找到SonarLint点击在 setting.json中添加以下配置
projectKey 同上图2中的 Project key 的名称
"sonarlint.connectedMode.project": {
"projectKey": "项目名称"
}
5.在编辑器中执行SonarLint命令,和服务器同步错误提示
按Ctrl + Shift + P(Windows/Linux)或Shift + Command +
P(Mac)打开命令面板,然后键入SonarLint以调出SonarLint命令并运行SonarLint: Update all bindings to SonarQube/SonarCloud
SonarLint 同步错误提示
SonarLint 提示同步完成
绑定后重启编辑器就可以在打开的页面上看到对应的错误提示
针对 Vue 配置 ESlint
添加eslint-plugin-vue扩展插件
vue add @vue/cli-plugin-eslint
// 或
npm install --save-dev eslint-plugin-vue
// 或
yarn add eslint-plugin-vue -D
修改 eslintrc.js
module.exports = {
root: true,
plugins: [
'@typescript-eslint' // 扩展 typescript 支持
],
/** .vue 文件需要使用 vue-eslint-parser 解析器来解析 */
parser: 'vue-eslint-parser',
env: {
es6: true,
browser: true,
node: true
},
/**
* 为了让 ESLint 在处理非 ECMAScript 5 特性时正常工作
* 配置属性 parserOptions 是必须的
*/
parserOptions: {
/**
* 如果想了解此项配置和上面的 parser,请阅读官方文档
* https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
*/
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaVersion: 2020,
ecmaFeatures: {
legacyDecorators: true
}
},
/**
* ESLint 规则
* 注:因为很多插件的规则中存在特殊符号,因此 rules 下面的所有 key 用单引号来声明
*/
rules: {
// 添加自定义配置
}
};
让 ESLint 支持 Vue 单文件组件
在编辑器菜单中一次点击 code > 首选项 > 设置, 在设置中找到 ESLint 点击在 setting.json中编辑,在eslint.validate中添加vue
"eslint.validate": [
//...
"vue"
]
针对 Angular 配置 ESLint
添加 angular-eslint 扩展插件
ng add @angular-eslint/schematics
TSLint 转换为 ESLint
ng g @angular-eslint/schematics:convert-tslint-to-eslint {{项目名称}}
修改 eslintrc.json (angular 会自动生成的为.json文件)
module.exports = {
root: true,
overrides: [
{
files: [
'*.ts'
],
parser: "@typescript-eslint/parser",
// 添加解析器
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
project: [
"tsconfig.app.json"
],
createDefaultProgram: true
},
plugins: [
"@typescript-eslint"
],
// 添加typescript 支持
rules: {
// 添加自定义配置
}
},
{
files: [
'*.html'
],
extends: [
'plugin:@angular-eslint/template/recommended'
],
rules: {}
}
]
};
针对 React 配置 ESlint
添加 ESLint的解析器
yarn add @typescript-eslint/parser -D
// 或
npm install --save-dev @typescript-eslint/parser
添加 Typescript 代码的规范扩展
yarn add @typescript-eslint/eslint-plugin @typescript-eslint/tslint-plugin -D
// 或
npm install --save-dev @typescript-eslint/eslint-plugin @typescript-eslint/tslint-plugin
修改 eslintrc.json
module.exports = {
parser: "@typescript-eslint/parser",
// Specifies the ESLint parser
parserOptions: {
ecmaVersion: 2020,
// Allows for the parsing of modern ECMAScript features
sourceType: "module"
// Allows for the use of imports
},
plugins: [
"@typescript-eslint"
],
rules: {
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-explicit-any': [
'warn'
],
'@typescript-eslint/dot-notation': 'off',
// 强制尽可能地使用点号
'@typescript-eslint/no-shadow': 'error',
'@typescript-eslint/member-ordering': 'error',
// 标准化类声明,类表达式,接口和类型文字的结构化和排序方式
'@typescript-eslint/semi': 'error',
// 强制使用一致的分号
'@typescript-eslint/no-extra-semi': 'error',
// 禁止不必要的分号
'@typescript-eslint/no-inferrable-types': 'error',
// 禁止声明显示类型
'@typescript-eslint/member-delimiter-style': 'error',
// 成员分隔符样式
'@typescript-eslint/no-unused-expressions': [
'error',
{
'allowShortCircuit': true,
'allowTernary': true,
'allowTaggedTemplates': true
}
],
'@typescript-eslint/no-extra-parens': 'off',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-empty-function': 'error',
'no-extra-boolean-cast': 'error',
// 禁止不必要的布尔类型转换
'prefer-const': 'error',
// 要求使用 const 声明那些声明后不再被修改的变量
'no-empty': 'error',
// 禁止出现空语句块
'eqeqeq': 'error',
// 要求使用 === 和 !==
'object-shorthand': 'error',
// 要求或禁止对象字面量中方法和属性使用简写语法
'no-shadow': 'off',
'prefer-arrow-callback': 'error',
// 单行箭头函数
'guard-for-in': 'off',
'no-underscore-dangle': [
'error',
{
allowAfterThis: true,
allowAfterSuper: true
}
]
}
};
作业流程
1.安装编辑器插件
安装 Stylelint、ESlint、SonarLint等插件并配置相关参数(参考工具安装与配置), 安装后编辑器会在开发途中用波浪线提示代码中存在的错误。
Stylelint 错误提示效果
ESlint 错误提示效果
SonarLint 错误提示效果
注:sonarLint需要和服务器同步错误提示(同步方法参考SonarLint配置)
2.在提交前代对代码进行检查
在提交代码前可以使用以下的命令行检测代码。 如果代码中存在错误, 命令行则会直接提示错误所在文件和所在行数还有错误类型,此时需要对其逐一修改,直至不在提示错误方可提交代码等待自动发布。
// CSS/LESS/SCSS 检查
yarn run styleCheck
// 或
npm run styleCheck
// JS/TS 检查
yarn run eslint
// 或
npm run eslint
执行 styleCheck 错误提示效果
执行 eslint 错误提示效果
3.提交后等待发布
提交代码后,sonarQube 会扫码代码,如收到标题为:FAILED: Job XXXXXX
的邮件,代表发布代码发布失败,此时需要登录 sonarQube 网站, 点击对应的工程,根据提示修改文件后方可再次提交
发布失败提示邮件
注:收件地址为用户提交代码所使用账号对应的邮箱,以 Sourcetree 为例,可在 设置 > 高级 > 电子邮箱地址 中查看
项目整体状态
具体错误提示