Skip to content
On this page

前端规范代码检查工具的最佳实践

浅谈前端团队规范化

前端团队规范化是一个重要的工作,它可以帮助提高团队的生产力,改善代码质量,提高开发效率,并且有助于提高团队的凝聚力。前端团队规范化主要分为三方面:团队沉淀,代码质量,工作流程。

团队沉淀主要为文档规范、技术沉淀。

工作流程主要为工作流规范、前后端协作规范、测试规范。

代码质量主要为技术栈规范、浏览器兼容规范、项目组织规范、异常处理规范、编码规范。

前端团队规范

其中,代码质量是前端团队规范化中的关键,而编码规范又是代码质量中十分重要的一环。就编码规范而言,我们应该采取有效的措施来确保编码规范的一致性。具体措施主要分为三类:

  1. 统一团队成员使用的编码规范。
  2. 使用适当的代码检查工具,如ESLint、Prettier等,以确保代码符合编码规范。
  3. 经常性进行代码审查,以保证代码质量。

本文将重点展示如何通过代码检查工具保证代码质量规范化的强制执行,以及它们对于团队的重要性,以便帮助团队更好地实现其核心任务。

痛点问题

市面上存在许多前端检查工具。总体感觉是杂乱无章的,因为不同的前端规范和框架对应的前端检查工具不尽相同,并且每个前端检查工具的规则也是又多又杂。

我将常用的前端检查工具进行梳理。如下图所示:

前端规范工具梳理

检查工具详解

Git规范

工具介绍

  1. simple-git-hooks:git生命周期钩子
  2. cz-git:输出标准格式的 commitizen 适配器和 CLI
  3. commitlint:commit信息校验CLI
  4. @commitlint/config-conventional:commit信息校验规范rules

工具配置

命令行中安装相应依赖:

bash
npm install -D @commitlint/{config-conventional,cli} cz-git simple-git-hooks
1

package.json 中添加执行脚本:

json
{
	"script":{
		"preinstall":"simple-git-hooks",
		"commit":"git add . && czg"
	}
}
1
2
3
4
5
6

并且在主目录中添加.simple-git-hook.js文件:

jsx
modules.exports = {
	"commit-msg":"npm exec commitlint --edit $1"
}
1
2
3

添加.commitlintrc.js 文件:

jsx
modules.exports = {
	extends:["@commitlint/config-conventional"],
	prompt:{
		useEmoji:false,
		enableMutipleScopess:true,
		scopeEnumSeparator:",",
	}
}
1
2
3
4
5
6
7
8

代码格式化

工具介绍

  1. prettier:自动代码格式化工具。
  2. eslint-plugin-prettier:基于 prettier 代码风格的 eslint 规则,即eslint使用pretter规则来格式化代码。
  3. eslint-config-prettier:禁用所有与格式相关的 eslint 规则,解决 prettier 与 eslint 规则冲突,确保将其放在 extends 队列最后,这样它将覆盖其他配置。

工具配置

命令行中安装相应依赖:

bash
npm install -D prettier eslint-config-prettier eslint-plugin-prettier
1

package.json 中添加执行脚本:

json
{
	"script":{
		"format":"prettier src/**/*.{js,jsx,ts,tsx,json,html,css,scss,less} --write",
	}
}
1
2
3
4
5

添加.prettierrc.js 文件:

jsx
module.exports = {
  semi: false,
  singleQuote: true,
}
1
2
3
4

Vscode规范

通过修改vscode配置文件进行配置vscode设置以及插件。

工具配置

添加vscode/setting.json文件:

