在vue3+ts项目中配置eslint、stylelint和commitlint

5,466 阅读10分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

前言

在团队开发中,通常需要统一项目的代码规范和书写风格。这时候eslintstylelint就派上用场了,eslint可以限制项目中vue、js、ts、tsx的代码风格;stylelint可以限制项目中样式文件和其他文件中的样式规范风格;使用commitlint可以在用户提交代码前校验代码是否符合规范(eslint、stylelintt),不规范的代码会被禁止提交到远程仓库。

开发环境

  • node 16.14.2
  • npm 8.5.0

项目初始化

如果为旧项目配置则忽略此步骤

使用 vite 创建项目, 执行下面命令:

npm create vite 你的项目名称

回车后的选择(箭头上下键切换、空格选择、回车确认)

  • Ok to proceed? (y)
    • y
  • Select a framework:
    • Vue
  • Select a variant:
    • TypeScript

如下图所示:

image.png

配置 eslint

需要cd到项目根目录执行指令,执行命令前请确保当前目录为项目根目录

安装eslint

eslint 为不属于项目依赖,但是可以保证代码的质量,约束代码的风格,为了减少代码打包后的体积,建议安装到 devDependencies 中, 及安装时增加 -D 指令(npm 的使用这里不做介绍,不懂的可以问下度娘或者问问神奇海螺)。

npm install eslint -D

初始化 eslint

高版本的 Node 自带 npm 模块,所以可以直接使用 npx 命令。万一不能用,则需要手动安装一下。

npx eslint --init

回车后的选择(箭头上下键切换、空格选择、回车确认)

  • How would you like to use ESLint? ...
    • To check syntax and find problems
  • What type of modules does your project use? ...
    • JavaScript modules (import/export)
  • Which framework does your project use? ...
    • Vue.js
  • Does your project use TypeScript?
    • Yes
  • Where does your code run? ...
    • 直接回车,默认只选择: Browser
  • What format do you want your config file to be in? ...
    • JavaScript
  • Would you like to install them now with npm?
    • Yes

如下图所示:

image.png

执行完上面的操作之后会在项目的根目录生成一个 .eslintrc.js 文件,改文件为生成的 eslint 配置文件(根据刚才你的选择生成的),eslint 版本不同后缀可能略有不同,也可能为 .cjs.json,但是不影响使用。文件内容如下:

# .eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:vue/vue3-essential",
        "plugin:@typescript-eslint/recommended"
    ],
    "parserOptions": {
        "ecmaVersion": "latest",
        "parser": "@typescript-eslint/parser",
        "sourceType": "module"
    },
    "plugins": [
        "vue",
        "@typescript-eslint"
    ],
    "rules": {
    }
}

注意 extends 中的 "plugin:vue/vue3-essential", 如果你的是 "plugin:vue/essential" 就修改为 "plugin:vue/vue3-essential", 不然有可能会出现 vue/no-multiple-template-root 报错。

image.png

在 package.json 文件中增加 scripts 命令

  • 增加 lint 命令用于执行 eslint 校验
  • 增加 lint:fix 命令用于执行 eslint 代码格式化
{
    "scripts": {
        "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx",
        "lint:fix": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix"
      },
}

修改 .eslintrc.js 配置文件

  • 由于项目是基于 vite 来构建的,需要使用到 node 的一些语法,例如 pathenv 等, 所以在配置文件的 env 中加上 node: true 防止报错
"env": {
    "browser": true,
    "es2021": true,
+    "node": true
},
  • 生成的 eslint 配置默认会 vue 的部分语法不是很兼容(由于 eslint 没有解析 .vue 后缀文件的能力,需要额外的解析器用来解析 .vue 后缀的文件),需要额外修改一下配置,在 parserOptions 前面一行加上 parser: "vue-eslint-parser", 用来解析 .vue 文件。
  ...
+ "parser": "vue-eslint-parser",
  • 这时候执行 npm run lint 命令发现还是有地方不符合规范,这时候我们需要增加忽略配置文件 .eslintignore 在文件中忽略对 src/vite-env.d.ts 文件的校验(接下来的段落eslint 忽略检查中会详细说明,文件内容也在这一段中),这时候我们发现代码通过了我们的 eslint 校验。

eslint 忽略检查

通常情况下对于一些不需要检查的 eslint 规则, 一般只需要在 .eslintrc.js 配置文件中的 rule 中增加自定义校验或者忽略规则即可,但是有时候我们需要忽略对某些文件或目录的 eslint 检查,则需要创建一个 .eslintignore 文件进行配置。下面是我的 .eslintignore 文件内容

