2021-05-06-vue编码规范

背景

前端规范管理,eslint、stylelint辅助管理。

ESLint、Stylelint、Prettier、EditorConfig这些检测工具的工作原理都比较类似,不外乎对象实例化-编译Javascript-遍历AST检测-输出检测结果这四步流程

  • ESLint:检测代码质量和代码风格;
  • StyleLint:检测样式代码质量和代码风格;
  • Prettier:检测代码风格;(ESLint、StyleLint调和剂,解决重复的规则【Error Loop】)
  • EditorConfig:统一不同IDE之间的配置差异,例如一个编辑器行缩进使用的是Tab,另一个编辑器使用的则是空格Space;

规范配置步骤

  • vue项目完整的规范配置,分为以下四步:
  1. 核心规则配置:各检测工具必要的规则配置文件,没有它们检测工具无法执行,如.eslintrc文件。
  2. 命令行脚本配置:package.json的script属性配置执行脚本,开发人员手动执行命令,如npm run lint。
  3. 开发环境配置:webpack开发环境构建配置中,添加开发环境运行时检测插件,类似于启动器,运行时自动执行各自的检测工具进行检测。
  4. 编辑器配置:VSCode编辑器安装插件并配置属性,实时进行检测。

第一步:

1. eslint配置 - .eslintrc.js

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
module.exports = {
root: true,
env: {
node: true,
browser: true,
},
extends: ['plugin:vue/essential', 'airbnb-base', 'plugin:prettier/recommended'],
parserOptions: {
parser: 'babel-eslint',
ecmaFeatures: {
jsx: true,
},
},
plugins: ['import'],
settings: {
// 别名
'import/resolver': {
alias: {
map: [['@', './src/']],
extensions: ['.js', '.vue'],
},
},
},
rules: {
'func-names': 0,
'import/extensions': 0,
'no-unused-expressions': 0,
/**
* 导入语句前不允许有任何非导入语句
*/
'import/first': 'error',
/**
* 禁止重复导入模块
*/
'import/no-duplicates': 'error',
/**
* 禁止使用 let 导出
*/
'import/no-mutable-exports': 'warn',
/**
* 禁用导入的模块时使用 webpack 特有的语法(感叹号)
*/
'import/no-webpack-loader-syntax': 'warn',
/**
* 当只有一个导出时,必须使用 export default 来导出
*/
'import/prefer-default-export': 'off',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: true,
},
],
'no-plusplus': [
'error',
{
allowForLoopAfterthoughts: true,
},
],
'import/no-dynamic-require': 0,
'global-require': 0,
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
},
};

1.1eslint忽略文件 .eslintignore

1
2
3
4
dist/**
es/**
lib/**
**/node_modules

