前端代码整洁之道:lint 标准化

在新公司的参与的第一个项目就是重构一个内部的管理平台,在第一次看源码的时候就发现各类编码风格都有,已经有着往“屎山”发展的趋势了,所以想着对项目进行编码规范和编码风格做一个统一。统一规范的格式不仅看着舒服了一些,还可以提前检查出一些潜在的问题、增强项目的维护性,何乐而不为呢?这周正好完成了风格规范的统一,下面也借机梳理一下使用到的代码检查工具(简称为 lint 工具)的配置流程,如果想要了解具体的配置项介绍可以参考各个工具的官网

重构的项目主要是 Vue3 相关的技术栈,主要使用到 lint 工具包括:

  • Editor Config :解决不同 IDE 编辑器编码风格不统一问题
  • Prettier:代码格式化工具
  • ESLint:JS / TS 代码检查和修复工具
  • StyleLint:CSS 代码检查和格式化工具

Editor Config 和 Prettier 配置

首先是配置 Editor Config ,在项目根目录下新建一个 .editorconfig 文件,配置文件中定义好编码规范可以了使用了,我在项目中使用到的 Editor Config 配置如下,主要定义了字符集、缩进、换行的风格

1# 表示是最顶层的 EditorConfig 配置文件 2root = true 3 4# 表示所有文件适用 5[*] 6charset = utf-8 # 设置文件字符集为 utf-8 7indent_style = space # 缩进风格(tab | space) 8indent_size = 2 # 缩进大小 9end_of_line = lf # 控制换行类型(lf | cr | crlf) 10trim_trailing_whitespace = true # 去除行首的任意空白字符 11insert_final_newline = true # 始终在文件末尾插入一个新行 12 13[*.{yml,yaml,json}] 14indent_style = space 15indent_size = 2 16 17[*.md] 18max_line_length = off 19trim_trailing_whitespace = false 20 21[Makefile] 22indent_style = tab

接下来是 Prettier 配置,首先引入依赖,在项目根目录创建 prettier 配置文件和需要忽略检查的文件

1pnpm i prettier -D # 引入 Prettier 依赖 2 3echo {}> .prettierrc.js # 目录新建 .prettierrc.js 配置文件 4echo > .prettierignore # 新建 .prettierignore 不需要被格式化的文件放在这里

.prettierrc.js 主要是定义与代码风格相关的内容,我习惯于定义好一套比较齐全的配置,便于后期维护,具体配置和介绍如下

1// .prettierrc.js 2module.exports = { 3 // 单行代码超出 100 个字符自动换行 4 printWidth: 100, 5 // 一个 tab 键缩进相当于 2 个空格 6 tabWidth: 2, 7 // 行缩进使用 tab 键代替空格 8 useTabs: false, 9 // 每一条语句后面添加分号 10 semi: true, 11 // 使用单引号 12 singleQuote: false, 13 // 仅仅当必须的时候才会加上双引号 14 quoteProps: 'as-needed', 15 // JSX 中使用单引号 16 jsxSingleQuote: false, 17 // 多行用逗号分隔的句法,未尾添加逗号(符合es5规范) 18 trailingComma: 'es5', 19 // 在括号和对象的文字之间加上一个空格 20 bracketSpacing: true, 21 // 多行的 JSX 对象结尾的 > 放在结尾同一行 22 bracketSameLine: false, 23 // 箭头函数,只有一个参数的时候,也需要括号 24 arrowParens: 'always', 25 // 格式化文档的某一部分,默认为全部 26 rangeStart: 0, 27 rangeEnd: Infinity, 28 // 对于 .vue 文件,缩进 <script> 和 <style> 里的内容 29 vueIndentScriptAndStyle: true, 30 // 不需要写文件开头的 @prettier 31 insertPragma: false, 32 // 不需要在文件开头插入 @prettier 33 requirePragma: false, 34 // 使用默认折行标准 35 proseWrap: 'preserve', 36 // 根据显示样式决定 html 要不要折行 37 htmlWhitespaceSensitivity: 'css', 38 // 换行符使用 lf 39 endOfLine: 'lf', 40}

.prettierignore 主要是定义 prettier 不需要格式化的文件,下面是我用到的配置

