前端工程化最佳实践:ESLint+Prettier+Git Hooks 统一开发规范

前端工程化最佳实践:ESLint+Prettier+Git Hooks 统一开发规范


目标与收益

  • 一致风格与质量:统一代码风格、导入顺序与常见错误拦截
  • 提交即校验:在提交阶段自动修复与拦截不合规改动
  • 持续集成:CI 对齐本地规则,保证主干稳定

安装与基础脚本

bash 复制代码
pnpm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-vue eslint-plugin-react eslint-plugin-import eslint-plugin-simple-import-sort
pnpm i -D prettier eslint-config-prettier eslint-plugin-prettier
pnpm i -D husky lint-staged @commitlint/cli @commitlint/config-conventional

package.json 脚本与 lint-staged:

json 复制代码
{
  "scripts": {
    "lint": "eslint --ext .ts,.tsx,.js,.vue src",
    "lint:fix": "eslint --ext .ts,.tsx,.js,.vue src --fix",
    "format": "prettier --write .",
    "test": "vitest run || jest"
  },
  "lint-staged": {
    "src/**/*.{ts,tsx,js,vue}": ["eslint --fix", "prettier --write"],
    "src/**/*.{css,scss,md}": ["prettier --write"]
  }
}

ESLint 配置(TS + Vue/React 通用示例)

.eslintrc.cjs

js 复制代码
module.exports = {
  root: true,
  env: { browser: true, es2021: true, node: true },
  parser: '@typescript-eslint/parser',
  parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
  plugins: ['@typescript-eslint', 'import', 'simple-import-sort', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:import/recommended',
    'plugin:import/typescript',
    'prettier'
  ],
  settings: { react: { version: 'detect' } },
  rules: {
    'prettier/prettier': ['error'],
    'import/order': ['off'],
    'simple-import-sort/imports': ['error'],
    'simple-import-sort/exports': ['error'],
    '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    'vue/multi-word-component-names': 'off'
  },
  overrides: [
    { files: ['**/*.vue'], rules: { 'react/jsx-uses-react': 'off', 'react/react-in-jsx-scope': 'off' } },
    { files: ['**/*.tsx'], rules: { 'vue/no-unused-components': 'off' } }
  ]
}

Prettier 配置

.prettierrc.json

json 复制代码
{ "semi": false, "singleQuote": true, "printWidth": 100, "trailingComma": "es5" }

可选 .editorconfig

ini 复制代码
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
end_of_line = lf
insert_final_newline = true

Git Hooks(Husky)

初始化与钩子:

bash 复制代码
npx husky init

.husky/pre-commit

sh 复制代码
npx lint-staged

.husky/commit-msg

sh 复制代码
npx commitlint --edit "$1"

.husky/pre-push

sh 复制代码
pnpm test

commitlint.config.cjs

js 复制代码
module.exports = { extends: ['@commitlint/config-conventional'] }

提交信息示例:

text 复制代码
feat(auth): 支持短信验证码登录
fix(router): 修复刷新后白屏问题
chore(deps): 升级 eslint 到最新版本

Monorepo 与工作区建议

  • 使用 pnpm workspaces 与 Turbo/Changesets 管理多包;在根目录统一 ESLint/Prettier 配置
  • lint-staged 可在根目录配置并针对各包的 src/ 路径匹配
  • 在 CI 中按变更范围运行 lint/test(如 Turbo pipeline lint/test

CI 配置(GitHub Actions)

.github/workflows/quality.yml

yaml 复制代码
name: Quality
on: { pull_request: { branches: [main] }, push: { branches: [main] } }
jobs:
  lint_test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'pnpm' }
      - run: corepack enable
      - run: pnpm i --frozen-lockfile
      - run: pnpm lint
      - run: pnpm test -- --coverage

规则扩展与导入排序

  • 导入排序统一:simple-import-sort 以组为单位排序,避免冲突
  • 不同栈规则:Vue 项目开启 plugin:vue/vue3-recommended;React 项目开启 react-hooks 规则
  • 忽略与例外:对自动生成文件与构建产物设置 .eslintignore.prettierignore

.eslintignore.prettierignore

text 复制代码
dist
node_modules
coverage
*.min.js

常见坑与修复

  • ESLint 与 Prettier 冲突:确保 extends 中最后是 prettier,开启 prettier/prettier 错误级别
  • Husky 不生效:确认 .husky 目录在仓库根且钩子文件可执行;在 CI 环境只执行脚本不启用钩子
  • lint-staged 过慢:控制匹配范围与并发;避免在钩子里运行全量测试
  • 多栈共存:通过 overrides 针对 .vue.tsx 设置规则差异

IDE 集成与本地一致性

json 复制代码
{
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": { "source.fixAll.eslint": true },
  "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue" ]
}

提交规范与自动发布(可选)

