构建项目Vite+Vue3+Eslint+Prettier+stylelint+husky+lintStaged+commitlint+commitizen

345 阅读14分钟

相关文档

Vite初始化项目

Vite介绍

Vite官方文档

Vite:下一代前端开发与构建工具

  • 💡 极速的开发服务器启动
  • ⚡️ 轻量快速的热模块重载(HMR)
  • 🛠️ 丰富的功能
  • 📦 自带优化的构建
  • 🔩 通用的插件接口
  • 🔑 完全类型化的 API

搭建一个Vite项目

墙裂推荐使用pnpm作为包管理工具,具体使用请查阅pnpm中文文档

如果没有安装pnpm,执行npm i -g pnpm全局安装(已经安装可跳过)

pnpm create vite

微信图片_20230406221805.png

(1) Project name: (项目名称)
默认:vite-project

(2) Select a framework: (选择一个框架)
选择:Vue

(3) Select a variant: (选择一个变体)
选择:TypeScript

根据提示,执行命令,即可启动项目

cd vite-project
pnpm install
pnpm run dev

配置Eslint

eslint中文文档

安装Eslint

pnpm add eslint -D

初始化Eslint

pnpm eslint --init

image-20230408013021736.png

(1) How would you like to use ESLint? (你想如何使用ESLint?)
选择:To check syntax and find problems (检查语法并发现问题)

(2) What type of modules does your project use? (你的项目使用什么类型的模块?)
选择:JavaScript modules (import/export) (JavaScript模块(导入/导出))

(3) Which framework does your project use? (你的项目使用哪个框架?)
选择:Vue.js

(4) Does your project use TypeScript? (你的项目使用TypeScript吗?)
选择:Yes

(5) Where does your code run? (你的代码在哪里运行?)
选择:Browser,Node

(6) What format do you want your config file to be in? (您希望您的配置文件采用什么格式?)
选择:JavaScript

(7) Would you like to install them now? (您现在要安装它们吗?)
选择:Yes

(8) Which package manager do you want to use? (您想使用哪个包管理器?)
选择:pnpm

初始化后,会生成.eslintrc.cjs配置文件

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

package.json文件中的script中添加命令

{
    ...
    "scripts": {
    	...
+       // eslint . 为指定lint当前项目中的文件
+       // --ext 为指定lint哪些后缀的文件
+       // --fix 开启自动修复
+       "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix"
    }
    ...
}

执行lint命令

pnpm lint

image-20230326004041037.png

这时候命令行中会显示出上图中的报错,意思就是在解析.vue后缀的文件时候出现解析错误parsing error。其实,在初始化Eslint是,是有配置解析vue文件的插件eslint-plugin-vue

通过eslint-plugin-vue官方文档可知。如果您已经使用了另一个解析器(例如"parser": "@typescript-eslint/parser"),请将其移动到 parserOptions中,这样它就不会与此插件vue-eslint-parser配置使用的解析器发生冲突。

修改.eslintrc.cjs文件

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
  ],
  overrides: [],
- parser: '@typescript-eslint/parser',
+ parser: 'vue-eslint-parser',
  parserOptions: {
+   parser: '@typescript-eslint/parser',
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['vue', '@typescript-eslint'],
  rules: {},
}

两个parser的区别在于,外面的parser用来解析.vue后缀文件,使得Eslint能解析<template>标签中的内容,而parserOptions中的parser,即@typescript-eslint/parser用来解析vue文件中<script>标签中的代码。

此时,再执行pnpm lint,就会发现校验通过了。

image-20230407215621841.png

测试阶段(可跳过)

.eslintrc.cjs配置文件中新增Eslint常用规则,Eslint的规则明细请查阅文档Eslint-Rules

module.exports = {
	...
	rules: {
+		// 禁用语句末尾使用分号
+		"semi": ["error", "never"],
+		// 要求尽可能地使用单引号
+       "quotes": ["error", "single"]
    }
	...
}