1/dist/* 2.local 3.output.js 4/node_modules/** 5 6**/*.svg 7**/*.sh 8 9/public/*

第三步是安装 Prettier 插件,在 VSCode 中 Prettier 插件在项目根目录中有 .editorconfig 文件和.prettierrc.js 文件中的一个时,就会优先读取项目中的配置,如果两个文件都没有的话才会读取 VSCode 的配置

安装好插件后,建议在 VSCode 的配置中开启自动保存、自动格式化相关功能,这样就可以实现实时自动化的效果了,用起来简直不要太爽

1// 自动保存 2"files.autoSave": "onFocusChange", 3// 保存自动格式化 4"editor.formatOnSave": true, 5// 保存自动去除多余空格 6"files.trimTrailingWhitespace": true, 7// 保存自动修复代码错误 8"editor.codeActionsOnSave": { 9 "source.fixAll": true 10},

ESLint 配置

安装 ESLint 最简单的方式其实是执行 eslint --init 命令(需要全局安装 ESLint),然后选择需要的规则、是否使用 TS、使用的框架就好,然后就会自动引入相应的依赖并生成好配置文件了,基本可以说是 “零配置”

截屏2022-07-18 21.06.44

但实际应用过程中还是考虑到原项目的情况,我还是手动引入的相关的依赖,主要使用到的依赖包括

第一步先安装依赖

1pnpm i -D eslint vue-eslint-parser eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser

第二步在根目录新建 .eslintrc.js .eslintignore 两个文件,.eslintignore 除外 node_modules,.eslintrc.js 配置内容如下

1module.exports = { 2 root: true, 3 env: { 4 browser: true, 5 node: true, 6 es6: true, 7 }, 8 parser: 'vue-eslint-parser', 9 parserOptions: { 10 parser: '@typescript-eslint/parser', 11 ecmaVersion: 2020, 12 sourceType: 'module', 13 jsxPragma: 'React', 14 ecmaFeatures: { 15 jsx: true, 16 }, 17 }, 18 extends: [ 19 'plugin:vue/vue3-recommended', 20 'plugin:@typescript-eslint/recommended', 21 ], 22 rules: { 23 // 关闭不允许使用 any 24 '@typescript-eslint/no-explicit-any': 'off', 25 // 关闭组件必须由多个单词命名 26 'vue/multi-word-component-names': 'off', 27 // 关闭禁止使用 ts 备注 28 '@typescript-eslint/ban-ts-comment': 'off', 29 // 关闭禁止使用非空断言 30 '@typescript-eslint/no-non-null-assertion': 'off', 31 // 可以使用 _ 开头定义不使用的变量 32 '@typescript-eslint/no-unused-vars': [ 33 'error', 34 { 35 argsIgnorePattern: '^_', 36 varsIgnorePattern: '^_', 37 }, 38 ], 39 'no-unused-vars': [ 40 'error', 41 { 42 argsIgnorePattern: '^_', 43 varsIgnorePattern: '^_', 44 }, 45 ], 46 }, 47}

其中 ESLint 和 Prettier 还会存在冲突,这时候会用到这两个插件,目的就是让 Prettier 的优先级大于 ESLint

  • eslint-plugin-prettier 将 Prettier 的规则设置到 ESLint 的规则中
  • eslint-config-prettier 关闭 ESLint 中与 Prettier 中会发生冲突的规则

安装命令: pnpm i eslint-plugin-prettier eslint-config-prettier -D.eslintrc.js extends 添加:"plugin:prettier/recommended",注意要添加到最后一个,ESLint 的解析顺序是按照从下往上的顺序来加载扩展的

1 extends: [ 2 "plugin:vue/vue3-recommended", 3 "plugin:@typescript-eslint/recommended", 4+ "plugin:prettier/recommended", 5 ],

第三步再安装微软官方的 ESLint 插件,关于 ESLint 的配置就算完成了

截屏2022-07-19 06.53.49

StyleLint 配置

我在项目中配置的 StyleLint 依赖包括

由于是 Vue 项目,并且使用到了 less,所以还会使用到两个 PostCSS 依赖

  • postcss-html:解析文件中 <style> 标签(vue 文件中使用)
  • postcss-less:支持解析 less 文件

首先安装相关依赖

1pnpm i -D stylelint stylelint-config-standard stylelint-config-recommended stylelint-config-recommended-vue stylelint-config-prettier stylelint-order postcss-html postcss-less

第二步在项目根目录新建 .stylelintrc.js.stylelintignore 两个文件, .stylelintrc.js 文件中的主要配置如下,我在 rules 配置项中为了兼容老项目的风格,还自定义了一些配置项

1module.exports = { 2 root: true, 3 plugins: ['stylelint-order'], 4 customSyntax: 'postcss-html', 5 rules: { 6 // 百分比声明为数字 rgb(0 0 0 / 0.1) 7 'alpha-value-notation': 'number', 8 // 空规则保持空行间隔 9 'at-rule-empty-line-before': 'never', 10 // 忽略一些关键字规则,主要为了兼容 less 和 tailwind 11 'at-rule-no-unknown': [ 12 true, 13 { 14 ignoreAtRules: [ 15 'tailwind', 16 'content', 17 'each', 18 'error', 19 'extend', 20 'for', 21 'function', 22 'if', 23 'include', 24 'mixin', 25 'return', 26 'while', 27 ], 28 }, 29 ], 30 // 颜色表示方式以逗号分隔:rgb(0, 0, 0) 31 'color-function-notation': 'legacy', 32 // 不允许非法的 hex 颜色表示方式:#fff 33 'color-no-invalid-hex': true, 34 'comment-empty-line-before': 'never', 35 // 不允许多行声明 36 'declaration-colon-newline-after': null, 37 // 每个属性之间没有空行 38 'declaration-empty-line-before': 'never', 39 // 不允许 linear-gradient() 存在不符合标准的方向 40 'function-linear-gradient-no-nonstandard-direction': true, 41 // https://stylelint.io/user-guide/rules/list/no-descending-specificity 42 'no-descending-specificity': null, 43 // 允许空文件 44 'no-empty-source': null, 45 // 结尾允许存在空行 46 'no-missing-end-of-source-newline': null, 47 // 小数必须以 0 开头 48 'number-leading-zero': 'always', 49 // 定义排序规则 50 'order/order': [ 51 [ 52 'dollar-variables', 53 'custom-properties', 54 'at-rules', 55 'declarations', 56 { 57 type: 'at-rule', 58 name: 'supports', 59 }, 60 { 61 type: 'at-rule', 62 name: 'media', 63 }, 64 'rules', 65 ], 66 { severity: 'warning' }, 67 ], 68 // 允许存在空行 69 'rule-empty-line-before': [ 70 'always', 71 { 72 ignore: ['after-comment', 'first-nested'], 73 }, 74 ], 75 }, 76 extends: ['stylelint-config-standard', 'stylelint-config-prettier'], 77 ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'], 78 overrides: [ 79 { 80 files: ['*.vue', '**/*.vue', '*.html', '**/*.html'], 81 extends: ['stylelint-config-recommended'], 82 }, 83 { 84 files: ['*.less', '**/*.less'], 85 customSyntax: 'postcss-less', 86 extends: [ 87 'stylelint-config-standard', 88 'stylelint-config-recommended-vue', 89 ], 90 }, 91 ], 92}

