一文搞定前端Prettier、ESLint 和 Stylelint

769 阅读15分钟

在前端 Vue 项目中,利用 Git 的 pre-commit 钩子,结合 Prettier、ESLint 和 Stylelint 实现代码提交时的自动格式化和修复,并包含自定义规则的校验。

核心流程概览:

  1. 环境准备: 确保 Node.js 和 npm/yarn 环境,以及一个 Vue 项目。
  2. 安装依赖: 安装 Prettier, ESLint, Stylelint 及其相关插件和配置。
  3. 配置 Prettier: 定义代码格式化规则。
  4. 配置 ESLint: 定义 JavaScript/Vue 脚本部分的规范,集成 Prettier,添加自定义规则。
  5. 配置 Stylelint: 定义 CSS/SCSS/Less 等样式部分的规范,集成 Prettier,添加自定义规则。
  6. 安装与配置 Husky: 用于管理 Git 钩子。
  7. 安装与配置 lint-staged: 用于仅对 Git 暂存区的文件执行 Linter 和 Formatter。
  8. 整合配置: 在 lint-staged 中配置 Prettier, ESLint, Stylelint 的执行命令。
  9. 测试: 尝试提交代码,验证钩子是否生效。

详细步骤与代码讲解

第一步:环境准备

确保你的开发环境已经安装了 Node.js (建议 LTS 版本) 和 npm 或 yarn。同时,你需要一个 Vue 项目。如果还没有,可以使用 Vue CLI 或 Vite 创建一个:

# 使用 Vue CLI
# npm install -g @vue/cli
# vue create my-vue-app

# 或者使用 Vite
# npm create vite@latest my-vue-app -- --template vue
# cd my-vue-app
# npm install # 或者 yarn install

第二步:安装开发依赖

我们需要安装一系列的开发依赖项。

# 使用 npm
npm install --save-dev \
  prettier \
  eslint eslint-plugin-vue @vue/eslint-config-prettier @babel/eslint-parser \
  stylelint stylelint-config-standard-vue stylelint-config-prettier postcss-html \
  husky lint-staged

# 或者使用 yarn
yarn add --dev \
  prettier \
  eslint eslint-plugin-vue @vue/eslint-config-prettier @babel/eslint-parser \
  stylelint stylelint-config-standard-vue stylelint-config-prettier postcss-html \
  husky lint-staged

# 如果你的项目使用 TypeScript,还需要安装:
# npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin @vue/eslint-config-typescript
# yarn add --dev @typescript-eslint/parser @typescript-eslint/eslint-plugin @vue/eslint-config-typescript

依赖说明:

  • prettier: 核心代码格式化工具。
  • eslint: JavaScript 和 Vue <script> 部分的 Linter。
  • eslint-plugin-vue: ESLint 官方推荐的 Vue.js 插件,提供 Vue 特定的 linting 规则。
  • @vue/eslint-config-prettier: (Vue CLI 创建项目时可能已包含) 关闭 eslint-plugin-vue 中与 Prettier 冲突的规则。注意: 现在更推荐直接使用 eslint-config-prettier 并确保它在 extends 数组的最后。我们后面会用 eslint-config-prettier
  • eslint-config-prettier: (替代 @vue/eslint-config-prettier) 关闭 ESLint 核心和各种插件(如 eslint-plugin-vue, @typescript-eslint/eslint-plugin)中与 Prettier 冲突的所有格式化相关规则。
  • @babel/eslint-parser: (如果不用 TypeScript) 一个 ESLint 解析器,允许 ESLint lint 所有有效的 Babel 代码。对于现代 JS 语法和 Vue 项目很有用。如果你用了 @vue/cli-plugin-babel,它通常是默认的。
  • stylelint: CSS, SCSS, Less, 以及 Vue <style> 部分的 Linter。
  • stylelint-config-standard-vue: 适用于 Vue 单文件组件的 Stylelint 标准配置,它内部包含了 stylelint-config-standard 并添加了对 Vue SFC 的支持。
  • stylelint-config-prettier: 关闭 Stylelint 中与 Prettier 冲突的规则。
  • postcss-html: 让 Stylelint 能够解析 .vue, .html 文件中的 <style> 标签内容。
  • husky: Git Hooks 管理工具,简化钩子脚本的创建和维护。
  • lint-staged: 在 Git 暂存文件上运行 linters/formatters 的工具,避免对整个项目进行检查,提高效率。
  • (可选 TypeScript) @typescript-eslint/parser: 允许 ESLint lint TypeScript 代码。
  • (可选 TypeScript) @typescript-eslint/eslint-plugin: 提供 TypeScript 特定的 linting 规则。
  • (可选 TypeScript) @vue/eslint-config-typescript: (Vue CLI 创建项目时可能已包含) 结合了 Vue 和 TypeScript 的 ESLint 基础配置。