node_modules
dist/
test
build/
.vscode
.github
.husky

src/vite-env.d.ts

配置 prettier

根据项目需求配置,但是一般都需要增加 prettier 配置,用来处理一下额外的配置。

安装 prettier

很多时候 prettier 可能会和 eslint 产生冲突,所以需要另外安装插件处理冲突。

npm install prettier -D

# 处理 prettier 与 eslint 冲突
npm install eslint-config-prettier eslint-plugin-prettier -D

.prettierrc.js

.prettierrc.jsprettier 的配置文件, 这个文件需要我们手动创建

我的文件内容如下:

module.exports = {
  printWidth: 100,
  tabWidth: 2,
  singleQuote: true,
  semi: true,
  trailingComma: 'all',
  endOfLine: 'auto',
  bracketSpacing: true
};

配置使用 prettier

extends: [
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:@typescript-eslint/recommended',
+    'plugin:prettier/recommended',
],

使用 airbnb 的语法规范

根据项目需求安装。Airbnb 的这份编码规范是互联网上最受欢迎的 JavaScript 编码规范之一。 它几乎涵盖了 JavaScript 的各个方面。

安装规范配置文件

npm install eslint-config-airbnb -D

在 .eslintrc.js 中配置

"extends": [
    "eslint:recommended",
    "plugin:vue/vue3-essential",
    "plugin:@typescript-eslint/recommended",
+    "airbnb",
],

配置 stylelint

参考其他掘金文章的配置: juejin.cn/post/711829…

依赖包安装

安装相关依赖包,如果你的项目中使用了 less 而不是 scss 则将下面命令中的 scss 全部换为 less

npm install stylelint postcss postcss-scss postcss-html stylelint-config-prettier stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-vue stylelint-scss stylelint-order -D

各个包的说明:

  • stylelint: css 样式 lint 工具, 用户检测样式文件(.css文件)
  • postcss: 转换 css 代码工具
  • postcss-scss: 识别 scss 语法的插件
  • postcss-html: 识别 html/vue 中的 <style></style> 标签中的样式
  • stylelint-config-standard: Stylelint的标准可共享配置规则,详细可查看官方文档
  • stylelint-config-prettier: 关闭所有不必要或可能与 Prettier 冲突的规则
  • stylelint-config-recommended-scss: scss的推荐可共享配置规则,详细可查看官方文档
  • stylelint-config-standard-vue: lint.vue文件的样式配置
  • stylelint-scss: stylelint-config-recommended-scss 的依赖,scssstylelint 规则集合
  • stylelint-order: 指定样式书写的顺序,在 .stylelintrc.jsorder/properties-order 指定顺序

在 package.json 文件中增加 scripts 命令

为了区分eslint和stylelint 这里将package.json中的 script修改为下面命令:

"scripts": {
    "eslint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue",
    "eslint:fix": "eslint . --fix --ext .js,.jsx,.ts,.tsx,.vue",
    "stylelint": "stylelint \"./**/*.{css,scss,sass,vue,html}\"",
    "stylelint:fix": "stylelint \"./**/*.{css,scss,sass,vue,html}\" --fix",
    "lint": "npm run eslint & npm run stylelint",
    "lint:fix": "npm run eslint:fix & npm run stylelint:fix"
},

说明:

  • eslint 使用 eslint 检测项目中的 vue、js、jsx、ts、tsx 代码。
  • eslint:fix 脚本代码格式化,一般搭配 eslint 命令使用。
  • stylelint 使用 stylelint 检测项目中的样式文件和其他样式规则的书写
  • stylelint:fix 样式代码格式化,一般搭配 stylelint 命令使用。
  • lint 同时检测样式代码和脚本代码(相当于同时执行了eslint和stylelint)。
  • lint:fix 代码格式化,一般搭配 lint 命令使用。

.stylelintrc.js 文件内容

