前言
大部分前端项目都配置Stylelint
、Eslint
、Tslint
和Prettier
四大前端代码校验工具。「代码校验工具」以下简称Lint
,为了解决代码不严谨,通过预设规则校验代码,检测其是否存在错误/漏洞
,并对错误/漏洞
提示修复方案并尽可能依据修复方案格式化出正确代码。该功能称为「格式化代码」,基本上所有编辑器都需配置该功能。
Lint
其实就是编辑器里运行的一个脚本进程,将代码解析成抽象语法树
,遍历抽象语法树
并通过预设规则做一些判断和修改,再将新的抽象语法树
转换成正确代码。整个校验过程都跟抽象语法树
相关,若暂未接触过抽象语法树
,可阅读babel源码
或eslint源码
了解其工作原理。
开发过程中启用Lint
能带来以下好处。
- 可强制规范
团队编码规范
,让新旧组员编码习惯得到一致提升 - 可灵活定制
团队编码风格
,让预设规则符合新旧组员心理预期 - 增加项目代码的
可维护性
和可接入性
,让新组员能快速适应项目的架构与需求 - 保障项目整体质量,可减少
无用代码
、重复代码
、错误代码
和漏洞代码
的产生几率
千万不能自私
有些同学可能一时适应不了Lint
带来的强制性操作,会在自己编辑器里关闭项目所有校验功能,这种自私行为会带来很严重的后果。
若上传无任何校验痕迹的代码块,当其他组员将该代码块更新合并到原有代码上时,由于编辑器一直配置着团队编码规范
,导致被拉下来的代码块立马报错甚至产生冲突。
上述情况会让其他组员花费更多时间解决因为你不遵守规矩而带来的问题,还浪费团队为了研究如何让整体编码风格更适合组员的精力。
这种自私行为不可取,若团队无任何编码规范可随意编码,若已认可团队编码规范
那就努力遵守,不给团队带来麻烦。
背景
本文着重讲解「一键格式化代码」的部署,像Lint
常用配置就不会讲解,毕竟百度谷歌一搜一大堆。这个「一键」当然是ctrl s
或cmd s
保存文件啦。在保存文件时触发Lint
自动格式化代码,这个操作当然不能100%
保证将代码格式化出最正确代码,而是尽可能依据修复方案格式化出正确代码。言下之意就是可能存在部分代码格式化失败,但将鼠标移至红色下划线上会提示修复方案,此时可依据修复方案自行修正代码。
为何写下本文?笔者有着严谨的代码逻辑和优雅的编码风格,所以特别喜欢格式化代码。然而又不想为每个项目配置Lint
,这些重复无脑的复制粘贴让笔者很反感,所以笔者只想一次配置全局运行Lint
,这样就无需为每个项目配置Lint
。在大量百度谷歌都未能搜到一篇相关文章(搜到的全部文章都是单独为一个项目配置,害
),笔者就花了半年多时间探讨出本方案,真正做到「一次配置全局运行」。若使用本方案,相信能将所有项目的Stylelint
、Eslint
、Tslint
和Prettier
相关依赖和配置文件全部移除,使项目目录变得超级简洁,如同下图。
笔者选用VSCode
作为前端开发的编辑器,其他编辑器不是性能差就是配置麻烦,所以统统放弃,只认VSCode
。
在此强调两个重要问题,这两个问题影响到后面能否成功部署VSCode
的「一键格式化代码」。
Tslint
官方已宣布废弃Tslint
,改用Eslint
代替其所有校验功能Eslint
部分配置与Prettier
部分配置存在冲突且互相影响,为了保证格式化性能就放弃接入Prettier
所以部署VSCode
的「一键格式化代码」只需安装Stylelint
和Eslint
两个插件。为了方便表述,统一以下名词。
- 以下提及的「Stylelint」和「Eslint」均为
VSCode插件
- 以下提及的「stylelint」和「eslint」均为
NPM依赖
步骤
前方高能,两大步骤就能为VSCode
部署「一键格式化代码」,请认真阅读喔!
安装依赖
为了搞清楚两个插件集成哪些NPM依赖
,以下区分安装stylelint
和eslint
及其相关依赖(「看看即可,不要安装,重点在后头」)。笔者有个习惯,就是喜欢将依赖更新到最新版本,在享受新功能的同时也顺便填坑。
# Stylelint
npm i -D stylelint stylelint-config-standard stylelint-order
代码语言:javascript复制# Eslint
npm i -D eslint babel-eslint eslint-config-standard eslint-plugin-html eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-react eslint-plugin-standard eslint-plugin-vue vue-eslint-parser
代码语言:javascript复制# TypeScript Eslint
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser typescript eslint-config-standard-with-typescript
安装完成后需配置多份对应配置文件,CSS方面有css/scss/less/vue
文件,JS方面有js/ts/jsx/tsx/vue
文件。查看插件文档,发现Stylelint
只能在settings.json
上配置,而Eslint
可配置成多份对应配置文件,并在settings.json
上通过特定字段指定Eslint
配置文件路径。
settings.json是VSCode的配置文件,用户可通过插件暴露的字段自定义编辑器功能。
由于配置文件太多不好管理,笔者开源了自己平常使用的配置文件集合,详情可查看vscode-lint。
- demo:随便捣鼓几个Demo用于测试格式化代码
- eslintrc.js:校验
js文件
- eslintrc.react.js:校验
jsx文件
- eslintrc.vue.js:校验
vue文件
- tsconfig.json:配置
TypeScript
- tslintrc.js:校验
ts文件
- tslintrc.react.js:校验
tsx文件
- tslintrc.vue.js:校验
vue文件
配置文件里的rule
可根据自己编码规范适当调整,在此不深入讲解,毕竟简单得来谁都会。建议使用vscode-lint
,若校验规则不喜欢可自行调整。
- 配置
Stylelint
请戳这里 - 配置
Eslint
请戳这里 - 配置
TypeScriptEslint
请戳这里 - 配置
VueEslint
请戳这里
以下会基于vscode-lint
部署VSCode
的「一键格式化代码」,找个目录通过git
克隆一份vscode-lint
,并安装其NPM依赖
。若使用vscode-lint
,上述依赖就不要安装了?。
git clone https://github.com/JowayYoung/vscode-lint.git
cd vscode-lint
npm i
配置插件
- 打开
VSCode
- 选择左边
工具栏
的插件
,搜索并安装Stylelint
和Eslint
,安装完成后重启VSCode
- 选择
文件 → 首选项 → 设置
,设置
里可选用户
和工作区
- 「用户」:配置生效后会作用于全局项目(
若大部分项目都是单一的React应用或Vue应用推荐使用全局配置
) - 「工作区」:配置生效后只会作用于当前打开项目
- 「用户」:配置生效后会作用于全局项目(
- 点击
设置
右上角中间图标打开设置(json)
,打开的对应文件是settings.json
(上述有提及) - 插入以下配置:若在
用户
选项下插入以下配置,遇到其他项目需覆盖配置时在工作区
选项下插入eslint.options.configFile
指定Eslint
配置文件路径 - 重启
VSCode
:为了保障每次修改配置后都能正常格式化代码,必须重启VSCode
{
"css.validate": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"eslint.nodePath": "path/vscode-lint/node_modules",
"eslint.options": {
"configFile": "path/vscode-lint/eslintrc.js"
},
"less.validate": false,
"scss.validate": false,
"stylelint.configBasedir": "path/vscode-lint",
"stylelint.configOverrides": {
"extends": "stylelint-config-standard",
"plugins": [
"stylelint-order"
],
"rules": {
"at-rule-empty-line-before": "never",
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"content",
"each",
"error",
"extend",
"for",
"function",
"if",
"include",
"mixin",
"return",
"while"
]
}
],
"color-hex-case": "lower",
"comment-empty-line-before": "never",
"declaration-colon-newline-after": null,
"declaration-empty-line-before": "never",
"function-linear-gradient-no-nonstandard-direction": null,
"indentation": "tab",
"no-descending-specificity": null,
"no-missing-end-of-source-newline": null,
"no-empty-source": null,
"number-leading-zero": "never",
"rule-empty-line-before": "never",
"order/order": [
"custom-properties",
"declarations"
],
"order/properties-order": [
// 布局属性
"display",
"visibility",
"overflow",
"overflow-x",
"overflow-y",
"overscroll-behavior",
"scroll-behavior",
"scroll-snap-type",
"scroll-snap-align",
// 布局属性:浮动
"float",
"clear",
// 布局属性:定位
"position",
"left",
"right",
"top",
"bottom",
"z-index",
// 布局属性:列表
"list-style",
"list-style-type",
"list-style-position",
"list-style-image",
// 布局属性:表格
"table-layout",
"border-collapse",
"border-spacing",
"caption-side",
"empty-cells",
// 布局属性:弹性
"flex-flow",
"flex-direction",
"flex-wrap",
"justify-content",
"align-content",
"align-items",
"align-self",
"flex",
"flex-grow",
"flex-shrink",
"flex-basis",
"order",
// 布局属性:多列
"columns",
"column-width",
"column-count",
"column-gap",
"column-rule",
"column-rule-width",
"column-rule-style",
"column-rule-color",
"column-span",
"column-fill",
"column-break-before",
"column-break-after",
"column-break-inside",
// 布局属性:格栅
"grid-columns",
"grid-rows",
// 尺寸属性
"box-sizing",
"margin",
"margin-left",
"margin-right",
"margin-top",
"margin-bottom",
"padding",
"padding-left",
"padding-right",
"padding-top",
"padding-bottom",
"border",
"border-width",
"border-style",
"border-color",
"border-colors",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"border-left-colors",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-right-colors",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-top-colors",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-bottom-colors",
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-left-radius",
"border-bottom-right-radius",
"border-image",
"border-image-source",
"border-image-slice",
"border-image-width",
"border-image-outset",
"border-image-repeat",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
// 界面属性
"appearance",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"outline-radius",
"outline-radius-topleft",
"outline-radius-topright",
"outline-radius-bottomleft",
"outline-radius-bottomright",
"background",
"background-color",
"background-image",
"background-repeat",
"background-repeat-x",
"background-repeat-y",
"background-position",
"background-position-x",
"background-position-y",
"background-size",
"background-origin",
"background-clip",
"background-attachment",
"bakground-composite",
"mask",
"mask-mode",
"mask-image",
"mask-repeat",
"mask-repeat-x",
"mask-repeat-y",
"mask-position",
"mask-position-x",
"mask-position-y",
"mask-size",
"mask-origin",
"mask-clip",
"mask-attachment",
"mask-composite",
"mask-box-image",
"mask-box-image-source",
"mask-box-image-width",
"mask-box-image-outset",
"mask-box-image-repeat",
"mask-box-image-slice",
"box-shadow",
"box-reflect",
"filter",
"mix-blend-mode",
"opacity",
"object-fit",
"clip",
"clip-path",
"resize",
"zoom",
"cursor",
"pointer-events",
"user-modify",
"user-focus",
"user-input",
"user-select",
"user-drag",
// 文字属性
"line-height",
"line-clamp",
"vertical-align",
"direction",
"unicode-bidi",
"writing-mode",
"ime-mode",
"text-overflow",
"text-decoration",
"text-decoration-line",
"text-decoration-style",
"text-decoration-color",
"text-decoration-skip",
"text-underline-position",
"text-align",
"text-align-last",
"text-justify",
"text-indent",
"text-stroke",
"text-stroke-width",
"text-stroke-color",
"text-shadow",
"text-transform",
"text-size-adjust",
"src",
"font",
"font-family",
"font-style",
"font-stretch",
"font-weight",
"font-variant",
"font-size",
"font-size-adjust",
"color",
// 内容属性
"tab-size",
"overflow-wrap",
"word-wrap",
"word-break",
"word-spacing",
"letter-spacing",
"white-space",
"caret-color",
"quotes",
"content",
"content-visibility",
"counter-reset",
"counter-increment",
"page",
"page-break-before",
"page-break-after",
"page-break-inside",
// 交互属性
"will-change",
"perspective",
"perspective-origin",
"backface-visibility",
"transform",
"transform-origin",
"transform-style",
"transition",
"transition-property",
"transition-duration",
"transition-timing-function",
"transition-delay",
"animation",
"animation-name",
"animation-duration",
"animation-timing-function",
"animation-delay",
"animation-iteration-count",
"animation-direction",
"animation-play-state",
"animation-fill-mode",
// Webkit专有属性
"-webkit-overflow-scrolling",
"-webkit-box-orient",
"-webkit-line-clamp",
"-webkit-text-fill-color",
"-webkit-tap-highlight-color",
"-webkit-touch-callout",
"-webkit-font-smoothing",
"-moz-osx-font-smoothing"
]
}
}
}
以上配置的path
为vscode-lint
所在的根目录,若刚才的vscode-lint
克隆到E:/Github
,那么path
就是E:/Github
。
示例
上述步骤完成后就可愉快敲代码了。每次保存文件就会自动格式化CSS代码
或JS代码
,这个格式化代码不仅会将代码按照规范整理
和排序
,甚至尽可能依据修复方案格式化出正确代码。
这样就无需为每个项目配置Lint
,将所有项目的Stylelint
、Eslint
、Tslint
和Prettier
相关依赖和配置文件全部移除,使项目目录变得超级简洁。
css/scss/less/vue文件
js/ts/jsx/tsx/vue文件
疑问
更新eslint到v6 就会失效
很多同学反映eslint v6
在VSCode
上失效,最高版本只能控制在v5.16.0
。其实这本身就是配置问题,跟版本无关。vscode-lint
的eslint
使用v7
照样能使用Eslint
,只要配置正确就能正常使用。
上述安装行为使用了NPM
,那么settings.json
的eslint.packageManager
必须配置为npm
(小写),但最新版本Eslint
已默认此项,所以无需配置。若上述安装行为变成yarn install
,那么必须在settings.json
里添加以下配置。
{
"eslint.packageManager": "yarn"
}
这个配置就是解决该问题的关键了。
首次安装Eslint并执行上述配置就会失效
首次安装Eslint
可能会在js/ts/jsx/tsx/vue
文件里看到以下警告。
Eslint is disabled since its execution has not been approved or denied yet. Use the light bulb menu to open the approval dialog.
说明Eslint
被禁用了,虽然配置里无明确的禁用字段,但还是被禁用了。此时移步到VSCode
右下角的工具栏,会看到禁用图标 ESLINT
的标红按钮,单击它会弹出一个弹框,选择Allow Everywhere
就能启用Eslint
所有校验功能。
总结
整体过程看似简单,其实笔者这半年填了很多坑才有了vscode-lint
,中间已省略了很多未记录的问题,这些疑问不重要却影响到很多地方。相信本文能让很多同学体验VSCode
一键格式化代码所带来的快感,最关键的部分还是无需为每个项目配置Lint
,这省下多少时间和精力呀!觉得牛逼给vscode-lint点个「Star」吧!