本文已参与「新人创作礼」活动,一起开启掘金创作之路。
在多人协同开发中,如何确保代码的语法和风格统一,成为了一个不可避免的问题。本文将介绍在 Vue3 + TypeScript 的项目中如何实现统一的代码规范。
ESLint + Prettier
首先我们来了解一下 ESLint 与 Prettier
ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。
Prettier 是一个代码格式化工具。
ESLint 与 Prettier 通常是一起使用的,前者只负责检测修复语法规则,后者负责代码格式化。
安装
首先,我们需要安装以下依赖
pnpm i eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier -D
| 依赖 | 版本 | 作用 |
|---|---|---|
| eslint | 8.13.0 | ESLint 核心库 |
| eslint-config-prettier | 8.5.0 | 关掉所有和 Prettier 冲突的 ESLint 的配置 |
| eslint-plugin-prettier | 4.0.0 | 将 Prettier 的 rules 以插件的形式加入到 ESLint 里面 |
| eslint-plugin-vue | 8.6.0 | 为 Vue 使用 ESlint 的插件 |
| @typescript-eslint/eslint-plugin | 5.19.0 | ESLint 插件,包含了各类定义好的检测 TypeScript 代码的规范 |
| @typescript-eslint/parser | 5.19.0 | ESLint 的解析器,用于解析 TypeScript,从而检查和规范 TypeScript 代码 |
| prettier | 2.6.2 | Prettier 核心库 |
配置 ESLint
安装完成之后,在根目录下创建 .eslintrc.js 文件,并添加以下代码
// @ts-check
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true
}
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended'
],
rules: {
'vue/script-setup-uses-vars': 'error',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'no-unused-vars': 'error',
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off',
'vue/multi-word-component-names': 'off'
}
}
我们会看到,在配置文件里面,有 extends 这个字段,这个字段可以理解为别人已经配置好的 ESlint 规则,我们可以直接拿来用,如果有多个规则重复,则后面的会覆盖前面的,所以引入顺序很重要。
当然,如果对 extends 的引入的规则都不满意的话,可以在 rules 中进行某个规则的设置。
rules 可以根据团队的情况自行配置,更多的配置请查看 eslint-plugin-vue 以及 @typescript-eslint/eslint-plugin
如果对某个文件或者目录下的文件不检测,可以在根目录下创建 .eslintignore 文件,然后添加对应的文件名或路径即可,如不检测 dist 目录下的所有文件。
/dist*
配置 Prettier
在根目录下创建 prettier.config.js 文件,并添加以下代码
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: false,
vueIndentScriptAndStyle: false,
singleQuote: true,
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'none',
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
rangeStart: 0
}
更多 Prettier 配置可以查看官方文档 www.prettier.cn/docs/option…
或者通过在线配置 www.prettier.cn/playground/ ,选择对应的语言及选项,确认好配置点击 Copy config JSON 进行复制,然后粘贴到项目中来即可。
同理,如果对某个文件或者某个路径下的文件不进行检测的话,在根目录下创建 .prettierignore 并添加对应文件名或路径,如
/dist*
修复
配置完 ESlint 和 Prettier 之后,我们在 package.json 中去添加对应的修复命令
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\""
然后运行 npm run lint:eslint 执行 ESlint 的检测修复,运行 npm run lint:format 执行 Prettier 代码格式化。
这样就可以显示代码的语法和风格检测修复了,如果能修复的,插件自动修复,没办法修复的,需要自己手动进行修复。
实时检测修复
代码的语法和风格检测,都是需要运行对应命令才能进行的,那么如何实时检测呢,这里需要使用到一个插件 vite-plugin-eslint
首先安装 vite-plugin-eslint
pnpm i vite-plugin-eslint -D
然后在 vite.config.ts 中添加以下代码
+ import EslintPlugin from 'vite-plugin-eslint'
export default defineConfig({
plugins: [
// ...
+ EslintPlugin({
+ cache: false,
+ include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
+ })
]
})
这样,在我们保存的时候,控制台上就能看到相对应的错误了。
配合编辑器
我们也可以在代码编辑器中安装相应的扩展,然后保存的时候自动帮我们进行代码格式化,这里以 VScode 为例。
安装 Prettier ESLint 、 Prettier 、 ESLint 这三个扩展,并且设置 Format on Save 为 true,这样在我们代码保存的时候,编辑器就能自动帮我们进行格式化代码了。
StyleLint
以上我们针对的都是 template 与 javaScript ,在样式中,也同样可以通过插件的形式来实现代码风格的统一。
安装
首先安装以下依赖
pnpm i stylelint stylelint-config-html stylelint-config-prettier stylelint-config-recommended stylelint-config-standard stylelint-order postcss-html -D
| 依赖 | 版本 | 作用 |
|---|---|---|
| stylelint | 14.6.1 | StyleLint 核心库 |
| stylelint-config-html | 1.0.0 | 解析 HTML 文件中的样式 |
| stylelint-config-prettier | 9.0.3 | 结合 Prettier 使用 |
| stylelint-config-recommended | 7.0.0 | 用来检验 CSS 文件 |
| stylelint-config-standard | 25.0.0 | StyleLint 的标准可共享配置 |
| stylelint-order | 5.0.0 | 顺序包,用于排列 CSS 属性的顺序 |
| stylelint-order | 5.0.0 | 用于解析 HTML(和类似 HTML)的 PostCSS语法 |
配置
在根目录下创建 stylelint.config.js 文件,并添加以下代码
module.exports = {
root: true,
plugins: ['stylelint-order'],
customSyntax: 'postcss-html',
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global', 'deep']
}
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin']
}
],
'no-empty-source': 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': null,
'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'
}
],
// Specify the alphabetical order of the attributes in the declaration block
'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'
]
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
overrides: [
{
files: ['*.vue', '**/*.vue', '*.html', '**/*.html'],
extends: ['stylelint-config-recommended', 'stylelint-config-html'],
rules: {
'keyframes-name-pattern': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted']
}
]
}
}
]
}
rules 字段可以根据团队情况进行配置,如果需要忽略某个文件或者某个目录下的文件,则可以在根目录下创建 .stylelintignore 文件,如
/dist*
接着就可以在 package.json 中配置检测修复命令了
"lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/"
运行 npm run lint:style 即可查看效果
总结
到这里,我们就实现了统一代码语法和风格的配置了,从而保证代码的可读性。
如果想要查看相应代码,可查看 vue-element-plus-admin