module.exports = {
  extends: [
    'stylelint-config-standard',
    'stylelint-config-recommended-scss',
    'stylelint-config-standard-vue',
    'stylelint-config-prettier',
  ],
  plugins: ['stylelint-order'],
  // 不同格式的文件指定自定义语法
  overrides: [
    {
      files: ['**/*.(scss|css|vue|html)'],
      customSyntax: 'postcss-scss',
    },
    {
      files: ['**/*.(html|vue)'],
      customSyntax: 'postcss-html',
    },
  ],
  ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts', '**/*.json', '**/*.md', '**/*.yaml'],
  rules: {
    'string-quotes': 'single',
    // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
    'no-descending-specificity': null,
    'selector-pseudo-element-no-unknown': [
      true,
      {
        ignorePseudoElements: ['v-deep'],
      },
    ],
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['deep'],
      },
    ],
    // 禁用每个选择器之前插入空行
    'rule-empty-line-before': null,
    // 禁止小于 1 的小数有一个前导零
    // 'number-leading-zero': 'never',
    // 一些特殊的scss指令
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: ['function', 'if', 'else', 'else-if', 'each', 'include', 'mixin'],
      },
    ],
    'at-rule-empty-line-before': [
      'always',
      {
        except: ['blockless-after-same-name-blockless', 'first-nested'],
        ignore: ['after-comment'],
        ignoreAtRules: ['else', 'else-if'],
      },
    ],
    // 指定样式的排序
    'order/properties-order': [
      'position',
      'top',
      'right',
      'bottom',
      'left',
      'z-index',
      'display',
      'justify-content',
      'align-items',
      'flex-shrink',
      'float',
      'clear',
      'overflow',
      'overflow-x',
      'overflow-y',
      'width',
      'min-width',
      'max-width',
      'height',
      'min-height',
      'max-height',
      'padding',
      'padding-top',
      'padding-right',
      'padding-bottom',
      'padding-left',
      'margin',
      'margin-top',
      'margin-right',
      'margin-bottom',
      'margin-left',
      'font-size',
      'font-family',
      'text-align',
      'text-justify',
      'text-indent',
      'text-overflow',
      'text-decoration',
      'white-space',
      'color',
      'background',
      'background-position',
      'background-repeat',
      'background-size',
      'background-color',
      'background-clip',
      'border',
      'border-style',
      'border-width',
      'border-color',
      'border-top-style',
      'border-top-width',
      'border-top-color',
      'border-right-style',
      'border-right-width',
      'border-right-color',
      'border-bottom-style',
      'border-bottom-width',
      'border-bottom-color',
      'border-left-style',
      'border-left-width',
      'border-left-color',
      'border-radius',
      'opacity',
      'filter',
      'list-style',
      'outline',
      'visibility',
      'box-shadow',
      'text-shadow',
      'resize',
      'transition',
      'content',
    ],
  },
};

commitlint 配置

commitlint 可用于在用户 git commit 前做一系列的操作,例如代码校验和 commit 注释校验等。

插件安装

npm install @commitlint/cli @commitlint/config-conventional -D

初始化 commitlint 配置

  • 可手动创建 commitlint.config.js 文件,写入下面的内容(生成的默认配置文件内容)。
  • 使用命令创建 commitlint.config.js 文件:
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

生成的默认配置文件内容:

module.exports = {extends: ['@commitlint/config-conventional']}

默认的 git commit 时注释的规则如下 rules[type-enum](该内容为commitlint.config.js的内容):

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'build', // 主要目的是修改项目构建系统(例如glup,webpack,rollup的配置等)的提交
        'ci', // 修改项目的持续集成流程(Kenkins、Travis等)的提交
        'chore', // 构建过程或辅助工具的变化
        'docs', // 文档提交(documents)
        'feat', // 新增功能(feature)
        'fix', // 修复 bug
        'pref', // 性能、体验相关的提交
        'refactor', // 代码重构
        'revert', // 回滚某个更早的提交
        'style', // 不影响程序逻辑的代码修改、主要是样式方面的优化、修改
        'test', // 测试相关的开发,
      ],
    ],
  },
};

搭配 husky 使用

通常情况下 commitlint 不会单独使用,一般要使用 husky 一起使用,huskygit hook 的管理工具,在提交代码的时候会触发 husky 的回调,因此可以使用 husky 监听 git commit 事件,触发其他的操作或者指令。

安装 husky 和辅助工具 lint-staged

npm install husky -D
# lint-staged 为 husky 的辅助工具
npm install lint-staged -D

安装成功后需要在 package.json 文件中增加一条 scripts 命令,用于在 npm install 初始化 husky

prepare: 在两种情况前运行,一是npm publish命令前,二是不带参数的npm install命令;它会在prepublish之后、prepublishOnly之前执行

"scripts": {
+    "prepare": "husky install",
},

初始化 husky

首次安装成功后,需要手动初始化(因为这时候不需要执行npm install),直接在控制台输入下面命令:

npm run prepare

指令执行成功后会在项目根目录下面生成一个 .husky 目录,该目录下有一个 _ 目录。

添加 commit-msg hook

commit-msg 文件中可以配置在 git commit 时对 commit 注释的校验指令

可手动创建文件再输入文件内容,但是建议使用命令创建,命令如下:

npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

上面命令执行成功后会在 .husky 目录下生成一个 commit-msg 文件,该文件的内容如下,表示在 git commit 前执行一下 npx --no -- commitlint --edit $1 指令,对 commit 的注释进行校验。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit $1

添加 pre-commit hook

pre-commit 文件中可以配置在 git commit 需要执行的操作

可手动创建文件再输入文件内容,但是建议使用命令创建,命令如下,下面出现的 npm run lint-staged 指令会在接下来的内容中解释(lint-staged 为husky的辅助工具)。

npx husky add .husky/pre-commit "npm run lint-staged"

上面命令执行成功后会在 .husky 目录下生成一个 pre-commit 文件,该文件的内容如下,表示在 git commit 前执行一下 npm run lint-staged 指令,对所有代码进行 eslint校验stylelint校验 ,不符合校验规则就终止commit。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint-staged

lint-staged 辅助工具

在项目根目录创建 .lintstagedrc 文件,并写入以下内容:

{
  "src/**/*.{js,ts,tsx,vue}": "npm run eslint",
  "src/**/*.{vue,css,scss}": "npm run stylelint"
}