2. stylelint配置

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
module.exports = {
// processors: ['stylelint-processor-html'],
// extends: ['stylelint-config-tx 替换tx','css-properties-sorting', 'stylelint-config-prettier'],
extends: ['css-properties-sorting', 'stylelint-config-prettier'],
plugins: ['stylelint-order'], // stylelint-order是CSS属性排序插件
rules: {
// "color-hex-case": "lower", // 颜色值为小写字母(stylelint-config-standard)
// "color-no-invalid-hex": true, // 颜色值不能为无效值(stylelint-config-standard)
'font-family-name-quotes': 'always-where-recommended', // 字体系列中命名时带引号
'function-url-quotes': 'always', // 地址一定要写引号
// 'number-leading-zero': 'never', // 或分数低于1的数字是否需要前导零
'number-no-trailing-zeros': true, // 禁止在数量尾随零
// 'string-quotes': 'double', // 指定字串
'length-zero-no-unit': true, // 禁止单位零长度。
'value-keyword-case': 'lower', // 指定小写关键字的值
'value-list-comma-newline-after': 'always-multi-line', // 在值列表的逗号后指定一个换行符或禁止留有空格
'shorthand-property-no-redundant-values': true, // 不允许在简写属性冗余值
// "property-case": "lower", // 为属性指定小写(stylelint-config-standard)
'keyframe-declaration-no-important': true, // 不允许!important在关键帧声明
// "block-closing-brace-empty-line-before": "never", // 不允许关闭括号前空一行(stylelint-config-standard)
// "block-closing-brace-newline-after": "always", // 需要一个换行符关闭括号后的空白(stylelint-config-standard)
// "block-opening-brace-newline-after": "always-multi-line", // 开括号的块之后需要新的一行(stylelint-config-standard)
'selector-class-pattern': "^[a-z]+([a-z0-9]?|[a-z0-9(-_'% ]*[a-z0-9)])$", // 兼容stylelint的bug(less的mixin没处理好), 暂时先这样处理~~~指定一个模式类选择符,限制选择器名称写法
'selector-id-pattern': "^[a-z]+([a-z0-9]?|[a-z0-9(-_'% ]*[a-z0-9)])$", // 兼容stylelint的bug(less的mixin没处理好), 暂时先这样处理~~~指定一个模式,id选择器,限制选择器名称写法
'no-empty-source': null, // 不允许空的来源
'at-rule-no-unknown': null, // 不允许at-rules不明
// "indentation": 2, // 指定缩进(stylelint-config-standard)
'max-nesting-depth': [5, { severity: 'warning' }], // 允许嵌套的深度为5
'no-duplicate-selectors': true, // 不允许重复的选择器
// "no-eol-whitespace": true, // 不允许行尾空白(stylelint-config-standard)
// "no-invalid-double-slash-comments": true // 不允许双斜杠注释(/ /…)不支持CSS(stylelint-config-standard)
'order/order': [
// 指定声明块内的内容顺序
['custom-properties', 'declarations'],
],
// "declaration-block-properties-order": "alphabetical",
'order/properties-order': [
// 指定声明块内属性的字母顺序
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'hyphens',
'src',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-attachment',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'border',
'border-collapse',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-color',
'border-image',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-spacing',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'border-radius-topright',
'border-radius-bottomright',
'border-radius-bottomleft',
'border-radius-topleft',
'content',
'quotes',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'zoom',
'transform',
'box-align',
'box-flex',
'box-orient',
'box-pack',
'box-shadow',
'box-sizing',
'table-layout',
'animation',
'animation-delay',
'animation-duration',
'animation-iteration-count',
'animation-name',
'animation-play-state',
'animation-timing-function',
'animation-fill-mode',
'transition',
'transition-delay',
'transition-duration',
'transition-property',
'transition-timing-function',
'background-clip',
'backface-visibility',
'resize',
'appearance',
'user-select',
'interpolation-mode',
'direction',
'marks',
'page',
'set-link-source',
'unicode-bidi',
'speak',
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep'],
},
],
},
};

3. prettier配置 - prettier.config.js

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
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾需要有逗号
trailingComma: 'all',
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// vue 文件中的 script 和 style 内不用缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf
endOfLine: 'lf',
};

4. EditorConfig部分:

editorConfig的作用是专门抹平不同编辑器同一配置之间差异。比较神奇的是它并不需要项目配依赖且配置及其简单,只需要编辑器安装指定扩展插件以及项目配置文件.editorConfig即可。

  • VSCode中安装插件:EditorConfig for VS Code
  • 项目根目录下创建并配置.editorConfig:
1
2
3
4
5
6
7
8
9
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

依赖清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 依赖清单
[
// ESLint
eslint,
babel-eslint,
eslint-plugin-vue,
eslint-webpack-plugin, // 开发环境实时检测(可选

// StyleLint(可选)
stylelint,
stylelint-webpack-plugin, // 开发环境实时检测
stylelint-config-standard,

// Prettier(可选)
prettier,
eslint-config-prettier,
eslint-plugin-prettier,
stylelint-config-prettier
]

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// ESLint
"eslint": "^6.7.2",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-vue": "^6.2.2",

// StyleLint(可选)
"stylelint": "^13.12.0",
"stylelint-order": "^4.1.0",

// Prettier(可选)
"prettier": "^2.3.2",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.3.1",
"stylelint-config-prettier": "^8.0.2",

第二步 - 命令行脚本配置

在package.json文件的script脚本中配置

1
2
3
4
5
6
7
{
// 省略其余属性...
"script": {
"eslint-lint": "eslint --ext .js,.vue --fix src",
"stylelint-lint": "stylelint src/**/.css src/**/.vue src/**/.less --fix"
}
}

【有待争议的规则点】

vue相关

1、vue/html-self-closing 自关闭标签
2、vue/attributes-order 属性顺序
3、vue/attribute-hyphenation 属性需用连字符分割单词,(这里不建议,父子组件使用时,无法直接搜索)