src/main.ts文件中添加代码段

...
+ const str = "xxx";

执行pnpm lint,Eslint会根据配置规则检测代码且尽可能修复代码。

- const str = "xxx";
+ const str = 'xxx'

image-20230407220200388.png

安装vscode插件Eslint

如果写一行代码就要执行一遍lint命令,这效率就太低了。所以我们可以配合vscode的ESLint插件,实现每次保存代码时,自动执行lint命令来修复代码的错误。

在项目中新建.vscode/settings.json文件,然后在其中加入以下配置。

{
    // 开启自动修复
    "editor.codeActionsOnSave": {
        "source.fixAll": false,
        "source.fixAll.eslint": true
    }
}

此时,编辑代码,保存时,Eslint插件会根据.eslintrc.cjs配置文件的规则检测代码且尽可能修复代码。

相关依赖说明

Eslint:javascript代码检测工具

eslint-plugin-vue:使用 ESLint 检查 .vue文件 的 <template><script>,以及.js文件中的Vue代码

配置Prettier

Prettier中文文档

安装Prettier

pnpm add prettier -D

在根目录下新建.prettierrc.cjs文件

module.exports = {
  // 指定自动换行的行长,默认值:80
  // printWidth: 80,

  // 指定每个缩进级别的空格数,默认值:2
  // tabWidth: 2,

  // 用制表符代替空格缩进行,默认值:false
  // useTabs: false,

  // 在语句的末尾打印分号,默认值:true
  semi: false,

  // 用单引号代替双引号,默认值:false
  singleQuote: true,

  // 选项:as-needed 只在需要的对象属性周围添加引号
  // 选项:consistent 如果对象中至少有一个属性需要引用,则需要引用所有属性
  // 选项:preserve 对象属性中引号的输入使用
  // 默认值:as-needed
  // quoteProps: 'as-needed',

  // 在JSX中使用单引号而不是双引号,默认值:false
  // jsxSingleQuote: false,

  // 在多行逗号分隔的语法结构中,尽可能打印尾随逗号(例如,单行数组的后面永远不会有逗号)
  // 选项:es5 尾随逗号在ES5中有效(对象、数组等)。在TypeScript中,类型参数中没有后面的逗号
  // 选项:none 后面没有逗号
  // 选项:all 尽可能以逗号结尾(包括函数参数和调用)
  // 默认值:es5
  // trailingComma: 'es5',

  // 在对象字面量的括号之间打印空格,默认值:true,例如:{ foo: bar }
  // bracketSpacing: true,

  // 将多行HTML (HTML, JSX, Vue, Angular)元素的>放在最后一行的末尾,而不是单独放在下一行(不适用于自关闭元素)。默认值:false
  // bracketSameLine: false,

  // 在唯一的箭头函数参数周围包含圆括号
  // 选项:always 总是包含圆括号。例如:(x) => x
  // 选项:avoid 尽可能圆括号。例如:x => x
  // 默认值:always
  arrowParens: 'avoid',

  // 尾行
  // 选项:lf,仅换行
  // 选项:crlf,回车符 + 换行符
  // 选项:cr,仅回车符
  // 选项:auto,维护现有的行尾(一个文件中的混合值通过查看第一行后使用的内容进行规范化)
  // 默认值:lf
  endOfLine: 'auto',
}

package.json中的script中添加命令

{
    ...
    "scripts": {
        ...
+       "format": "prettier --write \"./**/*.{html,vue,ts,js,cjs,json,md}\"",
    }
    ...
}

执行format命令

pnpm format

此时会格式化部分文件的代码

测试阶段(可跳过)

src/main.ts文件中添加代码段

...
+ const obj = {
+   a: "a"
+ };

执行pnpm format,Prettier会根据配置规则格式化代码。

...
const obj = {
-   a: "a"
- };
+   a: 'a',
+ }