第三步:配置 Prettier

在项目根目录下创建 .prettierrc.js (或 .prettierrc.json, .prettierrc.yaml 等) 文件。

// .prettierrc.js
// 访问 https://prettier.io/docs/en/options.html 查看所有选项
module.exports = {
  // 每行最大字符数
  printWidth: 100,
  // tab宽度为2空格
  tabWidth: 2,
  // 是否使用tab替代空格
  useTabs: false,
  // 结尾是否添加分号
  semi: true,
  // 是否使用单引号
  singleQuote: true,
  // 对象属性的引号使用:'as-needed' | 'consistent' | 'preserve'
  quoteProps: 'as-needed',
  // JSX中使用单引号
  jsxSingleQuote: false,
  // 多行时尽可能打印尾随逗号 'none' | 'es5' | 'all'
  trailingComma: 'es5', // 在ES5中有效的结尾逗号(对象、数组等), 'all' 包括函数参数
  // 对象前后添加空格 { foo: bar }
  bracketSpacing: true,
  // JSX标签闭合括号是否换行
  // <button
  //   className="prettier-class"
  //   id="prettier-id"
  //   onClick={this.handleClick}>
  //   Click Here
  // </button>
  // false:
  // <button
  //   className="prettier-class"
  //   id="prettier-id"
  //   onClick={this.handleClick}
  // >
  //   Click Here
  // </button>
  bracketSameLine: false, // 在jsx中把'>' 是否单独放一行, 旧版叫 jsxBracketSameLine
  // 箭头函数参数是否总是带括号 'always' | 'avoid'
  arrowParens: 'always',
  // Range 相关,通常不需要改
  // rangeStart: 0,
  // rangeEnd: Infinity,
  // 解析器推断,通常不需要指定
  // parser: undefined,
  // 文件路径,通常不需要指定
  // filepath: undefined,
  // 是否需要在文件开头插入 @format pragma
  requirePragma: false,
  // 是否在已被 Prettier 格式化的文件顶部插入 @format pragma
  insertPragma: false,
  // Markdown 处理方式 'always' | 'never' | 'preserve'
  proseWrap: 'preserve',
  // HTML 空白敏感度 'css' | 'strict' | 'ignore'
  htmlWhitespaceSensitivity: 'css',
  // Vue 文件脚本和样式标签缩进
  vueIndentScriptAndStyle: false,
  // 行尾换行符 'lf' | 'crlf' | 'cr' | 'auto'
  endOfLine: 'lf',
  // 是否格式化嵌入式语言 'auto' | 'off'
  embeddedLanguageFormatting: 'auto',
  // 单个属性换行时,是否将属性放在单独的行上
  singleAttributePerLine: false, // Vue/JSX中单个属性是否独占一行
};

同时,创建 .prettierignore 文件,告诉 Prettier 忽略哪些文件或目录:

# .prettierignore

# 忽略编译后的目录
/dist
/node_modules

# 忽略特定的配置文件 (如果它们不应该被格式化)
# /public
# /coverage

# 忽略自动生成的文件
# yarn.lock
# package-lock.json

# 特定类型的文件
*.svg
*.min.js
*.min.css

第四步:配置 ESLint

在项目根目录下创建 .eslintrc.js 文件。

