前端使用工具强制实行代码规范(Vue)
本文最后更新于:2021年8月4日 晚上
场景
非强制性的规范都不会成为真正的规范。在之前,我们也会在团队内编写一些开发规范文档,却总是不能真正有效的执行下去,原因何在?
- 其一,人是健忘的,就算真正看完了规范文档,也不可能记住每一条规范,并且在代码中正确实践。
- 其二,人是懒惰的,即便有着规范,但只要写代码的时候没有强制性,那总是不会在意的。
那么,使用自动化的工具是为首选,而前端恰好有了一些可用的工具来帮助我们。
ESLint
ESLint 是一个对 JS 低级错误进行修复的工具,甚至于,现在连 TypeScript 官方也抛弃 TSLint 转向这个工具了,因为它的生态更大,实现的规则数量远远多于 TSLint,能把 JS 中的大部分低级错误检测出来,并能自动修复其中一部分。
安装依赖
npm i -D eslint babel-eslint eslint-plugin-vue @vue/cli-plugin-eslint
依赖解释
eslint
: 本体包babel-eslint
: eslint 与 babel 整合包eslint-plugin-vue @vue/cli-plugin-eslint
: eslint 与 vue 整合包
进行配置
// .eslintrc.js
module.exports = {
root: true,
//环境
env: {
browser: true,
commonjs: true,
es6: true,
node: true,
},
extends: [
//继承 vue 的标准特性
"plugin:vue/essential",
"eslint:recommended",
],
rules: {
/**
* 禁止不需要的括号,例如 const i = (1 + 1),但该规则存在的问题是会认为类两侧的圆括号也是不合法的
* 例如: billId => (StringValidator.isBlank(billId) ? '否' : '是')
*/
// 'no-extra-parens': 'error',
/**
* 禁止魔法值,该规则的主要问题是很多误报
* 例如: offset / size + (offset % size === 0 ? 0 : 1)
*/
"no-magic-numbers": "off",
//禁止使用 var,强制要求使用 const/let
"no-var": "error",
//不使用未定义的变量
"no-use-before-define": "error",
//不允许在循环中使用 await,请使用 Promise.all
"no-await-in-loop": "error",
//不允许使用 return await,直接返回 Promise 就好
"no-return-await": "error",
//不允许使用 console 对象,因为会打印到控制台上
"no-console": "error",
//使用 class 中的方法必须使用 this. 前缀
// 'class-methods-use-this': 'error',
//禁止使用 alert, confirm, prompt,该 API 会阻断所有其他操作,但该规则存在的问题是有可能需要之后用上方便统一调用呢?
"no-alert": "error",
//禁止使用 eval,该操作是危险的
"no-eval": "error",
"no-implied-eval": "error",
//禁止使用 new Function 创建函数
"no-new-func": "error",
//禁止使用包装类 String, Number, Boolean
"no-new-wrappers": "error",
//禁止把语句作为计算结果返回,请使用两条语句
"no-return-assign": "error",
//禁止使用不应该的 concat 连接,字符串请使用 + 连接,数组则可以使用 [...arr1, ...arr2]
"no-useless-concat": "error",
//禁止 yoda 比较,不要用 1 === i 而是用 i === 1 更加自然
yoda: "error",
//禁止没有用的三元运算符,就算是 ⑨ 也知道这样做有问题 answer === 1 ? true : false
"no-unneeded-ternary": "error",
//禁止无用的计算属性 const obj = { ['1']: 1 }
"no-useless-computed-key": "error",
//如果可以使用解构,那就进行警告,例如 const name = user.name 就应该被替换为 const { name } = user,避免了重复声明,也能进行默认赋值等操作
"prefer-destructuring": "warn",
//使用 rest 不定参数代替全局变量 arguments
"prefer-rest-params": "error",
//使用扩展运算符代替 apply 调用
"prefer-spread": "error",
//使用 Symbol 必须使用描述说明它要做什么
"symbol-description": "error",
//如果可以使用反射,那就是用反射调用,Reflect 代替 delete 关键字删除对象属性
"prefer-reflect": "warn",
},
parserOptions: {
//使用 babel 解析语法
parser: "babel-eslint",
//使用 es2017 的语法
ecmaVersion: 2017,
},
};
如果有需要忽略的文件也可以在 .eslintignore 文件中进行配置
// .eslintignore
`// 忽略掉 TypeScript 类型定义文件
*/**/*.d.ts`
添加脚本
// package.json
{
// 其他配置。。。
"scripts": {
"lint:js": "vue-cli-service lint",
"fix:js": "vue-cli-service lint --fix"
}
// 其他配置。。。
}
运行脚本
npm run lint:js
现在,你可以检测到代码中的问题,并修复它了。
相关链接
WebStorm 配置 ESLint 即时检查:
StyleLint
不建议使用,本身只是对 css 代码的排序,而且很多时候只提出问题但不负责解决问题(那么只有解决提出问题的工具了 xd),或许使用 postcss 是个更好的选择。
StyleLint 是一个用来对 CSS 进行校验/修复的工具,和 ESLint 类似,但却针对 CSS 方面。我们使用它用来避免一些不好的 CSS 写法,也能避免 code review 时被其他人吐槽。。。
安装依赖
npm i -D stylelint stylelint-config-standard stylelint-order stylelint-scss
依赖解释
stylelint
: 本体包stylelint-config-standard
: stylelint 标准配置stylelint-scss
: stylelint scss 支持插件包stylelint-order
: stylelint 属性排序插件
进行配置
// stylelint.config.js
module.exports = {
plugins: ["stylelint-scss", "stylelint-order"],
extends: ["stylelint-config-standard"],
rules: {
/**
* 关键问题在于是否要忽略空检查
* 下面两种都会被判断为错误
* <style lang="scss" scoped></style>
* a {}
* 上面一种是很常见的
*/
// 'no-empty-source': null,
//禁止注释两侧出现空白(IDEA 默认就没有空白)
"comment-whitespace-inside": "never",
//scss 的特定符号将被 css 检查时忽略而在 scss 检查时才会生效
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
//CSS 声明定义顺序,自定义 class 放在默认元素上面
"order/order": ["custom-properties", "declarations"],
/**
* CSS 属性顺序
* 1. 控制外部属性
* 2. 盒模型
* 3. 视觉
* 4. 其他
* 5. 未定义
*/
"order/properties-order": [
[
//指令
"composes",
"@import",
"@extend",
"@mixin",
"@at-root",
//盒模型相关
"display",
"flex",
"flex-basis",
"flex-direction",
"flex-flow",
"flex-grow",
"flex-shrink",
"flex-wrap",
"grid",
"grid-area",
"grid-auto-rows",
"grid-auto-columns",
"grid-auto-flow",
"grid-gap",
"grid-row",
"grid-row-start",
"grid-row-end",
"grid-row-gap",
"grid-column",
"grid-column-start",
"grid-column-end",
"grid-column-gap",
"grid-template",
"grid-template-areas",
"grid-template-rows",
"grid-template-columns",
"gap",
"align-content",
"align-items",
"align-self",
"justify-content",
"justify-items",
"justify-self",
"order",
"float",
"clear",
"box-sizing",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
//定位相关
"position",
"top",
"right",
"bottom",
"left",
"z-index",
//边框
"border",
"border-color",
"border-style",
"border-width",
"border-top",
"border-top-color",
"border-top-width",
"border-top-style",
"border-right",
"border-right-color",
"border-right-width",
"border-right-style",
"border-bottom",
"border-bottom-color",
"border-bottom-width",
"border-bottom-style",
"border-left",
"border-left-color",
"border-left-width",
"border-left-style",
"border-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",
"border-collapse",
"border-spacing",
//溢出
"object-fit",
"object-position",
"overflow",
"overflow-x",
"overflow-y",
//版式
"color",
"font",
"font-weight",
"font-size",
"font-family",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"line-height",
"direction",
"letter-spacing",
"white-space",
"text-align",
"text-align-last",
"text-transform",
"text-decoration",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"text-justify",
"text-outline",
"text-wrap",
"text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"text-orientation",
"text-shadow",
"vertical-align",
"word-wrap",
"word-break",
"word-spacing",
"overflow-wrap",
"tab-size",
"hyphens",
"unicode-bidi",
"columns",
"column-count",
"column-fill",
"column-gap",
"column-rule",
"column-rule-color",
"column-rule-style",
"column-rule-width",
"column-span",
"column-width",
"page-break-after",
"page-break-before",
"page-break-inside",
"src",
//视觉
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"table-layout",
"empty-cells",
"caption-side",
"background",
"background-color",
"background-image",
"background-repeat",
"background-position",
"background-position-x",
"background-position-y",
"background-size",
"background-clip",
"background-origin",
"background-attachment",
"background-blend-mode",
//动画
"transition",
"transition-delay",
"transition-timing-function",
"transition-duration",
"transition-property",
"animation",
"animation-name",
"animation-duration",
"animation-play-state",
"animation-timing-function",
"animation-delay",
"animation-iteration-count",
"animation-direction",
"animation-fill-mode",
//其他
"appearance",
"content",
"clip",
"clip-path",
"counter-reset",
"counter-increment",
"resize",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"pointer-events",
"quotes",
"touch-action",
"will-change",
"zoom",
"fill",
"fill-rule",
"clip-rule",
"stroke",
],
{
unspecified: "bottom",
},
],
//CSS 属性值顺序
// 'order/properties-alphabetical-order': [],
},
};
添加脚本
// package.json
{
// 其他配置。。。
"scripts": {
"lint:css": "stylelint src/**/*.{vue,html,css,scss,sass}",
"fix:css": "stylelint --fix src/**/*.{vue,html,css,scss,sass}"
}
// 其他配置。。。
}
运行脚本
npm run lint:css
相关链接
WebStorm 使用 WebStorm 配置 StyleLint 即时检查:
参考 https://stackoverflow.com/questions/54304313/
添加外部工具以进行快速修复
然后添加一个快捷键即可
Prettier
Prettier 是一个代码格式化工具,但并非针对一种语言,对 HTML/CSS/JavaScript/Vue/SCSS
都有效果。可以通过配置文件在不同项目间统一代码格式化,以修正不同编辑器/IDE 之间格式化不同的问题。
安装依赖
npm i -D prettier eslint-plugin-prettier eslint-config-prettier prettier-eslint-cli stylelint-config-prettier stylelint-prettier
依赖解释
prettier
: 本体包eslint-plugin-prettier eslint-config-prettier prettier-eslint-cli
: prettier 与 eslint 整合包stylelint-config-prettier stylelint-prettier
: prettier 与 stylelint 整合包
进行配置
// .prettierrc.js
module.exports = {
// 缩进宽度
tabWidth: 4,
// 单行最大宽度
printWidth: 120,
// 去掉代码结尾的分号
semi: false,
// 使用单引号替代双引号
singleQuote: true,
// 尽量在所有地方都添加尾逗号
trailingComma: "all",
// 换行符
endOfLine: "crlf",
};
还需要修改 eslint 与 stylelint 的一些配置
// .eslintrc.js
module.exports = {
// 其他配置。。。
extends: [
//继承 vue 的标准特性
"plugin:vue/essential",
"eslint:recommended",
//避免与 prettier 冲突
"plugin:prettier/recommended",
],
// 其他配置。。。
};
// stylelint.config.js
module.exports = {
// 其他配置。。。
extends: ["stylelint-config-standard", "stylelint-config-prettier"],
// 其他配置。。。
};
添加脚本
// package.json
{
// 其他配置。。。
"scripts": {
"format": "prettier-eslint --write \"src/**/*.{js,vue,html,scss,css}\""
}
// 其他配置。。。
}
运行脚本
npm run format
相关链接
WebStorm 配置使用 Prettier 快速格式化:
建议修改为全局使用 Prettier 格式化,避免记忆两个格式化快捷键
husky 与 lint-staged
强制使用 linter 检查代码,不通过检查则无法提交代码,以使 linter 真正得到有效执行。
安装依赖
npm i -D husky lint-staged
依赖解释
husky
: 在项目中添加 git 钩子,在 git 各个生命周期(姑且这样称呼吧)中执行一些自定义操作。我们这里主要是用在 git 提交之前执行 linter 操作,不通过则提交无效。lint-staged
: 简而言之,就是只针对 git 提交的文件进行一些操作,而非整个项目的所有文件。我们这里主要是用在 git 提交之前进行 linter 时只针对提交的文件,以进行渐进式的重构。
进行配置
// .huskyrc.js
module.exports = {
hooks: {
// git commit 前的钩子
"pre-commit": "lint-staged",
// 修复 IDEA 的一些奇怪问题 <https://youtrack.jetbrains.com/issue/IDEA-135454>
"post-commit": "git update-index --again",
},
};
// lint-staged.config.js
module.exports = {
"src/**/*.{js,vue}": ["eslint --fix", "git add"],
"src/**/*.{vue,html,css,scss,sass}": ["stylelint --fix", "git add"],
"src/**/*.{js,vue,html,css,scss,sass}": [
"prettier-eslint --write",
"git add",
],
};
有人说 lint-staged 并行运行多个命令可能会有问题,因为 nodejs 本身在写入文件时不会加锁,导致多线程下可能存在问题(吾辈目前还没遇到过.JPG)
之后,在我们使用 git commit
时就会触发 lint 操作了!
相关链接
总结
基本上,这些工具初次配置起来还是非常麻烦的,但这是一件一劳永逸的事情,所以还是值得花时间去做的。
注: 目前还存在的问题是: ESLint 检测出来的部分错误能使用 A-Enter 修复 Prettier 与 WebStorm 自身格式化不能共存(自动切换) Prettier 在 WebStorm 中无法直接配置导致上面问题存在的必要性 StyleLint 不能使用 A-Enter 修复且不能与 WebStorm 共存可能的解决方案是找一下是否有一种方式能够让 WebStorm 一个快捷键执行多条命令,或者,写一个可用的插件。
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!