ESLint + Prettier 代码规范

发布于:2025-09-02 ⋅ 阅读:(14) ⋅ 点赞:(0)

ESLint 和 Prettier 是前端开发中用于代码规范和格式化的两大工具,配置好之后可以对项目进行规范,不过要是不是特别严格的开发团队,我觉得就使用警告的方式并且不要自动格式化。一般编译器都可以下载插件然后自动格式化。

一、工具核心作用

1. ESLint:代码质量检查工具
  • 核心功能:通过静态分析检测代码中的语法错误、逻辑问题、不符合编码规范的写法(如未使用的变量、不合理的条件判断、不符合团队约定的命名等)。
  • 示例场景:
    • 检测到未声明的变量(可能是拼写错误)。
    • 函数内存在无法到达的代码(如 return 后的语句)。
    • 强制使用 === 而非 == 避免隐式类型转换。
  • 灵活性:支持自定义规则(通过 .eslintrc 配置),可集成第三方规则集(如 eslint-config-airbnbeslint-config-standard)。
2. Prettier:代码格式化工具
  • 核心功能:专注于代码的格式美化,不关心代码逻辑,仅统一代码的缩进、换行、引号、空格等样式(如强制使用单引号、末尾加分号、对象属性换行规则等)。
  • 示例场景:
    • const a = "hello" 格式化为 const a = 'hello'(统一单引号)。
    • 将过长的函数参数自动换行,确保每行代码长度不超过设定值(如 80 字符)。
    • 统一数组、对象的括号前后空格(如 { key: 1 } 而非 {key:1})。
  • 特点:规则相对固定(可配置项较少),强调 “无配置” 的开箱即用,减少团队因格式细节争论。

二、核心区别:关注点不同