安装vscode的Prettier插件

安装该插件的目的是,让该插件在我们保存的时候自动完成格式化

.vscode/settings.json中添加一下规则

{
+   // 保存的时候自动格式化
+   "editor.formatOnSave": true,
+   // 默认格式化工具选择prettier
+   "editor.defaultFormatter": "esbenp.prettier-vscode",
    // 开启自动修复
    "editor.codeActionsOnSave": {
        "source.fixAll": false,
        "source.fixAll.eslint": true
    }
}

此时,编辑代码,保存时,Prettier插件会根据.prettierrc.cjs配置文件的规则格式化代码。

解决EslintPrettier的冲突

演示冲突

修改.eslintrc.cjs(演示完以后,建议恢复)

module.exports = {
	...
	extends: [
-		'eslint:recommended',
+		'eslint:all',
		...
	],
	...
}

这里需要注意eslint:recommended既是的规则,eslint:all则代表所有规则。 演示需要将eslint:recommended改为eslint:all。是因为作者发现eslint:recommended中并没有与Prettier冲突的规则。或者读者将来在extends中引入第三方配置也可能造成EslintPrettier的冲突。

image-20230408122148926.png

修改保存以后,.eslintrc.cjs文件的代码会出现红色波浪线报错

image-20230408115252491.png

此时继续按Ctrl+s手动保存。会出现屏幕闪一下后又恢复到了报错的状态。

如上截图,主要是有两个Eslint的报错

  1. Unquoted property 'extends' found. eslint(quote-props)

    该规则默认要求所有对象文本属性名称两边用引号引起来。

  2. Strings must use doublequote. eslint(quotes)

    该规则默认要求字符串尽可能使用双引号。

然而在Prettier配置中,也有相关的两个配置

image-20230408121413580.png

在理想的状态下,EslintPrettier应该各司其职。Eslint负责检测代码质量,Prettier负责格式化代码。但是在使用的过程中会发现,由于我们开启了自动化的Eslint修复与自动化的根据Prettier来格式化代码。所以我们已保存代码,会出现屏幕闪一起后又恢复到了报错的状态。

这其中的根本原因就是Eslint有部分规则与Prettier冲突了,所以保存的时候显示运行了Eslint的修复命令,然后再运行Prettier格式化,所以就会出现屏幕闪一下然后又恢复到报错的现象。这时候你可以检查一下是否存在冲突的规则。

eslint-plugin-prettier: 基于 prettier 代码风格的 eslint 规则,即eslint使用pretter规则来格式化代码。

eslint-config-prettier: 禁用所有与格式相关的 eslint 规则,解决 prettier 与 eslint 规则冲突,确保将其放在 extends 队列最后,这样它将覆盖其他配置

安装依赖

pnpm add eslint-config-prettier eslint-plugin-prettier -D

.eslintrc.cjsextends的最后添加一个配置

{ 
    extends: [
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:@typescript-eslint/recommended',
+    // 新增,必须放在最后面
+    'plugin:prettier/recommended' 
  ],
}

此时,发现.eslintrc.cjs文件的代码恢复正常,冲突已解决。

最后记得在.eslintrc.cjsextendseslint:all改回eslint:recommended。因为不推荐默认使用Eslint的全部规则,有需要可以自行在.eslintrc.cjsrules配置。

这里需要注意,在.eslintrc.cjsrules配置规则时,避免配置一些与Prettier冲突的规则。即使以上已经解决了冲突,但是rules的权重比extends的高,会覆盖extends的规则。以下列出部分EslintPrettier有冲突的规则,其余在实际工作中发现再自行解决。

EslintPrettier
semiSemicolons
quotesQuotes
quote-propsQuote Props

配置stylelint

依赖说明

pnpm add -D stylelint postcss postcss-html stylelint-config-html stylelint-config-prettier-scss stylelint-config-standard-scss stylelint-order

