快速搭建 Vite+vue3+TS+ESLint@9+Prettier+Husky@9+Commitlint 项目

814 阅读5分钟

之前也写过一篇类似的文章,但随着框架的更新,很多写法已经发生了改变,主要是eslint和husky,这里再重新梳理一遍。

创建项目

由于vite7对node版本选择比较高,这里我们先切换node版本nvm use 20,没有安装的先安装 nvm install 20,这里我的node版本是20.19.3。创建项目npm create vite@latest,选择vue和ts

image.png

代码规范

eslint

我们通过下面的命令可以非常简单地进行 ESLint 的初始化。

npm init @eslint/config

image.png 按需选择完配置后,选择立即安装,就可一键安装相关依赖。安装成功后 ESLint 帮我们创建了 .eslint.config.js 配置文件。

image.png 我们加点配置

import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import pluginVue from "eslint-plugin-vue";
import globals from "globals";
import tseslint from "typescript-eslint";

export default defineConfig([
+  {   //忽略校验文件
+    ignores: ["node_modules", "dist", "public"]
+  },
  {
    files: ["**/*.{js,mjs,cjs,ts,vue}"],
    plugins: { js },
    extends: ["js/recommended"]
  },
  { files: ["**/*.{js,mjs,cjs,ts,vue}"], languageOptions: { 
+     globals: {...globals.browser}  //配置全局变量,如{...globals.browser, wx:true}
  } },
  tseslint.configs.recommended,
  pluginVue.configs["flat/essential"],
  { files: ["**/*.vue"], languageOptions: { parserOptions: { parser: tseslint.parser } } },
+  {
+    rules: {}  //其他校验规则
+  }
]);

在 package.json 的 script 中添加命令

"scripts": {
    "lint": "eslint . --ext .js,.mjs,.cjs,.ts,.vue --fix"
  },

App.vue中添加一个未使用变量testProp

<script setup lang="ts">
   import HelloWorld from "./components/HelloWorld.vue";
+  const testProp = "sss";
</script>

运行npm run lint,报错,说明eslint配置生效

image.png

Prettier

一般 ESLint 用于检测代码风格代码规范,Prettier 用于对代码进行格式化。
安装依赖

npm  i prettier -D

然后再根目录创建 prettier.config.js 配置文件

export default {
  printWidth: 120,
  tabWidth: 2,
  useTabs: false,
  singleQuote: false,
  semi: true,
  trailingComma: "none",
  bracketSpacing: true
};

测试prettier是否生效,在 package.json 的 script 中添加命令

"scripts": {
    "prettier":"prettier --write ./src/App.vue"
  },

修改App.vue代码

<script setup lang="ts">
- import HelloWorld from "./components/HelloWorld.vue";
+ import HelloWorld from "./components/HelloWorld.vue"
+
+
+
+
+
const testProp = "sss";
</script>

运行npm run prettier后,代码已经格式化,说明prettier配置有效,删除script 中的prettier测试命令

image.png

ESLint + Prettier

在eslint校验中加入Prettier格式化,安装依赖

npm i eslint-config-prettier eslint-plugin-prettier -D

更改 Eslint 的配置文件 eslint.config.js, 在里面加入 Prettier 相关配置

import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginVue from "eslint-plugin-vue";
+  import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import { defineConfig } from "eslint/config";


export default defineConfig([
  { files: ["**/*.{js,mjs,cjs,ts,vue}"], plugins: { js }, extends: ["js/recommended"] },
  { files: ["**/*.{js,mjs,cjs,ts,vue}"], languageOptions: { globals: globals.browser } },
  tseslint.configs.recommended,
  pluginVue.configs["flat/essential"],
  { files: ["**/*.vue"], languageOptions: { parserOptions: { parser: tseslint.parser } } },
+  eslintPluginPrettierRecommended
]);

Husky + lint-staged

Husky

在安装Husky之前,我们先初始化一下git(有git的可以不用设置),运行git init, 然后再安装依赖