维度 ESLint Prettier
关注内容 代码质量(逻辑、语法错误) 代码格式(样式、排版)
规则性质 可大量自定义(灵活) 配置项少(强调统一)
冲突可能性 可能与 Prettier 规则冲突 专注格式,不与 ESLint 质量规则冲突
典型规则示例 禁止未使用的变量(no-unused-vars 强制使用单引号(singleQuote: true

三、协同配置:解决冲突,强强联合

由于 ESLint 也包含部分格式相关的规则(如缩进、引号),可能与 Prettier 冲突(例如 ESLint 强制双引号,而 Prettier 强制单引号)。因此需要通过以下步骤协同工作:

1. 安装必要依赖
# 核心工具
npm install eslint prettier --save-dev

# 解决 ESLint 与 Prettier 的冲突
npm install eslint-config-prettier --save-dev
# 让 ESLint 能够运行 Prettier 的格式化规则(作为 ESLint 规则执行)
npm install eslint-plugin-prettier --save-dev
2. 配置文件(.eslintrc.js)

通过 eslint-config-prettier 禁用 ESLint 中与 Prettier 冲突的格式规则,再通过 eslint-plugin-prettier 将 Prettier 格式规则作为 ESLint 规则运行(即格式问题会被 ESLint 检测为错误)。

module.exports = {
  extends: [
    // 基础规则集(如标准规则)
    'eslint:recommended',
    // 禁用 ESLint 中与 Prettier 冲突的规则
    'prettier',
    // 让 Prettier 规则以 ESLint 规则的形式生效(必须放在最后)
    'plugin:prettier/recommended'
  ],
  plugins: ['prettier'],
  rules: {
    // 自定义 ESLint 质量规则(如禁止 console)
    'no-console': 'warn',
    // Prettier 格式规则通过 .prettierrc 配置,此处无需重复设置
  }
};
3. 配置 Prettier(.prettierrc)

通过 .prettierrc 定义格式规则(可选,默认已有合理配置):

{
  "singleQuote": true,      // 强制单引号
  "semi": true,             // 语句末尾加分号
  "printWidth": 80,         // 每行最大字符数
  "tabWidth": 2,            // 缩进空格数
  "trailingComma": "es5"    // 对象/数组最后一个元素后加逗号(如 { a: 1, })
}
4. 忽略文件(可选)
  • .eslintignore:指定 ESLint 忽略的文件(如 node_modules/)。
  • .prettierignore:指定 Prettier 忽略的文件(与 ESLint 忽略规则可保持一致)。

四、使用方式:自动化执行

  1. 命令行手动执行
    • 仅 ESLint 检查:npx eslint src/(检查 src 目录下的文件)。
    • 仅 Prettier 格式化:npx prettier --write src/(自动修复格式问题)。
    • 结合 ESLint 执行 Prettier:npx eslint src/ --fix(同时修复 ESLint 可修复的问题和 Prettier 格式问题)。
  2. 编辑器集成
    • 在 VS Code 中安装 ESLintPrettier 插件,配置保存时自动修复(editor.formatOnSave: true + editor.codeActionsOnSave: { "source.fixAll.eslint": true })。
  3. 提交代码时自动检查
    • 通过 husky + lint-staged 在代码提交前自动执行 ESLint 和 Prettier,确保提交的代码符合规范(避免不合规代码进入仓库)。

五、总结

  • ESLint 负责 “把关代码质量”,解决语法错误和逻辑问题;
  • Prettier 负责 “统一代码颜值”,解决格式不统一的问题;
  • 二者结合时,通过 eslint-config-prettiereslint-plugin-prettier 消除冲突,实现 “质量 + 格式” 双重保障,是现代前端工程化中不可或缺的工具链组合。

ESLint配置

module.exports = {
  root: true,
  // 环境配置
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  // 继承的规则集
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:import/recommended',
    'plugin:import/typescript',
    'prettier' // 与Prettier兼容,必须放在最后
  ],
  // 解析器配置
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true // 如不使用JSX可设为false
    }
  },
  // 插件
  plugins: [
    'vue',
    '@typescript-eslint',
    'import',
    'unused-imports' // 自动移除未使用的导入
  ],
  // 自定义规则
  rules: {
    // 基础JavaScript规则
    'eqeqeq': ['error', 'always'], // 强制使用===
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-var': 'error', // 禁止使用var
    'prefer-const': 'error', // 优先使用const
    'no-unused-vars': 'off', // 由@typescript-eslint/no-unused-vars替代

    // Vue规则
    'vue/script-setup-uses-vars': 'error', // 确保<script setup>中变量被使用
    'vue/no-mutating-props': 'error', // 禁止修改props
    'vue/attributes-order': 'warn', // 属性排序建议
    'vue/no-unused-components': 'warn', // 未使用组件警告
    'vue/require-prop-types': 'error', // 强制props类型定义
    'vue/multi-word-component-names': ['error', {
      ignores: ['index'] // 允许index作为单字组件名
    }],

    // TypeScript规则
    '@typescript-eslint/no-unused-vars': ['warn', {
      argsIgnorePattern: '^_',
      varsIgnorePattern: '^_'
    }],
    '@typescript-eslint/explicit-module-boundary-types': 'off', // 放宽导出函数返回类型检查
    '@typescript-eslint/no-explicit-any': ['warn', {
      ignoreRestArgs: true // 允许...args: any[]
    }],
    '@typescript-eslint/consistent-type-imports': 'error', // 统一使用import type
    '@typescript-eslint/no-non-null-assertion': 'warn', // 非空断言警告
    
    // 导入规则
    'import/order': ['warn', {
      'groups': [['builtin', 'external'], 'internal', ['parent', 'sibling', 'index']],
      'newlines-between': 'always'
    }],
    'unused-imports/no-unused-imports': 'error', // 自动移除未使用的导入
    'unused-imports/no-unused-vars': 'off',

    // 其他规则
    'no-trailing-spaces': 'error', // 禁止行尾空格
    'eol-last': ['error', 'always'], // 文件末尾必须有空行
    'quotes': 'off' // 由Prettier控制引号
  },
  // 全局变量
  globals: {
    defineProps: 'readonly',
    defineEmits: 'readonly',
    defineExpose: 'readonly',
    withDefaults: 'readonly',
    defineModel: 'readonly',
    defineOptions: 'readonly'
  },
  // 针对特定文件的规则覆盖
  overrides: [
    {
      files: ['*.ts', '*.tsx', '*.vue'],
      rules: {
        'no-undef': 'off' // TypeScript会处理未定义变量检查
      }
    }
  ]
};

