Vite+Vue3+Typescript项目前端编程规范搭建:ESlint+Prettier+Commitizen+Git hooks

998 阅读3分钟

1.ESLint9+Prettier

依赖安装

pnpm install eslint eslint-plugin-prettier eslint-define-config eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier vue-eslint-parser prettier -D
  • eslint-define-config: 是 ESLint 生态系统中的一个包,它主要用于简化 ESLint 配置的定义。具体来说:它允许你以一种更简洁和类型安全的方式定义 ESLint 的配置规则。
  • eslint-config-prettier : 它的主要作用是禁用所有与格式化相关的 ESLint 规则,确保 ESLint 不会干扰 Prettier 的格式化工作,避免两者之间的冲突。安装eslint-plugin-prettier后不再需要
  • eslint-plugin-prettier: 这个插件的主要作用就是将 prettier 作为 ESLint 的规则来使用,相当于代码不符合 Prettier 的标准时,会报一个 ESLint 错误,同时也可以通过 eslint --fix 来进行格式化。推荐的配置会自动启用 eslint-config-prettier 来禁用所有与格式化相关的 ESLint 规则
  • eslint-plugin-vue: Vue.js 官方ESlint插件

ESLint配置

//创建eslint.config.js文件
import js from "@eslint/js";
import pluginTypeScript from "@typescript-eslint/eslint-plugin";
import * as parserTypeScript from "@typescript-eslint/parser";
import { defineFlatConfig } from "eslint-define-config";
import pluginPrettier from "eslint-plugin-prettier";
import pluginVue from "eslint-plugin-vue";
import * as parserVue from "vue-eslint-parser";
​
export default defineFlatConfig([
  {
    // 基础JavaScript配置
    ...js.configs.recommended,
    // 忽略特定文件和目录
    ignores: [
      "**/.*",
      "dist/*",
      "*.d.ts",
      "public/**/*",
      "src/assets/**",
      "src/**/iconfont/**"
    ],
    // 定义全局变量
    languageOptions: {
      globals: {
        // index.d.ts
      }
    },
    // 插件配置
    plugins: {
      prettier: pluginPrettier
    },
    // 规则配置
    rules: {
      /**
       *  eslint-config-prettier: 禁用所有与代码格式化相关的 ESLint 规则,避免 ESLint 和 Prettier 的规则冲突
       *  @description
       *  包含 "eslint-config-prettier" 的规则
       *  将 Prettier 的格式化规则集成到 ESLint 中,使得 Prettier 的格式化问题可以通过 ESLint 报告并修复。
       *  它会在 ESLint 运行时调用 Prettier 来检查代码格式,并将不符合 Prettier 规则的地方标记为 ESLint 错误或警告。
       */
      ...pluginPrettier.configs.recommended.rules,
      "no-debugger": "off", // 关闭对debugger语句的检查,适应开发调试需求
      // 对未使用的变量进行检查,但忽略以'_'开头的参数和变量
      "no-unused-vars": [
        "error",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_"
        }
      ],
      // 配置Prettier规则,确保代码格式一致性,自动处理行尾符
      "prettier/prettier": [
        "error",
        {
          endOfLine: "auto"
        }
      ]
    }
  },
  {
    // TypeScript文件配置
    files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"],
    languageOptions: {
      parser: parserTypeScript,
      parserOptions: {
        sourceType: "module",
        warnOnUnsupportedTypeScriptVersion: false
      }
    },
    plugins: {
      "@typescript-eslint": pluginTypeScript
    },
    rules: {
      ...pluginTypeScript.configs.strict.rules,
      "@typescript-eslint/ban-types": "off", //关闭对特定类型定义的检查,允许使用一些不推荐的类型
      "@typescript-eslint/no-redeclare": "error", // 禁止变量或函数的重复声明
      "@typescript-eslint/ban-ts-comment": "off", // 关闭对 TypeScript 注释的限制,允许使用 '// @ts-ignore' 等注释
      "@typescript-eslint/no-explicit-any": "off", // 关闭对使用 'any' 类型的限制,但在实践中应尽量避免使用 'any'
      "@typescript-eslint/prefer-as-const": "warn", // 建议在可能的情况下使用 'as const' 语法来优化类型推断
      "@typescript-eslint/no-empty-function": "off", // 关闭对空函数体的检查,允许存在没有操作的函数
      "@typescript-eslint/no-non-null-assertion": "off", // 关闭对非空断言的限制,但在使用时需确保安全性
      "@typescript-eslint/no-unused-expressions": "off", // 关闭对未使用的表达式的检查,允许存在未使用的赋值等操作
      "@typescript-eslint/no-unsafe-function-type": "off", // 关闭对潜在不安全函数类型的检查,允许使用函数类型
      "@typescript-eslint/no-import-type-side-effects": "error", // 禁止导入类型时产生副作用
      "@typescript-eslint/explicit-module-boundary-types": "off", // 关闭对模块边界的显式类型定义要求
      "@typescript-eslint/consistent-type-imports": [
        // 要求在导入类型时保持一致性,禁止某些不一致的导入方式
        "error",
        { disallowTypeAnnotations: false, fixStyle: "inline-type-imports" }
      ],
      "@typescript-eslint/prefer-literal-enum-member": [
        // 要求枚举成员尽量使用字面量类型,允许位运算表达式
        "error",
        { allowBitwiseExpressions: true }
      ],
      "@typescript-eslint/no-unused-vars": [
        // 检查未使用的变量,但忽略以下划线开头的参数或变量
        "error",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_"
        }
      ]
    }
  },
  {
    // 针对声明文件的配置
    files: ["**/*.d.ts"],
    rules: {
      "eslint-comments/no-unlimited-disable": "off",
      "import/no-duplicates": "off",
      "unused-imports/no-unused-vars": "off"
    }
  },
  {
    files: ["**/*.?([cm])js"],
    rules: {
      "@typescript-eslint/no-require-imports": "off",
      "@typescript-eslint/no-var-requires": "off"
    }
  },
  {
    // Vue文件配置
    files: ["**/*.vue"],
    languageOptions: {
      globals: {
        $: "readonly",
        $$: "readonly",
        $computed: "readonly",
        $customRef: "readonly",
        $ref: "readonly",
        $shallowRef: "readonly",
        $toRef: "readonly"
      },
      parser: parserVue,
      parserOptions: {
        ecmaFeatures: {
          jsx: true
        },
        extraFileExtensions: [".vue"],
        parser: "@typescript-eslint/parser",
        sourceType: "module"
      }
    },
    plugins: {
      vue: pluginVue
    },
    processor: pluginVue.processors[".vue"],
    rules: {
      ...pluginVue.configs.base.rules,
      ...pluginVue.configs["vue3-essential"].rules,
      ...pluginVue.configs["vue3-recommended"].rules,
      "no-undef": "off",
      "no-unused-vars": "off",
      "vue/no-v-html": "off",
      "vue/require-default-prop": "off",
      "vue/require-explicit-emits": "off",
      "vue/multi-word-component-names": "off",
      "vue/no-setup-props-reactivity-loss": "off",
      // 配置 HTML 自闭合标签的规则
      "vue/html-self-closing": [
        "error",
        {
          html: {
            void: "always",
            normal: "always",
            component: "always"
          },
          svg: "always",
          math: "always"
        }
      ]
    }
  }
]);
​