npm  i husky -D

初始化husky

npx husky init

完成后在package.json的scripts中会多一行命令,运行npm run prepare

image.png 运行完成后,会在项目根目录下多一个.husky文件夹

image.pngpre-commit内容改成npm run lint后提交一次内容 git add . && git commit -m 'init',如果触发了lint校验,说明husky配置成功

image.png

lint-staged

每次提交都检测所有代码并不是一个好的决定,比如你只修改了文件 A 结果文件 B 报错了,但是文件 B 并不是你负责的模块,emmm改还是不改?

我们可以通过 lint-staged 只对暂存区的代码进行检验。

首先安装依赖

npm i lint-staged -D

然后在 package.json 添加相关配置。

{
   ...,
   "lint-staged": {
      "*.{ts,vue}": [
        "npm run lint",
        "prettier --write"
      ]
    }
}

并在 .husky/pre-commit 中替换 npm run lint 为 npx lint-staged。现在我们每次提交代码前都会对改动的文件进行 Lint 检查和prettier格式化。

image.png

commitlint

使用 commitlint 对提交信息进行校验。先安装依赖:

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

然后在根目录创建配置文件 commitlint.config.js

export default {
  extends: ["@commitlint/config-conventional"]
};

然后把 commitlint 命令也添加 Husky Hook。运行命令或在.husky下新建commit-msg文件,输入--no-install commitlint -e "$HUSKY_GIT_PARAMS"

echo npx '--no-install commitlint -e "$HUSKY_GIT_PARAMS"' > .husky/commit-msg

image.png 现在,运行命令git add . && git commit -m 'init',会发现经过eslint校验和prettier格式化后,提交信息不合法被拦截导致提交失败

commitizen

安装自动化提示工具

npm i commitizen cz-conventional-changelog -D

然后在 package.json 中添加命令 commit

{
  "scripts": {
    "commit": "git add . && git-cz"
  },
}

初始化命令行的选项信息

npx commitizen init cz-conventional-changelog --force

执行后会在package.json生成commitizen的配置信息

image.png 运行npm run commit,就可以快捷选择相应特性啦,按照提示一步一步下去就可以。到这一步基本就完成了.

image.png

eslint-plugin-simple-import-sort

自动修复 import 排序,墙裂推荐 墙裂推荐 墙裂推荐的插件

安装插件

npm i eslint-plugin-simple-import-sort -D

eslint.config.js中配置

import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
+import importSort from "eslint-plugin-simple-import-sort";
import pluginVue from "eslint-plugin-vue";
import globals from "globals";
import tseslint from "typescript-eslint";

export default defineConfig([
  {
    files: ["**/*.{js,mjs,cjs,ts,vue}"],
-   plugins: { js },
+   plugins: { js, "simple-import-sort": importSort },
    extends: ["js/recommended"]
  },
  { files: ["**/*.{js,mjs,cjs,ts,vue}"], languageOptions: { globals: globals.browser } },
  tseslint.configs.recommended,
  pluginVue.configs["flat/essential"],
  { files: ["**/*.vue"], languageOptions: { parserOptions: { parser: tseslint.parser } } },
  eslintPluginPrettierRecommended,
  {
    rules: {
+      "simple-import-sort/imports": [
+       "error",
+        {
+          groups: [
+            [
+              "^vue", // vue放在首行
+              // 以字母(或数字或下划线)或“@”后面跟着字母开头的东西,通常为nodeModules引入
+              "^@?\\w",
+              "^@(/.*|$)", // 内部导入 "@/"
+              "^\\.\\.(?!/?$)", // 父级导入. 把 `..` 放在最后.
+              "^\\.\\./?$",
+              // 同级导入. 把同一个文件夹.放在最后
+              "^\\./(?=.*/)(?!/?$)",
+              "^\\.(?!/?$)",
+              "^\\./?$",
+              "^.+\\.?(css|less|scss)$", // 样式导入.
+              "^\\u0000" // 带有副作用导入,比如import 'a.css'这种.
+            ]
+          ]
+        }
+      ]
+    }
  }
]);