Prettier配置

{
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "singleQuote": true,
  "semi": true,
  "trailingComma": "all",
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  
   quoteProps: 'as-needed',
   jsxSingleQuote: false,
   jsxBracketSameLine: false,
   rangeStart: 0,
   rangeEnd: Infinity,
   requirePragma: false,
   insertPragma: false,
   proseWrap: 'preserve',
   htmlWhitespaceSensitivity: 'css',
}

ESLint 8.23+ 扁平化配置

import globals from "globals";
import pluginVue from "eslint-plugin-vue";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import importPlugin from "eslint-plugin-import";
import unusedImports from "eslint-plugin-unused-imports";
import prettier from "eslint-config-prettier";

// 环境变量配置
const env = {
  browser: true,
  node: true,
  es2025: true,
};

// 全局变量配置
const globalVariables = {
  ...globals.browser,
  ...globals.node,
  defineProps: "readonly",
  defineEmits: "readonly",
  defineExpose: "readonly",
  withDefaults: "readonly",
  defineModel: "readonly",
  defineOptions: "readonly",
};

// 基础规则配置
export default [
  // 忽略文件
  {
    ignores: ["node_modules/**", "dist/**", "*.d.ts", "*.generated.ts"],
  },

  // 全局设置
  {
    env,
    globals: globalVariables,
    languageOptions: {
      ecmaVersion: "latest",
      sourceType: "module",
    },
  },

  // Prettier 配置(关闭与Prettier冲突的规则)
  prettier,

  // Vue 规则
  ...pluginVue.configs["flat/essential"],
  {
    files: ["*.vue", "**/*.vue"],
    languageOptions: {
      parser: pluginVue.parser,
      parserOptions: {
        parser: tsParser,
        ecmaVersion: "latest",
        sourceType: "module",
      },
    },
    rules: {
      ...pluginVue.configs["flat/recommended"].rules,
      "vue/script-setup-uses-vars": "error",
      "vue/no-mutating-props": "error",
      "vue/attributes-order": "warn",
      "vue/no-unused-components": "warn",
      "vue/require-prop-types": "error",
      "vue/multi-word-component-names": ["error", {
        ignores: ["index"]
      }],
      "vue/no-v-model-argument": "off",
    },
  },

  // TypeScript 规则
  {
    files: ["*.ts", "**/*.ts", "*.vue", "**/*.vue"],
    plugins: {
      "@typescript-eslint": tseslint,
      "unused-imports": unusedImports,
    },
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: "./tsconfig.json", // 确保存在tsconfig.json
      },
    },
    rules: {
      ...tseslint.configs.recommended.rules,
      "@typescript-eslint/no-unused-vars": ["warn", {
        argsIgnorePattern: "^_",
        varsIgnorePattern: "^_",
      }],
      "@typescript-eslint/explicit-module-boundary-types": "off",
      "@typescript-eslint/no-explicit-any": ["warn", {
        ignoreRestArgs: true,
      }],
      "@typescript-eslint/consistent-type-imports": "error",
      "@typescript-eslint/no-non-null-assertion": "warn",
      "unused-imports/no-unused-imports": "error",
    },
  },

  // 导入规则
  {
    plugins: {
      import: importPlugin,
    },
    rules: {
      "import/order": ["warn", {
        groups: [["builtin", "external"], "internal", ["parent", "sibling", "index"]],
        "newlines-between": "always",
      }],
    },
  },

  // 通用JavaScript规则
  {
    rules: {
      "eqeqeq": ["error", "always"],
      "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
      "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
      "no-var": "error",
      "prefer-const": "error",
      "no-unused-vars": "off", // 由TypeScript规则接管
      "no-trailing-spaces": "error",
      "eol-last": ["error", "always"],
    },
  },
];
    

网站公告

今日签到

点亮在社区的每一天
去签到