第三步配置 .stylelintignore 需要忽略的文件

/dist/*
/public/*
public/*

第四步安装 StyleLint 插件并且在 VSCode 这开启使用 StyleLint 格式化代码

截屏2022-07-18 20.44.47

1// stylelint 2"css.validate": false, 3"less.validate": false, 4"scss.validate": false, 5"stylelint.enable": true, 6"stylelint.validate": ["html", "css", "scss", "less", "vue"],

这个插件现在已经是 v1.2.2 的版本了,已经不支持 StyleLint 13 版本,如果你用的是 StyleLint 13 版本的话,需要将这个插件降级使用。点击插件旁边的小齿轮,再点 Install Another Version,选择其他版本进行安装选 0.87.6 版本安装就可以了,这时 css 自动格式化功能恢复正常。但是 StyleLint 14 的版本的格式化排序功能还是不能在自动保存时实现,我的解决方案是手动在 StyleLint 配置文件增加详细的 CSS 属性排序,这样就可以舒服的使用 StyleLint 的格式化功能了,具体的排序可以参考

再加上统一的执行命令

上面这一套配置下来就可以顺利的实现保存自动格式化,并且所有人的代码风格都是一致的效果了,最后又朝着“像写诗一样写代码”的目标更近了一步,最后在 package.json 文件中加上统一的全局格式化命令,就可以愉快的使用了

1"scripts": { 2 "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock, build}/**/*.{vue,ts,tsx}\" --fix", 3 "lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"", 4 "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", 5} 6