json
{
  "typescript.tsdk": "node_modules/typescript/lib",
  "javascript.suggestionActions.enabled": false,
  "npm.packageManager": "pnpm",
  "editor.fontSize": 14,
  "editor.lineNumbers": "on",
  "editor.formatOnSave": true,
  "editor.suggestSelection": "first",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "source.fixAll.stylelint": true,
    "source.fixAll.markdownlint": true,
  },
  "editor.snippetSuggestions": "top",
  "editor.multiCursorModifier": "ctrlCmd",
  "editor.formatOnPaste": false,
  "editor.fontLigatures": true,
  "editor.tabSize": 2,
  "editor.detectIndentation": true,
  "editor.minimap.enabled": false,
  "editor.largeFileOptimizations": false,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "eslint.probe": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html",
    "vue",
    "markdown",
    "json",
    "jsonc"
  ],
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html",
    "vue",
    "markdown",
    "json",
    "jsonc"
  ],

  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,
  "stylelint.enable": true,
  // "stylelint.packageManager": "pnpm",
  "stylelint.validate": ["css", "less", "scss", "sass", "postcss", "vue"],

  "files.autoSave": "afterDelay", // 自动保存
  "files.eol": "\n",
  // 在使用搜索功能时,将这些文件夹/文件排除在外
  "search.exclude": {
    "**/node_modules": true,
    "**/*.log": true,
    "**/*.log*": true,
    "**/bower_components": true,
    "**/dist": true,
    "**/elehukouben": true,
    "**/.git": true,
    "**/.gitignore": true,
    "**/.svn": true,
    "**/.DS_Store": true,
    "**/.idea": true,
    "**/.vscode": false,
    "**/yarn.lock": true,
    "**/pnpm-lock.yaml": true,
    "**/tmp": true,
    "out": true,
    "dist": true,
    "node_modules": true,
    "CHANGELOG.md": true,
    "examples": true,
    "res": true,
    "screenshots": true,
    "yarn-error.log": true,
    "**/.yarn": true
  },
  "files.exclude": {
    "**/.cache": true,
    "**/.editorconfig": true,
    "**/.eslintignore": true,
    "**/.prettierignore": true,
    "**/.stylelintignore": true,
    "**/LICENSE": true,
    "**/.eslintcache": true,
    "**/bower_components": true,
    "**/.idea": true,
    "**/tmp": true,
    "**/.git": true,
    "**/.svn": true,
    "**/.hg": true,
    "**/CVS": true,
    "**/.DS_Store": true,
    "**/*.meta": true,
    "**/*.*.meta": true,
    "**/*.unity": true,
    "**/*.unityproj": true,
    "**/*.mat": true,
    "**/*.fbx": true,
    "**/*.FBX": true,
    "**/*.tga": true,
    "**/*.cubemap": true,
    "**/*.prefab": true,
    "**/Library": true,
    "**/ProjectSettings": true,
    "**/Temp": true
  },
  "files.watcherExclude": {
    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/.vscode/**": true,
    "**/node_modules/**": true,
    "**/tmp/**": true,
    "**/bower_components/**": true,
    "**/dist/**": true,
    "**/yarn.lock": true,
    "**/pnpm-lock.yaml": true
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[less]": {
    "editor.defaultFormatter": "stylelint.vscode-stylelint"
  },
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[markdown]": {
    "editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
  },
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

添加vscode/extensions.json文件:

json
{
  "recommendations": [
    "formulahendry.auto-close-tag",
    "formulahendry.auto-complete-tag",
    "steoates.autoimport",
    "formulahendry.auto-rename-tag",
    "pranaygp.vscode-css-peek",
    "mikestead.dotenv",
    "dbaeumer.vscode-eslint",
    "antfu.iconify",
    "wix.vscode-import-cost",
    "xabikos.javascriptsnippets",
    "akamud.vscode-javascript-snippet-pack",
    "obkoro1.korofileheader",
    "mrmlnc.vscode-less",
    "DavidAnson.vscode-markdownlint",
    "leizongmin.node-module-intellisense",
    "christian-kohler.path-intellisense",
    "esbenp.prettier-vscode",
    "mohsen1.prettify-json",
    "2gua.rainbow-brackets",
    "mrmlnc.vscode-scss",
    "stylelint.vscode-stylelint",
    "Vue.volar",
    "Vue.vscode-typescript-vue-plugin",
    "misterj.vue-volar-extention-pack",
    "sdras.vue-vscode-snippets"
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

样式代码规范

工具介绍

  1. stylelint: css样式lint工具
  2. postcss: 转换css代码工具
  3. postcss-less: 识别less语法
  4. postcss-scss: 识别scss语法
  5. postcss-html: 识别html/vue 中的<style></style>标签中的样式
  6. stylelint-config-standard: Stylelint的标准可共享配置规则,详细可查看官方文档
  7. stylelint-config-recommended-vue: vue的推荐可共享配置规则,详细可查看官方文档
  8. stylelint-less: stylelint-config-recommended-less的依赖,lessstylelint规则集合
  9. stylelint-order: 指定样式书写的顺序,在.stylelintrc.jsorder/properties-order指定顺序

工具配置

命令行中安装相应依赖:

bash
npm install -D stylelint postcss stylelint-order stylelint-config-standard stylelint-config-recommended
1

vue项目需要安装:

bash
npm install -D stylelint-config-recommended-vue postcss-html
1

less或scss项目需要分别安装:

bash
npm install -D postcss-less
1
bash
npm install -D postcss-scss
1

添加.stylelintrc.js文件:

jsx
module.exports = {
  plugins: ['stylelint-order'],
  customSyntax: 'postcss-html',
  extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
  rules: {
    'selector-class-pattern': null,
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global'],
      },
    ],
    'selector-pseudo-element-no-unknown': [
      true,
      {
        ignorePseudoElements: ['v-deep'],
      },
    ],
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'tailwind',
          'apply',
          'variants',
          'responsive',
          'screen',
          'function',
          'if',
          'each',
          'include',
          'mixin',
          // // scss
          // 'at-root',
          // 'use',
          // 'forward',
          // 'return',
        ],
      },
    ],
    'no-empty-source': null,
    'string-quotes': null,
    'named-grid-areas-no-invalid': null,
    'unicode-bom': 'never',
    'no-descending-specificity': null,
    'font-family-no-missing-generic-family-keyword': null,
    'declaration-colon-space-after': 'always-single-line',
    'declaration-colon-space-before': 'never',
    // 'declaration-block-trailing-semicolon': 'always',
    'rule-empty-line-before': [
      'always',
      {
        ignore: ['after-comment', 'first-nested'],
      },
    ],
    'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
    'order/order': [
      [
        'dollar-variables',
        'custom-properties',
        'at-rules',
        'declarations',
        {
          type: 'at-rule',
          name: 'supports',
        },
        {
          type: 'at-rule',
          name: 'media',
        },
        'rules',
      ],
      { severity: 'warning' },
    ],
    'order/properties-order': [
      {
        // Must be first.
        properties: ['all'],
      },
      {
        // Position.
        properties: [
          'position',
          'inset',
          'inset-block',
          'inset-inline',
          'top',
          'right',
          'bottom',
          'left',
          'z-index',
        ],
      },
      {
        // Display mode.
        properties: ['box-sizing', 'display'],
      },
      {
        // Flexible boxes.
        properties: [
          'flex',
          'flex-basis',
          'flex-direction',
          'flex-flow',
          'flex-grow',
          'flex-shrink',
          'flex-wrap',
        ],
      },
      {
        // Grid layout.
        properties: [
          'grid',
          'grid-area',
          'grid-template',
          'grid-template-areas',
          'grid-template-rows',
          'grid-template-columns',
          'grid-row',
          'grid-row-start',
          'grid-row-end',
          'grid-column',
          'grid-column-start',
          'grid-column-end',
          'grid-auto-rows',
          'grid-auto-columns',
          'grid-auto-flow',
          'grid-gap',
          'grid-row-gap',
          'grid-column-gap',
        ],
      },
      {
        // Gap.
        properties: ['gap', 'row-gap', 'column-gap'],
      },
      {
        // Layout alignment.
        properties: [
          'place-content',
          'place-items',
          'place-self',
          'align-content',
          'align-items',
          'align-self',
          'justify-content',
          'justify-items',
          'justify-self',
        ],
      },
      {
        // Order.
        properties: ['order'],
      },
      {
        // Box model.
        properties: [
          'float',
          'width',
          'min-width',
          'max-width',
          'height',
          'min-height',
          'max-height',
          'aspect-ratio',
          'padding',
          'padding-block',
          'padding-block-start',
          'padding-block-end',
          'padding-inline',
          'padding-inline-start',
          'padding-inline-end',
          'padding-top',
          'padding-right',
          'padding-bottom',
          'padding-left',
          'margin',
          'margin-block',
          'margin-block-start',
          'margin-block-end',
          'margin-inline',
          'margin-inline-start',
          'margin-inline-end',
          'margin-top',
          'margin-right',
          'margin-bottom',
          'margin-left',
          'overflow',
          'overflow-x',
          'overflow-y',
          '-webkit-overflow-scrolling',
          '-ms-overflow-x',
          '-ms-overflow-y',
          '-ms-overflow-style',
          'overscroll-behavior',
          'overscroll-behavior-x',
          'overscroll-behavior-y',
          'overscroll-behavior-inline',
          'overscroll-behavior-block',
          'clip',
          'clip-path',
          'clear',
        ],
      },
      {
        // Typography.
        properties: [
          'font',
          'font-family',
          'font-size',
          'font-variation-settings',
          'font-style',
          'font-weight',
          'font-feature-settings',
          'font-optical-sizing',
          'font-kerning',
          'font-variant',
          'font-variant-ligatures',
          'font-variant-caps',
          'font-variant-alternates',
          'font-variant-numeric',
          'font-variant-east-asian',
          'font-variant-position',
          'font-size-adjust',
          'font-stretch',
          'font-effect',
          'font-emphasize',
          'font-emphasize-position',
          'font-emphasize-style',
          '-webkit-font-smoothing',
          '-moz-osx-font-smoothing',
          'font-smooth',
          'hyphens',
          'line-height',
          'color',
          'text-align',
          'text-align-last',
          'text-emphasis',
          'text-emphasis-color',
          'text-emphasis-style',
          'text-emphasis-position',
          'text-decoration',
          'text-decoration-line',
          'text-decoration-thickness',
          'text-decoration-style',
          'text-decoration-color',
          'text-underline-position',
          'text-underline-offset',
          'text-indent',
          'text-justify',
          'text-outline',
          '-ms-text-overflow',
          'text-overflow',
          'text-overflow-ellipsis',
          'text-overflow-mode',
          'text-shadow',
          'text-transform',
          'text-wrap',
          '-webkit-text-size-adjust',
          '-ms-text-size-adjust',
          'letter-spacing',
          'word-break',
          'word-spacing',
          'word-wrap', // Legacy name for `overflow-wrap`
          'overflow-wrap',
          'tab-size',
          'white-space',
          'vertical-align',

          'list-style',
          'list-style-position',
          'list-style-type',
          'list-style-image',

          'src',
          'font-display',
          'unicode-range',
          'size-adjust',
          'ascent-override',
          'descent-override',
          'line-gap-override',
        ],
      },
      {
        // Accessibility & Interactions.
        properties: [
          'pointer-events',
          '-ms-touch-action',
          'touch-action',
          'cursor',
          'visibility',
          'zoom',
          'table-layout',
          'empty-cells',
          'caption-side',
          'border-spacing',
          'border-collapse',
          'content',
          'quotes',
          'counter-reset',
          'counter-increment',
          'resize',
          'user-select',
          'nav-index',
          'nav-up',
          'nav-right',
          'nav-down',
          'nav-left',
        ],
      },
      {
        // Background & Borders.
        properties: [
          'background',
          'background-color',
          'background-image',
          "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient",
          'filter:progid:DXImageTransform.Microsoft.gradient',
          'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader',
          'filter',
          'background-repeat',
          'background-attachment',
          'background-position',
          'background-position-x',
          'background-position-y',
          'background-clip',
          'background-origin',
          'background-size',
          'background-blend-mode',
          'isolation',
          'border',
          'border-color',
          'border-style',
          'border-width',
          'border-block',
          'border-block-start',
          'border-block-start-color',
          'border-block-start-style',
          'border-block-start-width',
          'border-block-end',
          'border-block-end-color',
          'border-block-end-style',
          'border-block-end-width',
          'border-inline',
          'border-inline-start',
          'border-inline-start-color',
          'border-inline-start-style',
          'border-inline-start-width',
          'border-inline-end',
          'border-inline-end-color',
          'border-inline-end-style',
          'border-inline-end-width',
          'border-top',
          'border-top-color',
          'border-top-style',
          'border-top-width',
          'border-right',
          'border-right-color',
          'border-right-style',
          'border-right-width',
          'border-bottom',
          'border-bottom-color',
          'border-bottom-style',
          'border-bottom-width',
          'border-left',
          'border-left-color',
          'border-left-style',
          'border-left-width',
          'border-radius',
          'border-start-start-radius',
          'border-start-end-radius',
          'border-end-start-radius',
          'border-end-end-radius',
          'border-top-left-radius',
          'border-top-right-radius',
          'border-bottom-right-radius',
          'border-bottom-left-radius',
          'border-image',
          'border-image-source',
          'border-image-slice',
          'border-image-width',
          'border-image-outset',
          'border-image-repeat',
          'outline',
          'outline-width',
          'outline-style',
          'outline-color',
          'outline-offset',
          'box-shadow',
          'mix-blend-mode',
          'filter:progid:DXImageTransform.Microsoft.Alpha(Opacity',
          "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha",
          'opacity',
          '-ms-interpolation-mode',
        ],
      },
      {
        // SVG Presentation Attributes.
        properties: [
          'alignment-baseline',
          'baseline-shift',
          'dominant-baseline',
          'text-anchor',
          'word-spacing',
          'writing-mode',

          'fill',
          'fill-opacity',
          'fill-rule',
          'stroke',
          'stroke-dasharray',
          'stroke-dashoffset',
          'stroke-linecap',
          'stroke-linejoin',
          'stroke-miterlimit',
          'stroke-opacity',
          'stroke-width',

          'color-interpolation',
          'color-interpolation-filters',
          'color-profile',
          'color-rendering',
          'flood-color',
          'flood-opacity',
          'image-rendering',
          'lighting-color',
          'marker-start',
          'marker-mid',
          'marker-end',
          'mask',
          'shape-rendering',
          'stop-color',
          'stop-opacity',
        ],
      },
      {
        // Transitions & Animation.
        properties: [
          'transition',
          'transition-delay',
          'transition-timing-function',
          'transition-duration',
          'transition-property',
          'transform',
          'transform-origin',
          'animation',
          'animation-name',
          'animation-duration',
          'animation-play-state',
          'animation-timing-function',
          'animation-delay',
          'animation-iteration-count',
          'animation-direction',
        ],
      },
    ],

    // // scss
    // 'scss/operator-no-newline-after': null,
    // 'block-closing-brace-newline-after': null,
    // 'at-rule-empty-line-before': null,
    // 'function-name-case': null,
  },
  ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts', '**/*.json'],
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465

package.json 中添加执行脚本:

json
{
	"script":{
		"lint:style": "stylelint \"./**/*.{css,less,vue,html}\" --fix",
	}
}
1
2
3
4
5

代码语法规范

工具介绍

eslint:语法检查工具

工具配置

安装依赖:

bash
pnpm add eslint -D
1

执行eslint初始化命令:

bash
pnpm eslint --init
1

依次选择初始化选项。随后会安装相关依赖,依赖安装完成后,会生成.eslintrc.js配置文件

angular配置

angular暂不支持通过eslint脚手架配置。我们可以通过执行脚本进行配置:

bash
ng add @angular-eslint/schematics
1

通过monorepo管理检查工具项目

搭建检查工具脚手架——傻瓜式配置检查工具

个人思考

  1. 规范应通过工具进行强制约束

Light tomorrow with today.