image.png 提交后自动修复import的排列顺序,比之前看着舒服多了,我的强迫症终于好了😏😏😏

image.png

unocss

unocss 是一个非常好用的自定义 CSS 的工具,简单方便,但是能带来很强的css定义功能,也是一个墙裂推荐 墙裂推荐 墙裂推荐的插件,你绝对会爱上的一个插件

安装插件

npm i unocss -D

修改配置vite.config.ts

import UnoCSS from 'unocss/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    UnoCSS(),
  ],
})

在根目录下创建 uno.config.ts 文件

/* eslint-disable */
import { defineConfig } from "unocss";

export default defineConfig({
  // 配置动态规则
  rules: [
    [/^m-([\.\d]+)$/, ([_, num]) => ({ margin: `${num}px` })],
    [/^mt-([\.\d]+)$/, ([_, num]) => ({ "margin-top": `${num}px` })],
    [/^mb-([\.\d]+)$/, ([_, num]) => ({ "margin-bottom": `${num}px` })],
    [/^mr-([\.\d]+)$/, ([_, num]) => ({ "margin-right": `${num}px` })],
    [/^ml-([\.\d]+)$/, ([_, num]) => ({ "margin-left": `${num}px` })],
    [/^p-([\.\d]+)$/, ([_, num]) => ({ padding: `${num}px` })],
    [/^pt-([\.\d]+)$/, ([_, num]) => ({ "padding-top": `${num}px` })],
    [/^pb-([\.\d]+)$/, ([_, num]) => ({ "padding-bottom": `${num}px` })],
    [/^pr-([\.\d]+)$/, ([_, num]) => ({ "padding-right": `${num}px` })],
    [/^pt-([\.\d]+)$/, ([_, num]) => ({ "padding-left": `${num}px` })],
    [/^fs-([\.\d]+)$/, ([_, num]) => ({ "font-size": `${num}px` })],
    [/^fw-([\.\d]+)$/, ([_, num]) => ({ "font-weight": `${num}` })],
    [/^lh-([\.\d]+)$/, ([_, num]) => ({ "line-height": `${num}px` })],
    [/^w-([\.\d]+)$/, ([_, num]) => ({ width: `${num}px` })],
    [/^h-([\.\d]+)$/, ([_, num]) => ({ height: `${num}px` })],
    [/^bg-(.+)$/, ([_, color]) => ({ "background-color": color })],
    [/^c-(.+)$/, ([_, color]) => ({ color })],
    [/^flex$/, () => ({ display: "flex" })],
    [/^fdc$/, () => ({ "flex-direction": "column" })],
    [/^fww$/, () => ({ "flex-wrap": "wrap" })],
    [/^fjc-(.+)$/, ([_, value]) => ({ "justify-content": value })],
    [/^fai-(.+)$/, ([_, value]) => ({ "align-items": value })],
    [
      /^line-([\.\d]+)$/,
      ([_, num]) => {
        return {
          overflow: "hidden",
          "word-break": "break-all",
          "text-overflow": "ellipsis",
          "-webkit-box-orient": "vertical !important",
          "-webkit-line-clamp": num,
          "line-clamp": num,
          display: "-webkit-box !important"
        };
      }
    ],
    [
      /^b([\.\d]+)$/,
      ([_, num]) => {
        return {
          border: `${num}px solid #cccccc`
        };
      }
    ]
  ],
  //配置快捷方式,将多个规则组合成一个简写
  shortcuts: {
    "flex-center": "flex  fjc-center fai-center",
    "flex-col": "flex fdc"
  }
});

将 virtual:uno.css 添加到你的主入口main.ts中:

import { createApp } from "vue";
import App from "./App.vue";
+import "virtual:uno.css";

createApp(App).mount("#app");

使用的时候,省去了麻烦的css定义,尤其是各自magin/padding等存在多种值的时候

image.png