一次搭好 ESLint + Prettier + Husky + lint-staged 前端代码工作流

适用场景:Vite + React + TypeScript 项目。Vue、Webpack 或其他工程也可以沿用相同思路,只需要替换对应的 ESLint 插件与文件匹配规则。

多人协作时,代码规范最容易遇到两个问题:一是每个人的编辑器配置不同,二是规范只写在文档里,却没有进入开发流程。结果往往是本地看起来没有问题,提交后才在代码审查或 CI 中暴露大量格式差异。

一套稳定的前端代码工作流应当把职责拆开:

  • ESLint 负责发现潜在错误和不合理的代码写法;
  • Prettier 负责统一缩进、引号、换行等格式;
  • Husky 负责管理 Git Hooks;
  • lint-staged 只检查暂存区中的文件,避免每次提交都扫描整个项目。

最终流程如下:

sql 复制代码
编写代码
  ↓
编辑器保存时格式化
  ↓
git add 将文件加入暂存区
  ↓
git commit 触发 pre-commit
  ↓
lint-staged 对暂存文件执行 ESLint 与 Prettier
  ↓
检查通过后完成提交

下面从一个新的 Vite 项目开始完成配置。

1. 创建 Vite + React + TypeScript 项目

sql 复制代码
npm create vite@latest frontend-workflow -- --template react-ts
cd frontend-workflow
npm install
npm run dev

新版 Vite 模板通常已经包含基础 ESLint 配置。如果是已有项目,也可以直接从下一节开始,根据当前依赖补装缺少的包。

2. 配置 ESLint

ESLint 负责处理代码质量问题,例如未使用变量、不安全语法和 React Hooks 使用错误。ESLint 9 已默认使用 Flat Config,配置文件通常命名为 eslint.config.js,不再推荐新项目继续使用 .eslintrc.*

2.1 安装依赖

bash 复制代码
npm install -D eslint @eslint/js globals typescript-eslint
npm install -D eslint-plugin-react-hooks eslint-plugin-react-refresh

如果项目尚未生成任何 ESLint 配置,也可以运行初始化向导:

kotlin 复制代码
npm init @eslint/config@latest

初始化时根据实际项目选择浏览器环境、React 和 TypeScript 即可。

2.2 编写 Flat Config

在项目根目录创建或修改 eslint.config.js

php 复制代码
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
​
export default defineConfig([
  globalIgnores(['dist', 'coverage']),
  {
    files: ['**/*.{js,jsx,ts,tsx}'],
    extends: [js.configs.recommended],
    languageOptions: {
      ecmaVersion: 'latest',
      globals: globals.browser,
    },
  },
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      ...tseslint.configs.recommended,
      reactHooks.configs.flat.recommended,
      reactRefresh.configs.vite,
    ],
    rules: {
      '@typescript-eslint/no-unused-vars': [
        'warn',
        {
          argsIgnorePattern: '^_',
          varsIgnorePattern: '^_',
        },
      ],
    },
  },
])

这里做了几件事:

  1. 忽略构建产物与测试覆盖率目录;
  2. 对 JavaScript 和 TypeScript 启用 ESLint 推荐规则;
  3. 为 TypeScript 增加对应解析与规则;
  4. 检查 React Hooks;
  5. 对 Vite 的 React Fast Refresh 使用方式进行约束。

先执行一次检查:

erlang 复制代码
npx eslint .

如果需要自动修复可修复的问题:

css 复制代码
npx eslint . --fix

3. 接入 Prettier

ESLint 更关注代码是否合理,Prettier 更关注代码长什么样。把两者的职责分开,配置更容易维护,也能减少规则冲突。

3.1 安装依赖

lua 复制代码
npm install -D --save-exact prettier
npm install -D eslint-config-prettier

eslint-config-prettier 会关闭与 Prettier 冲突或重复的 ESLint 格式规则。

3.2 创建 Prettier 配置

在根目录创建 prettier.config.mjs

yaml 复制代码
/** @type {import('prettier').Config} */
export default {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: false,
  singleQuote: true,
  trailingComma: 'all',
  bracketSpacing: true,
  arrowParens: 'always',
  endOfLine: 'lf',
}

规则没有绝对标准,团队统一即可。已经存在历史代码的项目,建议先保持原有分号、引号和换行习惯,避免一次格式化产生过大的无意义 Diff。

再创建 .prettierignore

lua 复制代码
node_modules
dist
coverage
package-lock.json
*.min.*

3.3 避免 ESLint 与 Prettier 冲突

eslint.config.js 中引入 eslint-config-prettier,并把它放在配置数组的最后:

javascript 复制代码
import eslintConfigPrettier from 'eslint-config-prettier/flat'

export default defineConfig([
  // 其他 ESLint 配置
  eslintConfigPrettier,
])

放在最后的原因是让它覆盖前面配置中可能与 Prettier 冲突的格式规则。

本文不把 Prettier 作为 ESLint 规则运行,而是让 eslint --fixprettier --write 分别执行。这样职责更清晰,命令输出也更容易定位。

4. 增加项目脚本

package.json 中增加以下脚本:

json 复制代码
{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier . --write",
    "format:check": "prettier . --check",
    "typecheck": "tsc -b --pretty false"
  }
}

日常使用:

arduino 复制代码
npm run lint
npm run lint:fix
npm run format
npm run format:check
npm run typecheck

建议在 CI 中至少执行 lintformat:check 和项目构建。提交前的 Git Hook 是第一层保护,CI 是无法绕过的最终检查。

5. 配置 VS Code 保存时自动处理

安装以下扩展:

  • ESLint
  • Prettier - Code formatter

在项目中创建 .vscode/settings.json

swift 复制代码
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "files.eol": "\n"
}

提交 .vscode/settings.json 后,团队成员打开项目即可获得一致的基础保存行为。即使某位成员不使用 VS Code,后面的 Git Hook 仍会在提交阶段执行统一检查。

6. 安装 Husky 与 lint-staged

6.1 安装依赖

复制代码
npm install -D husky lint-staged

6.2 初始化 Husky

csharp 复制代码
npx husky init

该命令会创建 .husky/pre-commit,并在 package.json 中增加 prepare 脚本。新版 Husky 不需要再使用旧教程中的 husky installhusky add 命令。

.husky/pre-commit 内容改为:

复制代码
npx lint-staged

这表示每次执行 git commit 时,先运行 lint-staged;只有检查成功后才继续提交。

7. 配置 lint-staged

在项目根目录创建 lint-staged.config.mjs

arduino 复制代码
/** @type {import('lint-staged').Configuration} */
export default {
  '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
  '*.{json,css,scss,less,html,md,yml,yaml}': 'prettier --write',
}

lint-staged 会自动把匹配到的暂存文件路径追加到命令后面,因此不需要手动拼接路径。它也会处理修复后的暂存状态,新版配置无需再额外执行 git add

与全量执行 eslint . 相比,这种方式有两个优势:

  • 提交速度更快,只处理本次修改的文件;
  • 老项目接入时不会突然格式化全部历史代码。

8. 验证完整工作流

故意在某个 TypeScript 文件中加入格式不规范的代码,然后执行:

sql 复制代码
git add .
git commit -m "test: verify code workflow"

正常情况下会依次发生:

  1. Git 触发 .husky/pre-commit
  2. lint-staged 获取暂存区文件;
  3. ESLint 修复可自动处理的问题;
  4. Prettier 统一文件格式;
  5. 如果仍存在 ESLint 错误,提交被中止;
  6. 检查全部通过,提交继续完成。

也可以不提交,先手动测试:

复制代码
npx lint-staged

9. 常见问题

9.1 为什么不在 Vite 开发服务器中强制运行 ESLint?

可以使用 Vite 插件在开发阶段实时检查,但它会把代码检查和构建过程绑定在一起,大型项目中可能影响启动或热更新速度。更通用的方案是编辑器即时提示、Git Hook 检查增量文件、CI 检查全量文件。

9.2 ESLint 与 Prettier 为什么会互相打架?

通常是 ESLint 中仍开启了缩进、引号、分号等格式类规则。确保安装 eslint-config-prettier,并将其放到 Flat Config 数组靠后的位置。

9.3 为什么提交时没有触发 Hook?

依次检查:

arduino 复制代码
npm run prepare
git config core.hooksPath

同时确认 .husky/pre-commit 已提交到 Git,并且项目确实位于 Git 仓库内。

9.4 老项目如何逐步接入?

不要一开始就全量运行 prettier . --write。可以先接入 lint-staged,让新增和修改的文件逐渐符合规范;等团队确认规则后,再单独提交一次全量格式化,避免格式变化与业务代码混在一起。

9.5 能否绕过 Git Hook?

Git Hook 可以被 --no-verify 跳过,因此不能代替 CI。可靠的团队流程应该在 CI 中再次运行:

arduino 复制代码
npm run lint
npm run format:check
npm run typecheck
npm run build

10. 总结

完成以上配置后,代码质量约束不再只依赖个人习惯:

  • 编辑器负责快速反馈与保存格式化;
  • ESLint 负责代码质量;
  • Prettier 负责统一格式;
  • lint-staged 控制检查范围;
  • Husky 把检查接入提交动作;
  • CI 对整个仓库进行最终兜底。

工具本身并不是目标。真正有价值的是把团队认可的规范固化到开发流程中,让问题尽量在代码进入仓库之前被发现。

参考资料

相关推荐
zhifou1234561 小时前
Vue基础(二)
前端·javascript·vue.js
两只羊ovo2 小时前
手写一个最小版 Claude Code:从任务拆解到 Agent Loop 转起来
前端
给个offer养家糊口2 小时前
抽离 elpis npm 包
前端
默_笙2 小时前
🙃 我让爬虫终于看到了我的网站,后端同事说"这也行?"(下):App Router 全栈实战
前端·javascript
葡萄城技术团队2 小时前
一个单元格放置两个日期选择器:用 SpreadJS CellButtons 录入日期范围
前端
马可家的菠萝3 小时前
自动保存已经有了,为什么笔记软件还需要“历史版本”?
前端·后端·架构
半个落月3 小时前
从 "use client" 到 Route Handler:用 Todos 理解 Next.js 水合与全栈请求
前端·react.js·next.js
qq_452396233 小时前
第七篇:《大型前端项目的模块化与目录结构设计》
前端
你脑门上的脚印3 小时前
Vue 项目从零实现语音转文字、文字转语音功能(完整可用 + 踩坑指南)
前端·vue.js