@eslint/config-inspector:从ESLint v9.0.0开始,新的配置系统已经正式发布,带来了许多优势。配置文件现在更容易管理,并且更透明地组合使用。然而,要理解特定文件中哪些规则被启用或禁用仍然可能不简单,特别是当您的配置复杂或由多个来源组成时。ESLint配置检查器(ESLint Config Inspector),是一个可视化和交互式的工具,帮助您更好地理解和检查配置文件

# 适用于ESLint9的ESLint Config Inspector是一个可视化和交互式的工具,可以帮助你更好地理解和检查配置文件,使用下面的命令启动可视化页面
npx @eslint/config-inspector

Screenshot of config inspector

Prettier配置

//创建.prettierrc.js 配置文件
// @ts-check/** @type {import("prettier").Config} */
export default {
  bracketSpacing: true,
  singleQuote: false,
  arrowParens: "avoid",
  trailingComma: "none"
};
​
# 创建 .prettierignore 忽略文件 
public/**/*

package.json添加如下配置,执行pnpm lintnpm run lint进行eslint的代码检查和自动修复以及prettier的代码格式化

"scripts": {
    ...
    "lint:eslint": "eslint --cache --max-warnings 0  \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
    "lint:prettier": "prettier --write  \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
    "lint": "pnpm lint:eslint && pnpm lint:prettier"
    ...
},

image-20241224232356689.png

2.commitizen+commitlint

Commitizen是一个帮助我们规范化Git提交信息的工具,它提供了一种交互式的方式来生成符合约定格式的提交信息。

# 全局安装 commitizen,如此一来可以快速使用 cz 或 git cz 命令进行启动。
pnpm install -g commitizen
​
#cz-git: 一款工程性更强,轻量级,高度自定义,标准输出格式的 commitizen 适配器
# 什么是适配器:更换 commitizen 命令行工具的 交互方式 插件。
pnpm install -D cz-git
​
# commitlint: 就是当我们运行git commmit -m 'xxx'时,来检查'xxx'是不是满足团队约定好的提交规范的工具。
pnpm add --save-dev @commitlint/cli @commitlint/config-conventional

修改package.json添加config指定使用的适配器

{
  "scripts": {},
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  }
}

添加自定义配置,这边选择cz-git 与 commitlint 进行联动给予校验信息,所以可以编写于 commitlint 配置文件之中。创建 commitlint.config.js文件并编写以下配置代码:

import { defineConfig } from "cz-git";
​
export default defineConfig({
    ignores: [commit => commit.includes("init")],
    extends: ["@commitlint/config-conventional"],
    rules: {
        // @see: https://commitlint.js.org/#/reference-rules
    },
    prompt: {
        alias: { fd: "docs: fix typos" },
        messages: {
            type: "选择你要提交的类型 :",
            scope: "选择一个提交范围(可选):",
            customScope: "请输入自定义的提交范围 :",
            subject: "填写简短精炼的变更描述 :\n",
            body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
            breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
            footerPrefixesSelect: "选择关联issue前缀(可选):",
            customFooterPrefix: "输入自定义issue前缀 :",
            footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
            confirmCommit: "是否提交或修改commit ?"
        },
        types: [
            {
                value: "feat",
                name: "feat:     ✨  新增功能 | A new feature",
                emoji: ":sparkles:"
            },
            {
                value: "fix",
                name: "fix:      🐛  修复缺陷 | A bug fix",
                emoji: ":bug:"
            },
            {
                value: "docs",
                name: "docs:     📝  文档更新 | Documentation only changes",
                emoji: ":memo:"
            },
            {
                value: "style",
                name: "style:    💄  代码格式 | Changes that do not affect the meaning of the code",
                emoji: ":lipstick:"
            },
            {
                value: "refactor",
                name: "refactor: ♻️    代码重构 | A code change that neither fixes a bug nor adds a feature",
                emoji: ":recycle:"
            },
            {
                value: "perf",
                name: "perf:     ⚡️  性能提升 | A code change that improves performance",
                emoji: ":zap:"
            },
            {
                value: "test",
                name: "test:     ✅  测试相关 | Adding missing tests or correcting existing tests",
                emoji: ":white_check_mark:"
            },
            {
                value: "build",
                name: "build:    📦️   构建相关 | Changes that affect the build system or external dependencies",
                emoji: ":package:"
            },
            {
                value: "ci",
                name: "ci:       🎡  持续集成 | Changes to our CI configuration files and scripts",
                emoji: ":ferris_wheel:"
            },
            {
                value: "chore",
                name: "chore:    🔨  其他修改 | Other changes that don't modify src or test files",
                emoji: ":hammer:"
            },
            {
                value: "revert",
                name: "revert:   ⏪️  回退代码 | Reverts a previous commit",
                emoji: ":rewind:"
            }
        ],
        useEmoji: true,
        emojiAlign: "center",
        useAI: false,
        aiNumber: 1,
        themeColorCode: "",
        scopes: [],
        allowCustomScopes: true,
        allowEmptyScopes: true,
        customScopesAlign: "bottom",
        customScopesAlias: "custom",
        emptyScopesAlias: "empty",
        upperCaseSubject: false,
        markBreakingChangeMode: false,
        allowBreakingChanges: ["feat", "fix"],
        breaklineNumber: 100,
        breaklineChar: "|",
        skipQuestions: [],
        issuePrefixes: [
            { value: "closed", name: "closed:   ISSUES has been processed" }
        ],
        customIssuePrefixAlign: "top",
        emptyIssuePrefixAlias: "skip",
        customIssuePrefixAlias: "custom",
        allowCustomIssuePrefix: true,
        allowEmptyIssuePrefix: true,
        confirmColorize: true,
        scopeOverrides: undefined,
        defaultBody: "",
        defaultIssues: "",
        defaultScope: "",
        defaultSubject: ""
    }
});
​

使用示例

image-20241225094817027.png

也可以使用阿里的通义灵码插件,自动生成符合格式规范的提交信息,如下路所示

image-20241225095637186.png

3.husky+lint-staged

依赖安装

# lint-staged 安装
pnpm install lint-staged -D
# husky 安装及初始化
pnpm add --save-dev husky
npx husky init

lint-staged: 是一个用于在 Git 提交前对暂存(staged)文件运行 lint 工具的库。它的主要作用是确保只有即将提交的文件经过代码风格和质量检查,从而避免对整个项目进行不必要的 lint 检查,提高开发效率并保持代码库的整洁。

husky: 是一个用于在 Git 仓库中轻松设置和管理 Git 钩子(hooks)的工具。它允许你在 Git 操作的关键时刻(如提交、推送等)自动执行自定义脚本,从而实现自动化的工作流和代码质量控制。

创建.lintstagedrc文件,编写lint-staged配置

{
  "*.{js,jsx,ts,tsx}": [
    "prettier --cache --ignore-unknown  --write",
    "eslint --cache --fix"
  ],
  "{!(package)*.json,*.code-snippets,.!({browserslist,npm,nvm})*rc}": [
    "prettier --cache --write--parser json"
  ],
  "package.json": ["prettier --cache --write"],
  "*.vue": ["prettier --write", "eslint --cache --fix"],
  "*.{css,scss,html}": ["prettier --cache --ignore-unknown --write"],
  "*.md": ["prettier --cache --ignore-unknown --write"]
}
​

在husky初始化的目录脚本下添加以下pre-commitcommit-msg脚本,配置代码提交前、和提交信息前的钩子的脚本

image-20241225102456490.png

pre-commit脚本内容:

#!/bin/sh
# 执行lint-staged,用于在代码提交前进行格式和风格检查
pnpm exec lint-staged

commit-msg脚本内容

#!/bin/sh
# 使用npx运行commitlint进行commit信息检查
# 参数:
#   $1: commit信息文件的路径
npx --no-install commitlint --edit "$1"