内容说明:

  • 在遇到以 .js、.ts、.tsx、.vue 为后缀的文件时, 使用 npm run eslint 指令对代码进行校验,校验失败则终止commit。
  • 在遇到以 .vue、.css、.scss 为后缀的文件时, 使用 npm run stylelint 指令对代码进行校验,校验失败则终止commit。

写在最后

以上是我前段时间在配置项目时的一些经验积累,部分内容来源网络。以下是我的 package.json 文件内容:

{
  "name": "",
  "private": true,
  "version": "0.0.0",
  "description": "",
  "scripts": {
    "eslint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue",
    "eslint:fix": "eslint . --fix --ext .js,.jsx,.ts,.tsx,.vue",
    "lint": "npm run eslint & npm run stylelint",
    "lint:fix": "npm run eslint:fix & npm run stylelint:fix",
    "stylelint": "stylelint \"./**/*.{css,scss,sass,vue,html}\"",
    "stylelint:fix": "stylelint \"./**/*.{css,scss,sass,vue,html}\" --fix",
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "preview": "vite preview",
    "prepare": "husky install",
    "lint-staged": "lint-staged"
  },
  "dependencies": {
    "@element-plus/icons-vue": "^2.0.6",
    "axios": "^0.27.2",
    "crypto-js": "^4.1.1",
    "element-plus": "^2.2.11",
    "js-pinyin": "^0.1.9",
    "jsencrypt": "^3.2.1",
    "nanoid": "^3.3.4",
    "pinia": "^2.0.21",
    "socket.io-client": "^4.5.1",
    "spark-md5": "^3.0.2",
    "vue": "^3.2.37",
    "vue-router": "^4.1.3"
  },
  "devDependencies": {
    "@commitlint/cli": "^17.1.2",
    "@commitlint/config-conventional": "^17.1.0",
    "@types/crypto-js": "^4.1.1",
    "@types/node": "^17.0.30",
    "@types/spark-md5": "^3.0.2",
    "@typescript-eslint/eslint-plugin": "^5.38.0",
    "@typescript-eslint/parser": "^5.38.0",
    "@vitejs/plugin-legacy": "^2.0.0",
    "@vitejs/plugin-vue": "^3.1.0",
    "@vitejs/plugin-vue-jsx": "^2.0.0",
    "eslint": "^8.24.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-vue": "^9.5.1",
    "husky": "^8.0.1",
    "lint-staged": "^13.0.3",
    "postcss": "^8.4.16",
    "postcss-html": "^1.5.0",
    "postcss-scss": "^4.0.4",
    "prettier": "^2.7.1",
    "sass": "^1.51.0",
    "stylelint": "^14.11.0",
    "stylelint-config-prettier": "^9.0.3",
    "stylelint-config-recommended-scss": "^7.0.0",
    "stylelint-config-standard": "^28.0.0",
    "stylelint-config-standard-vue": "^1.0.0",
    "stylelint-order": "^5.0.0",
    "stylelint-scss": "^4.3.0",
    "terser": "^5.14.2",
    "typescript": "^4.6.4",
    "vite": "^3.1.0",
    "vite-plugin-html": "^3.2.0",
    "vite-plugin-style-import": "^2.0.0",
    "vue-tsc": "^0.40.4"
  }
}