// .eslintrc.js
module.exports = {
  // 指定脚本运行环境,支持的全局变量
  env: {
    browser: true, // 浏览器环境
    es2021: true, // 支持 ES2021 语法
    node: true, // Node.js 环境
    'vue/setup-compiler-macros': true, // 为了支持 <script setup> 中的 defineProps/defineEmits 等宏
  },
  // 继承的配置,后面的会覆盖前面的
  extends: [
    // Vue 推荐的规则集 (包含 base, essential, strongly-recommended, recommended)
    'plugin:vue/vue3-recommended', // 或 'plugin:vue/essential', 'plugin:vue/strongly-recommended' 根据项目需求选择
    // ESLint 推荐的基础规则
    'eslint:recommended',
    // 如果使用 TypeScript,取消注释下面这行,并确保安装了 @vue/eslint-config-typescript
    // '@vue/eslint-config-typescript/recommended', // 注意:这通常会包含 @typescript-eslint/recommended
    // 关键:放在最后,用于关闭与 Prettier 冲突的 ESLint 规则
    'prettier', // 使用 eslint-config-prettier 关闭冲突规则 (需要 npm install --save-dev eslint-config-prettier)
    // 如果你使用的 Vue 配置(如 @vue/eslint-config-xxx)没有自动包含 prettier,则需要显式添加
  ],
  // 指定 ESLint 解析器
  // 对于普通 JavaScript 或 Vue 项目中的 <script>(未使用 TypeScript)
  parser: 'vue-eslint-parser', // 必须使用 vue-eslint-parser 来解析 .vue 文件
  parserOptions: {
    ecmaVersion: 'latest', // 使用最新的 ECMAScript 标准
    sourceType: 'module', // 支持模块化
    // 如果使用 @babel/eslint-parser 作为 JS 解析器 (vue-eslint-parser 内部会调用它)
    parser: '@babel/eslint-parser',
    requireConfigFile: false, // 如果没有 Babel 配置文件,需要设置为 false
    babelOptions: {
      // 如果需要,可以在这里指定 Babel 插件或预设
      // presets: ["@babel/preset-env"],
    },
    // 如果项目使用 TypeScript,需要替换 parser 和添加 parserOptions.parser
    // parser: 'vue-eslint-parser',
    // parserOptions: {
    //   parser: '@typescript-eslint/parser', // 让 vue-eslint-parser 使用 TS 解析器
    //   ecmaVersion: 'latest',
    //   sourceType: 'module',
    //   jsxPragma: 'React',
    //   ecmaFeatures: {
    //     jsx: true
    //   },
    //   // 如果 tsconfig.json 不在根目录,需要指定路径
    //   // project: './tsconfig.json',
    //   // extraFileExtensions: ['.vue'], // 确保能解析 .vue 文件中的 TS
    // },
  },
  // 使用的插件列表
  plugins: [
    'vue',
    // 如果使用 TypeScript,取消注释下面这行
    // '@typescript-eslint',
    // 可选:如果你想用 eslint-plugin-prettier 来报告 Prettier 格式问题作为 ESLint 错误(通常不推荐,因为 Prettier 自动格式化更好)
    // 'prettier'
  ],
  // 自定义规则 (会覆盖 extends 中的规则)
  // "off" 或 0 - 关闭规则
  // "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  // "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
  rules: {
    // === Prettier 兼容性 ===
    // 如果使用了 eslint-plugin-prettier (不推荐与自动修复同时使用,因为 Prettier 会修复)
    // 'prettier/prettier': 'warn', // 将 Prettier 问题报告为 ESLint 警告

    // === ESLint 核心规则自定义 ===
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 生产环境禁止 console,开发环境允许
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 生产环境禁止 debugger
    'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], // 未使用的变量警告,忽略以下划线开头的参数
    'no-undef': 'error', // 禁止使用未定义的变量
    'semi': ['error', 'always'], // 必须使用分号
    'quotes': ['error', 'single'], // 必须使用单引号
    'comma-dangle': ['error', 'always-multiline'], // 多行时必须使用尾随逗号

    // === Vue 相关规则自定义 (基于 'plugin:vue/vue3-recommended') ===
    'vue/html-indent': ['error', 2], // Vue 模板缩进为 2 空格
    'vue/max-attributes-per-line': ['warn', {
      singleline: 3, // 单行最多 3 个属性
      multiline: 1, // 多行时每行 1 个属性
    }],
    'vue/singleline-html-element-content-newline': 'off', // 关闭单行 HTML 元素内容强制换行
    'vue/multiline-html-element-content-newline': 'off', // 关闭多行 HTML 元素内容强制换行
    'vue/html-self-closing': ['error', {
      html: {
        void: 'always', // 对于没有内容的 HTML 标签(如 <br>)总是自闭合
        normal: 'never', // 对于普通 HTML 标签(如 <div>)从不自闭合
        component: 'always', // 对于 Vue 组件总是自闭合
      },
      svg: 'always',
      math: 'always',
    }],
    'vue/no-v-html': 'warn', // 警告使用 v-html,防止 XSS 攻击,根据需要调整
    'vue/attribute-hyphenation': ['error', 'always'], // 组件 prop 和 HTML 属性名使用 kebab-case
    'vue/component-name-in-template-casing': ['error', 'kebab-case', {
      registeredComponentsOnly: false // 对所有组件标签应用 kebab-case
    }],
    'vue/v-on-event-hyphenation': ['error', 'always', { // v-on 事件名使用 kebab-case
      autofix: true, // 启用自动修复
    }],
    'vue/custom-event-name-casing': ['error', 'kebab-case'], // 自定义事件名使用 kebab-case
    'vue/no-unused-components': 'warn', // 未使用的组件警告
    'vue/no-unused-vars': 'warn', // Vue 模板中未使用的变量警告 (作用域变量)

    // === 自定义规则示例 ===
    // 强制组件的 name 选项必须设置 (对于调试和 DevTools 很有用)
    'vue/require-name-property': 'error',
    // 限制每个文件的代码行数 (示例,可能过于严格)
    // 'max-lines': ['warn', { max: 500, skipBlankLines: true, skipComments: true }],
    // 限制函数复杂度 (示例)
    // 'complexity': ['warn', 10],

    // === 如果使用 TypeScript,可以添加 TS 相关规则 ===
    // '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
    // '@typescript-eslint/explicit-function-return-type': 'off', // 通常关闭,除非需要强制返回类型
    // '@typescript-eslint/no-explicit-any': 'warn', // 警告使用 any 类型
  },
  // 全局变量声明
  globals: {
    // 如果使用了 defineProps, defineEmits 等编译器宏 (env 中已配置 'vue/setup-compiler-macros': true)
    // defineProps: 'readonly',
    // defineEmits: 'readonly',
    // defineExpose: 'readonly',
    // withDefaults: 'readonly',
    // 如果有其他全局变量,在这里声明
    // myGlobalVar: 'readonly',
  },
  // 文件忽略配置(也可以使用 .eslintignore 文件)
  ignorePatterns: [
    'node_modules/',
    'dist/',
    'public/',
    '*.min.js',
    '*.md',
    // 其他需要忽略的文件或目录
  ],
};