feat 映射 minorfix 映射 patch、含 BREAKING CHANGE 映射 major

Changesets 或 semantic-release 可自动生成版本与变更日志。

性能优化与门禁

json 复制代码
{
  "lint-staged": {
    "src/**/*.{ts,tsx,js,vue}": ["eslint --fix --cache", "prettier --write"],
    "*.{css,scss,md}": ["prettier --write"]
  }
}
json 复制代码
{
  "coverageThreshold": { "global": { "branches": 70, "functions": 70, "lines": 70 } }
}

规则模板与扩展建议

  • Vue:vue/no-mutating-propsvue/require-prop-types
  • React:react/jsx-no-useless-fragmentreact/no-array-index-keyreact-hooks/exhaustive-deps
  • 样式与测试:按需加入 stylelinteslint-plugin-jest@testing-library/eslint-plugin

规范约束矩阵(示例)

  • 代码风格:Prettier 统一;ESLint 禁止未用变量与未排序导入
  • 提交信息:Conventional Commits(type(scope): subject)
  • 质量门禁:pre-commit 运行 lint-staged;pre-push 运行测试;CI 覆盖率阈值
  • 编辑器一致性:保存即格式化与自动修复;统一换行与缩进

commitlint 高级配置(示例)

commitlint.config.cjs

js 复制代码
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', ['feat','fix','chore','docs','refactor','perf','test','build','ci','revert']],
    'scope-enum': [2, 'always', ['app','ui','api','deps','lint','release']],
    'subject-case': [2, 'never', ['sentence-case','start-case','pascal-case','upper-case']],
    'header-max-length': [2, 'always', 72]
  }
}

导入排序自定义分组

.eslintrc.cjs 片段:

js 复制代码
rules: {
  'simple-import-sort/imports': ['error', {
    groups: [
      ['^react', '^vue', '^@?\w'],
      ['^(@/)(.*)$'],
      ['^\.\.(?!/?$)', '^\.'],
      ['^.+\.s?css$']
    ]
  }]
}

行尾与换行一致性

.gitattributes

text 复制代码
* text=auto eol=lf

统一跨平台换行(LF),避免格式化差异。

lint-staged 性能优化

在钩子中开启 ESLint 缓存、控制并发与匹配范围:

json 复制代码
{
  "lint-staged": {
    "src/**/*.{ts,tsx,js,vue}": ["eslint --fix --cache", "prettier --write"],
    "*.{css,scss,md}": ["prettier --write"]
  }
}

迁移步骤(落地)

  • 第一步:在根目录新增 ESLint/Prettier/commitlint 配置与忽略文件
  • 第二步:初始化 Husky 与 lint-staged,开启 pre-commit/commit-msg/pre-push
  • 第三步:在 CI 增加质量工作流(lint/test/coverage)
  • 第四步:统一编辑器设置与扩展,保存即格式化
  • 第五步:每月回顾规则与规范产出,按反馈迭代(新增或降低规则级别)

落地清单(10 项)

  • 在根目录统一 ESLint/Prettier 配置并落库
  • 初始化 Husky 并启用 pre-commit/commit-msg/pre-push
  • 配置 lint-staged 针对源码目录匹配
  • 引入 commitlint 并规范提交信息
  • 为导入排序与未用变量设定统一规则
  • 设置 ignore 列表,避免构建产物被格式化
  • 在 CI 中运行 linttest 并采集覆盖率
  • 为 Monorepo 统一配置并按变更范围运行管线
  • 在编辑器启用 ESLint 与 Prettier 扩展(保存即格式化)
  • 每月回顾规则与产出,按团队反馈迭代

总结

  • 通过 ESLint+Prettier+Git Hooks 的组合,在提交与 CI 阶段统一质量门禁
  • 配合导入排序、类型规则与提交规范,团队可持续保持高一致性与低维护成本
相关推荐
SakuraOnTheWay3 小时前
React Grab实践 | 记一次与Cursor的有趣对话
前端·cursor
阿星AI工作室3 小时前
gemini3手势互动圣诞树保姆级教程来了!附提示词
前端·人工智能
徐小夕3 小时前
知识库创业复盘:从闭源到开源,这3个教训价值百万
前端·javascript·github
xhxxx3 小时前
函数执行完就销毁?那闭包里的变量凭什么活下来!—— 深入 JS 内存模型
前端·javascript·ecmascript 6
StarkCoder3 小时前
求求你试试 DiffableDataSource!别再手算 indexPath 了(否则迟早崩)
前端
fxshy3 小时前
Cursor 前端Global Cursor Rules
前端·cursor
红彤彤3 小时前
前端接入sse(EventSource)(@fortaine/fetch-event-source)
前端
WindStormrage3 小时前
umi3 → umi4 升级:踩坑与解决方案
前端·react.js·cursor
十一.3663 小时前
103-105 添加删除记录
前端·javascript·html