在根目录下新建.stylelintrc.cjs文件

module.exports = {
  extends: [
    'stylelint-config-standard-scss',
    'stylelint-config-prettier-scss',
    'stylelint-config-html',
  ],
  plugins: ['stylelint-order'],
  rules: {
    // 指定样式的排序
    'order/properties-order': [
      'position',
      'top',
      'right',
      'bottom',
      'left',
      'z-index',
      'display',
      'justify-content',
      'align-items',
      'float',
      'clear',
      'overflow',
      'overflow-x',
      'overflow-y',
      'padding',
      'padding-top',
      'padding-right',
      'padding-bottom',
      'padding-left',
      'margin',
      'margin-top',
      'margin-right',
      'margin-bottom',
      'margin-left',
      'width',
      'min-width',
      'max-width',
      'height',
      'min-height',
      'max-height',
      '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'
    ]
  }
}

package.json中的script中添加命令

{
    ...
    "scripts": {
        ...
+       "lint:style": "stylelint \"./**/*.{css,scss,vue,html}\" --fix"
    }
    ...
}

执行lint:style命令

pnpm lint:style

此时会格式化部分文件样式的代码

安装vscode的Stylelint插件

安装该插件可在我们保存代码时自动执行stylelint

.vscode/settings.json中添加一下规则

{
+   // stylelint校验的文件格式
+   "stylelint.validate": ["css", "scss", "vue", "html"]
    // 保存的时候自动格式化
    "editor.formatOnSave": true,
    // 默认格式化工具选择prettier
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    // 开启自动修复
    "editor.codeActionsOnSave": {
        "source.fixAll": false,
        "source.fixAll.eslint": true,
+       "source.fixAll.stylelint": true
    }
}

配置husky

husky中文文档:操作 Git 钩子的工具

Git规范

Git 有很多的 hooks, 让我们在不同的阶段,对代码进行不同的操作,控制提交到仓库的代码的规范性,和准确性, 以下只是几个常用的钩子

  • pre-commit 通过钩子函数,判断提交的代码是否符合规范
  • commit-msg 通过钩子函数,判断commit信息是否符合规范
  • pre-push 通过钩子,执行测试,避免对以前的内容造成影响

安装husky

pnpm add husky -D

package.jsonscript中添加命令

pnpm set-script prepare "husky install"

执行以上命令,会自动在package.jsonscript添加一条命令,如下

{
	...
	"script": {
		...
+		"prepare": "husky install"
	}
	...
}

初始化husky

pnpm prepare

初始化 husky, 会在根目录创建 .husky文件夹

image-20230408161044235.png

添加pre-commit钩子

npx husky add .husky/pre-commit "pnpm lint && pnpm format && pnpm lint:style"

执行git commit -m "xxx"会在pre-commit阶段进行EslintPrettierStylelint检测修复代码

image-20230408223249598.png

配置lint-staged

lint-staged:检测本地暂存代码的工具

每次执行git commit -m “xxx”都会把所有文件检测一遍,在实际项目,commit时我们只想检测暂存的代码。这时候我们就可以借助lint-staged对已经通过 git add加入到暂存区stage的文件进行扫描即可。

安装lint-staged

pnpm add lint-staged -D

package.json中配置lint-staged

{
  ...
+ "lint-staged": {
+   ".vue,.js,.ts,.jsx,.tsx": "eslint --fix",
+   "./**/*.{html,vue,ts,js,cjs,json,md}": "prettier --write",
+   "./**/*.{css,scss,vue,html}": [
+     "stylelint --fix",
+     "prettier --write"
+   ]
+ },
}

修改.husky/pre-commit文件

...
- pnpm lint && pnpm format && pnpm lint:style
+ npx lint-staged

执行git commit -m "xxx"会在pre-commit阶段只对暂存区的代码进行EslintPrettierStylelint检测修复代码

image-20230408230535143.png

配置commitlint