说明:

  • env: 定义了代码运行的环境,ESLint 会据此预设一些全局变量 (如 window, document for browser, require, module for node)。
  • extends: 继承预设的规则集。顺序很重要,后面的配置会覆盖前面的。prettier 必须放在最后,以确保它能覆盖掉所有可能与 Prettier 冲突的格式化规则。
  • parser: 指定解析器。vue-eslint-parser 用于解析 .vue 文件,它内部可以配置使用哪个 JS/TS 解析器来解析 <script> 标签。
  • parserOptions: 配置解析器的选项。
  • plugins: 加载 ESLint 插件。
  • rules: 自定义或覆盖规则。这里我们添加了一些常用的 ESLint 核心规则和 Vue 相关规则的自定义配置,并展示了如何添加完全自定义的规则。你可以根据团队规范调整。
  • globals: 声明不需要 importrequire 就能使用的全局变量。
  • ignorePatterns: 定义 ESLint 应忽略的文件或目录,效果同 .eslintignore 文件。

第五步:配置 Stylelint

在项目根目录下创建 .stylelintrc.js 文件。

// .stylelintrc.js
module.exports = {
  // 继承的配置
  extends: [
    // Stylelint 标准规则集,包含了许多最佳实践规则
    // 'stylelint-config-standard', // stylelint-config-standard-vue 已经包含了它
    // 适用于 Vue 的 Stylelint 配置,处理 <style> 标签和 .vue 文件
    'stylelint-config-standard-vue', // 需要 npm install --save-dev stylelint-config-standard-vue
    // 关键:放在最后,用于关闭与 Prettier 冲突的 Stylelint 规则
    'stylelint-config-prettier', // 需要 npm install --save-dev stylelint-config-prettier
  ],
  // 使用的插件 (如果有需要)
  // plugins: [
  //   'stylelint-order', // 例如,强制属性排序
  // ],
  // 解析 .vue 文件中的 <style> 标签
  // `stylelint-config-standard-vue` 通常会自动处理这个,但显式指定有时更清晰
  // 注意: postcss-html 是必须的,我们已经安装了
  overrides: [
    {
      files: ['**/*.vue'],
      customSyntax: 'postcss-html', // 指定解析 .vue 文件的语法
    },
    // 如果你还用 SCSS 或 Less
    // {
    //   files: ["**/*.scss"],
    //   customSyntax: "postcss-scss" // 需要 npm install --save-dev postcss-scss
    // },
    // {
    //   files: ["**/*.less"],
    //   customSyntax: "postcss-less" // 需要 npm install --save-dev postcss-less
    // }
  ],
  // 自定义规则
  rules: {
    // === Stylelint 核心规则自定义 ===
    'selector-class-pattern': null, // 不强制类名选择器的模式 (例如 BEM),根据需要设置,例如:'^[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$'
    'selector-pseudo-class-no-unknown': [true, { ignorePseudoClasses: ['deep', 'global'] }], // 忽略 Vue 的 :deep 和 :global
    'selector-pseudo-element-no-unknown': [true, { ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'] }], // 忽略 Vue 的 ::v-deep 等
    'at-rule-no-unknown': [true, { ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen', 'layer'] }], // 忽略 Tailwind CSS 的 @ 指令
    'rule-empty-line-before': ['always', { // 在规则之前总是需要空行(除了第一个规则和在@规则块内的规则)
      except: ['first-nested'],
      ignore: ['after-comment'],
    }],
    'unit-no-unknown': true, // 禁止未知的单位
    'block-no-empty': true, // 禁止空块
    'no-descending-specificity': null, // 允许特异性较低的选择器出现在较高的选择器之后 (有时难以避免)
    'font-family-no-missing-generic-family-keyword': null, // 不强制要求字体族必须有通用族关键字 (如 sans-serif)

    // === Vue (<style scoped>) 相关规则 (通常由 stylelint-config-standard-vue 处理) ===
    // 确保 scoped 样式中的选择器是有效的

    // === 自定义规则示例 ===
    // 颜色值必须使用小写十六进制或 rgb/rgba
    'color-hex-case': 'lower',
    'color-function-notation': 'legacy', // 使用旧版逗号分隔的 rgb()/rgba() 而不是现代空格分隔的语法,根据兼容性需求选择 'modern'
    // 禁止使用 ID 选择器
    'selector-max-id': 0,
    // 限制选择器的嵌套深度
    'max-nesting-depth': [3, { ignore: ['pseudo-classes'] }], // 嵌套深度最多 3 层,忽略伪类
    // 禁止使用 !important
    'declaration-no-important': true,

    // === 如果使用 stylelint-order 插件 ===
    // 'order/properties-order': [ // 定义 CSS 属性的书写顺序
    //   'position',
    //   'top',
    //   'right',
    //   'bottom',
    //   'left',
    //   'z-index',
    //   'display',
    //   // ... 更多属性
    // ],
  },
  // 忽略文件配置 (也可以使用 .stylelintignore 文件)
  ignoreFiles: [
    'node_modules/**/*',
    'dist/**/*',
    'public/**/*',
    '*.min.css',
    '*.md',
    // 其他需要忽略的文件
  ],
  // 默认严重性级别 ("warning" 或 "error")
  defaultSeverity: 'error',
};

说明:

  • extends: 同样,继承预设配置,stylelint-config-prettier 放在最后。stylelint-config-standard-vue 是关键,它为 Vue SFC 的 <style> 提供了良好的基础。
  • overrides: 针对特定文件类型应用不同的配置,特别是为 .vue 文件指定 customSyntax: 'postcss-html',这样 Stylelint 才能正确解析 <style> 块。如果使用 SCSS/Less,也需要类似配置并安装对应的 postcss-scsspostcss-less
  • rules: 自定义规则。这里添加了一些常用的自定义项,比如放宽类名模式、忽略 Vue 特定的伪类/伪元素、忽略 Tailwind 指令、设置颜色格式、禁止 ID 选择器、限制嵌套深度等。
  • ignoreFiles: 忽略文件。
  • defaultSeverity: 设置违反规则时的默认严重级别。

第六步:安装与配置 Husky

Husky 能让我们更容易地管理 Git 钩子。

  1. 初始化 Husky:

    npx husky init
    npm install # 或者 yarn install (如果 husky init 没有自动安装)
    # 或者 (旧版方式,或手动安装后)
    # npx husky install # 在 CI 环境或首次设置后可能需要
    

    这会在项目根目录创建一个 .husky 文件夹,并在 package.json 中添加 prepare 脚本(用于在 npm install 后自动启用 Husky 钩子)。

  2. 创建 pre-commit 钩子:

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

    这会创建一个名为 pre-commit 的文件在 .husky 目录下,内容大致如下:

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

    这个脚本会在每次执行 git commit 命令之前运行 npx lint-staged 命令。

第七步:安装与配置 lint-staged

lint-staged 的作用是只对 Git 暂存区(git add 之后的文件)执行我们定义的命令,这样可以大大加快提交前的检查速度。

package.json 文件中添加 lint-staged 配置项,或者在根目录创建 .lintstagedrc.js (或 .lintstagedrc.json 等) 文件。推荐使用 package.json

// package.json
{
  // ... 其他配置如 name, version, scripts, dependencies ...
  "devDependencies": {
    // ... 你安装的所有开发依赖 ...
  },
  "scripts": {
    // ... 其他脚本如 serve, build ...
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix && stylelint "**/*.{vue,css,scss,less}" --fix",
    "format": "prettier --write "**/*.{vue,js,jsx,cjs,mjs,ts,tsx,json,css,scss,less,html,md}""
    // "prepare": "husky install" // husky init 应该已经自动添加了这个
  },
  // lint-staged 配置
  "lint-staged": {
    // 对所有暂存的指定类型文件,先执行 Prettier 格式化
    "*.{vue,js,jsx,cjs,mjs,ts,tsx,json,css,scss,less,html,md}": [
      "prettier --write"
    ],
    // 对暂存的 .vue, .js 文件执行 ESLint 检查和自动修复
    // 注意:ESLint 应该在 Prettier 格式化之后运行
    "*.{vue,js,jsx,cjs,mjs}": [
      "eslint --fix"
    ],
    // 如果使用 TypeScript
    // "*.{vue,ts,tsx}": [
    //   "eslint --fix"
    // ],
    // 对暂存的样式相关文件执行 Stylelint 检查和自动修复
    // 注意:Stylelint 也应该在 Prettier 之后运行
    "*.{vue,css,scss,less}": [
      "stylelint --fix"
    ]
    // 如果你的命令很复杂或者需要链式操作失败则停止,可以这样写:
    // "*.{js,vue}": [
    //   "prettier --write",
    //   "eslint --fix",
    //   "git add" // 可选:如果修复后需要重新暂存,但 lint-staged 默认会处理
    // ]
  }
}

说明:

  • 我们在 package.json 中添加了 lintformat 脚本,方便手动执行全项目的检查和格式化。

  • lint-staged 配置是一个对象,键是匹配文件模式的 glob 表达式,值是一个数组,包含要在匹配文件上执行的命令。

  • 执行顺序很重要:

    1. 先运行 prettier --write 对所有相关类型的文件进行格式化。这样可以确保 ESLint 和 Stylelint 检查的是已经被 Prettier 格式化后的代码,避免它们因为格式问题报错。
    2. 然后运行 eslint --fix 对 JS/Vue 文件进行 linting 和自动修复。
    3. 最后运行 stylelint --fix 对样式文件 (包括 Vue 文件中的 <style>) 进行 linting 和自动修复。
  • lint-staged 会自动处理命令执行和文件暂存。如果一个命令失败(例如 ESLint 发现无法自动修复的错误),lint-staged 会退出,阻止 git commit 继续执行。修复错误后,你需要重新 git add 文件,然后再次尝试 git commit

第八步:整合配置(检查)

现在,我们已经完成了所有工具的安装和配置。检查一下:

  1. .prettierrc.js 定义了格式化规则。
  2. .eslintrc.js 定义了 JS/Vue 的 linting 规则,继承了推荐配置,关闭了与 Prettier 的冲突,并添加了自定义规则。
  3. .stylelintrc.js 定义了样式的 linting 规则,继承了推荐配置 (Vue 适用),关闭了与 Prettier 的冲突,配置了 .vue 文件解析,并添加了自定义规则。
  4. .husky/pre-commit 脚本已存在,内容是 npx lint-staged
  5. package.json 中有 lint-staged 配置,指定了对暂存文件依次运行 Prettier, ESLint, Stylelint 的修复命令。

第九步:测试

现在,尝试修改项目中的一些 .vue, .js, 或样式文件,故意引入一些不符合规范的代码(比如:使用双引号、忘记分号、错误的缩进、不符合自定义规则的代码、无效的 CSS 属性等)。

然后执行:

git add . # 将修改的文件添加到暂存区
git commit -m "feat: add feature with auto formatting and linting test"

观察终端输出:

  • 你应该能看到 lint-staged 正在运行 Prettier, ESLint, Stylelint。
  • 如果代码有可以自动修复的格式问题或 linting 问题,这些工具会尝试修复它们。修复后的文件会自动被 lint-staged 重新添加到暂存区。
  • 如果修复成功且没有其他无法自动修复的错误,commit 会成功。
  • 如果存在无法自动修复的 ESLint 或 Stylelint 错误(规则设置为 error),lint-staged 会报错,commit 会被阻止。你需要根据错误提示手动修改代码,然后再次 git addgit commit

示例:触发自定义规则

假设你在 .eslintrc.js 中添加了规则 'vue/require-name-property': 'error'。现在创建一个没有 name 选项的 Vue 组件:

<template>
  <div>Hello</div>
</template>

<script>
export default {
  // Missing name property
  props: {
    msg: String,
  },
};
</script>

<style scoped>
div {
  color: blue; /* 符合 Stylelint 规则 */
}
</style>

当你尝试提交这个文件时 (git add src/components/MyComponent.vue && git commit -m "test"), ESLint 会因为违反了 vue/require-name-property 规则而报错,commit 会失败。你需要添加 name 属性才能成功提交。


总结与后续

通过以上步骤,你已经成功地在 Vue 项目中配置了一个强大的 pre-commit 工作流。每次提交代码时,Husky 会触发 lint-staged,后者会智能地对你修改并暂存的文件执行 Prettier 格式化、ESLint 检查与修复、Stylelint 检查与修复。这不仅保证了代码风格的统一,还能在早期发现并修复潜在的错误,极大地提高了代码质量和团队协作效率。

你可以根据项目的具体需求和团队规范,随时调整 .prettierrc.js, .eslintrc.js, .stylelintrc.js 中的规则。记住,良好的配置需要持续维护和根据项目演进进行调整。