commitlint:commit信息校验的工具

@commitlint/config-conventional:这是一个规范配置,标识采用什么规范来执行消息校验

安装commitlint

pnpm add @commitlint/config-conventional @commitlint/cli -D

在根目录新建commitlint.config.cjs文件

module.exports = {
  extends: ['@commitlint/config-conventional'],
  // 校验规则
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'style',
        'refactor',
        'perf',
        'test',
        'chore',
        'revert',
        'build',
      ],
    ],
    'type-empty': [2, 'never'],
    'scope-empty': [0],
    'subject-empty': [2, 'never'],
  },
}

添加commit-msg钩子

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

执行 git commit -m "xxx"会报如下错误,需按照约定式提交,例如:git commit -m "feat: xxx"

image-20230408235857181.png

配置commitizen

commitizen:使用commitizen提交时,系统会提示你在提交时填写任何必需的提交字段

cz-conventional-changelog

cz-customizable:自定义commitizen规则

由于添加了commitlint验证,对于不熟悉提交规范的开发者会有一定影响,可以添加 commitizen 工具,手动生成规范化commit。

安装commitizen

pnpm add -D commitizen cz-conventional-changelog

package.json中的script中添加命令及配置

{
    ...
    "scripts": {
        ...
+       "commit": "cz"
    }
    ...
+   "config": {
+     "commitizen": {
+       "path": "cz-conventional-changelog"
+     }
+   }
}

此时,执行pnpm commit就可以提示我们填写代码规则了

image-20230409014325348.png

自定义commitizen规则

使用cz-customizable工具

pnpm add cz-customizable -D

在根目录下新建.cz-config.cjs文件

module.exports = {
  types: [
    { value: 'feat', name: '✨feat:     新功能' },
    { value: 'fix', name: '🐛fix:      修复' },
    { value: 'docs', name: '✏️docs:      文档变更' },
    { value: 'style', name: '💄style:    代码格式(不影响代码运行的变动)' },
    {
      value: 'refactor',
      name: '♻️refactor:  重构(既不是增加feature,也不是修复bug)',
    },
    { value: 'perf', name: '⚡️perf:     性能优化' },
    { value: 'test', name: '✅test:     增加测试' },
    { value: 'chore', name: '🚀chore:    构建过程或辅助工具的变动' },
    { value: 'revert', name: '⏪️revert:   回退' },
    { value: 'build', name: '📦️build:    打包' },
    { value: 'ci', name: '👷CI:       related changes' },
  ],
  scopes: [
    { name: '模块1' },
    { name: '模块2' },
    { name: '模块3' },
    { name: '模块4' },
  ],
  messages: {
    type: '请选择提交类型(必选):',
    scope: '请输入文件修改范围(可选):',
    customScope: '请输入修改范围(可选):',
    subject: '请简要描述提交(必填):',
    body: '请输入详细描述(可选):',
    breaking: '非兼容性说明 (可选):\n',
    footer: '请输入要关闭的issue(可选):',
    confirmCommit: '确认使用以上信息提交?(Y/n)',
  },

  allowCustomScopes: true,
  allowBreakingChanges: ['feat', 'fix'],
  skipQuestions: ['body', 'footer'],
  subjectLimit: 120,
}

修改package.json

{
    ...
    "scripts": {
        ...
-       "commit": "cz"
+		"commit": "git-cz"
    }
    ...
    "config": {
      "commitizen": {
-       "path": "cz-conventional-changelog"
+		"path": "cz-customizable"
      },
+     "cz-customizable": {
+       "config": ".cz-config.cjs"
+     }
    }
}

执行pnpm commit ,根据.cz-config.cjs配置提示提交信息

image-20230409021605037.png

总结

本篇文章主要是以一个vite-vue3-ts的项目搭建为基础,在项目中引入Eslint+Prettier+stylelint+husky+lint-staged+commitlint+commitizen来规范项目。