深夜十一点,太阳餐厅的后厨依然灯火通明。主厨老王刚研发出一道新菜,正要端出厨房,却被质检员陈姐拦了下来:“老王,你这道菜的食材处理方式不合规矩啊!”紧接着,摆盘师小美接过盘子,把歪七扭八的配菜重新摆得整整齐齐。最后,门口传菜员大刘探进头来:“今天的菜太杂,我先查查新出锅的那几道,确认没问题才能端出去。” 这就是代码规范工具链的日常:ESLint 是质检员,Prettier 是摆盘师,Husky 是传菜员,lint-staged 是大刘手里的传菜清单。它们共同保障团队代码的“出品质量”——即使十个厨师轮班掌勺,端上桌的每道菜(每次代码提交)都看起来像出自同一人之手。本文将用这个厨房故事,带你认识前端工程化中这套最实用的规范工具链。

岗位关联

学会用 ESLint / Prettier / Husky 管理代码规范,是 AI 时代前端工程师的基本职业素养:它能让你与 AI 协作生产代码时,仍能守住代码质量与团队风格统一的生命线。

目录
  1. 可视化演示
  2. 规范三板斧
  3. ESLint 规则配置
  4. Prettier 格式化
  5. Husky 提交钩子
  6. lint-staged 增量检查
  7. 自定义 ESLint 规则
  8. Monorepo 规范配置
  9. 团队落地实践
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示ESLint、Prettier与Husky构成的前端代码规范全链路工具,包括规则检查流程、lint-staged增量校验与pre-commit钩子机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

代码规范工具链流程 ESLint检查代码质量规则(未使用变量、隐式类型转换等) Prettier统一代码格式化(缩进、引号、换行风格) code → ESLint(quality) → Prettier(format) → Husky(hook) → commit

规范三板斧

太阳餐厅的后厨有条不成文的规矩:任何一道菜端出厨房前,必须经过三道工序。第一道是质检员陈姐,她拿着《后厨守则》逐项核对食材处理是否干净、火候是否合规;第二道是摆盘师小美,她不管菜品口味如何,一律按店里的标准摆盘;第三道是传菜员大刘,他守在厨房门口,确认今天传菜间没有夹带“来历不明”的菜品。三道工序各司其职,缺一不可。

前端工程的代码规范工具链,正是这样一套“后厨流水线”。ESLint 对应陈姐——它扫描 JavaScript 代码,找出未使用的变量、可疑的相等比较、危险 API 调用等问题;Prettier 对应小美——它不关心逻辑对错,只负责把缩进、引号、分号、换行统一成同一种风格;Husky 对应大刘——它钩住 Git 的提交动作,在 git commit 之前强制跑一遍检查,不通过就不放行。

这三者常被合称为“代码规范三板斧”,它们各有各的 npm 包:eslint、prettier、husky。安装顺序也很讲究:先装 ESLint 和 Prettier 做检查与格式化,再用 Husky 把检查绑定到 Git 钩子上。执行时,代码按“写出 → ESLint 检查 → Prettier 格式化 → Husky 拦截”的顺序行进。一套流程可以浓缩为一个逻辑表达式:

就这样,老王发现后厨的秩序变了:以前十位厨师各写各的,代码风格五花八门;现在有了陈姐、小美和大刘,每个人写的代码放进仓库后,都像同一个人写的。这正是规范化带给团队的最大价值——确定性。

check_result=ESLint(f)∧Prettier(f)∧Husky(s)\text{check\_result} = \mathrm{ESLint}(f) \land \mathrm{Prettier}(f) \land \mathrm{Husky}(s)
公式解读:一份代码文件 f 要依次通过 ESLint 检查、Prettier 格式化校验、Husky 对暂存文件集合 s 的钩子检查,三者全部“为真”(通过),整个提交才算合格。
  1. ESLint(f) 静态质检f 表示某份待检查的源码文件,ESLint 读取它并对照规则产出问题列表。
    ESLint(f)\mathrm{ESLint}(f)
  2. Prettier(f) 格式校验Prettier 对同一份文件做格式化,若结果与原文件不一致则视为格式不合格。
    Prettier(f)\mathrm{Prettier}(f)
  3. Husky(s) 钩子把关s 是本次提交涉及的文件集合,Husky 在提交前触发钩子运行检查命令。
    Husky(s)\mathrm{Husky}(s)
  4. 与运算串联\land 表示三个结果必须同时为真,任何一个环节失败,整个提交都会被拦截。
    A∧B∧CA \land B \land C

ESLint 规则配置

陈姐之所以能一眼看出食材处理不合规,是因为她手里有一本厚厚的《后厨守则》。守则写了什么,她就查什么;守则没写的,她绝不越权多管。ESLint 的工作方式正是如此:它的全部行为都由“规则”驱动,规则决定了哪些代码该报错、哪些该警告。

在 ESLint 中,一切配置围绕 rules 字段展开。每条规则都有三级“执法力度”:off 表示关闭这条规则,warn 表示发现问题只给黄色警告不阻断流程,error 表示升级为红色错误、直接让检查不通过。常见规则如 no-unused-vars 检查未使用的变量,no-console 检查是否残留 console.log,eqeqeq 强制使用 === 而非 ==。这些规则可以写在 .eslintrc.json 文件中,也可以借助 extends 继承 eslint:recommended 或 airbnb-base 等成熟规则集。

规则的强度本身就是一个三档枚举值,可以写成:

配置文件是团队规范的“立法文件”。.eslintrc.json 里的 extends 字段负责继承,plugins 字段负责加载扩展规则,rules 字段则针对具体规则做微调。比如团队想禁止 console.log 进入主分支,就在 rules 里写 "no-console": "error"。陈姐的守则也一样,哪天店长说“不许再用老抽上色”,守则加一行,质检立刻生效。

severity∈{0 (off), 1 (warn), 2 (error)}\text{severity} \in \{0\,(\mathrm{off}),\ 1\,(\mathrm{warn}),\ 2\,(\mathrm{error})\}
公式解读:ESLint 给每条规则配置一个严重等级,0 关闭、1 警告、2 报错,数字越小越宽松。
  1. 0 表示关闭规则规则完全不起作用,适合暂时不想约束的检查项。
    0 (off)0\,(\mathrm{off})
  2. 1 表示警告级别检查到问题只输出黄色提示,不改变命令的退出码。
    1 (warn)1\,(\mathrm{warn})
  3. 2 表示错误级别检查到问题输出红色错误,并让进程以失败状态退出。
    2 (error)2\,(\mathrm{error})

Prettier 格式化

陈姐查完食材,小美才接手摆盘。她从不评判口味,只做一件事:把每道菜的配菜位置、酱汁淋法、葱花切法统一成店里的标准模样。哪怕是一盘炒糊的菜,她也会先把摆盘修正得漂漂亮亮——至于糊没糊,那是陈姐该管的事。Prettier 就是这个小美。

Prettier 的核心理念是“固执的格式化器”:它不给用户太多选项,只按一套默认审美重排代码。开发者安装 prettier 后,在 package.json 里声明 "format": "prettier --write .",它就会把所有源码统一重写一遍。常见配置项包括 semi(是否加分号)、singleQuote(是否用单引号)、trailingComma(尾部逗号策略)、printWidth(每行最大宽度)。这些参数写在 .prettierrc 文件中。

Prettier 的核心操作可以理解为一个纯函数:输入原始源码,输出格式化后的新源码,新源码只由旧源码和配置决定。

Prettier 与 ESLint 不是竞争关系,而是互补关系。业界常用 eslint-config-prettier 关掉 ESLint 中与格式相关的规则,让 ESLint 专注查逻辑问题、Prettier 专注排风格,两边不再打架。这就像陈姐检查食材新鲜度、小美负责摆盘,两人互不越界,但配合得天衣无缝。

f′=Prettier.format(f, config)f' = \mathrm{Prettier.format}(f,\ \text{config})
公式解读:Prettier 接收源码 f 和配置 config,返回重新排版后的源码 f'。它不修改原文件,除非你显式写回。
  1. Prettier.format 格式化入口Prettier 暴露的编程接口,接受源码字符串作为输入。
    Prettier.format\mathrm{Prettier.format}
  2. config 格式配置决定风格细节的配置对象,如引号类型、分号开关等。
    config\text{config}
  3. 输出新源码f' 表示根据输入与配置生成的重排版代码,是唯一产出物。
    f′f'

Husky 提交钩子

菜做好了,检查也过了,摆盘也齐了,但大刘仍不放心。他怕有人趁他不注意,把没检查过的菜偷偷端出去。于是他的守则是:凡是离开厨房的菜,都必须经过我这道门,我要亲眼确认它过了陈姐和小美那两关。Husky 就是这道门,守的是 Git 提交。

Git 本身有“钩子”(Hooks)机制:在特定动作发生时,Git 会去 .git/hooks 目录下寻找对应脚本并执行。Husky 的价值在于把这些脚本的配置从晦涩的 .git 目录里解放出来,统一放进项目的 .husky 目录中,并跟随代码仓库一起分享给所有开发者。最常见的钩子叫 pre-commit,它在提交刚被创建、还没有最终生效时触发。

pre-commit 的行为逻辑可以看作一个条件分支:

配置 Husky 非常简单:项目初始化时运行 npx husky init,它会生成 .husky/pre-commit 文件;开发者只需在该文件里写上 npm run lint,大刘就正式上岗了。从此以后,任何试图绕过检查的提交都会被这道铁门拦下——这比在代码审查时靠人工提醒“记得跑一下 lint”可靠得多。

commit={passif lint_staged=trueabortotherwise\mathrm{commit} = \begin{cases} \mathrm{pass} & \text{if } \mathrm{lint\_staged} = \mathrm{true} \\ \mathrm{abort} & \text{otherwise} \end{cases}
公式解读:Git 提交时先判断预检查命令是否成功:成功则提交通过,失败则中止提交。这保证了不合规的代码根本无法进入仓库历史。
  1. pre-commit 触发阶段git commit 执行到一半时,Git 先运行钩子脚本。
    commit\mathrm{commit}
  2. lint_staged 检查条件真正执行检查的是 lint-staged 命令,其结果为真则放行。
    lint_staged=true\mathrm{lint\_staged} = \mathrm{true}
  3. pass 放行提交检查全部通过,允许本次提交写入版本历史。
    pass\mathrm{pass}
  4. abort 中止提交任一检查失败,提交被终止,代码留在工作区等待修改。
    abort\mathrm{abort}

lint-staged 增量检查

大刘上岗第一天就发现一个问题:餐厅有三百道历史菜品,如果每次传菜都要把仓库里所有菜重新检查一遍,不仅耗时间,还会翻出许多陈年旧账把他淹没。于是他改了策略:只检查今天新出锅的菜。lint-staged 就是这个“传菜清单”。

lint-staged 从字面理解是“针对暂存区文件的检查”。Git 的暂存区(staging area)保存着开发者 git add 过、准备提交的文件。lint-staged 会读取这些文件,再根据配置的文件匹配规则,只对匹配的文件运行指定的检查命令。例如:

这样,无论项目历史上有多少“黑历史”,提交时只检查本次要提交的少量文件,速度飞快,也避免了全量检查风暴。暂存文件集合可以用 Git 命令语义表达:

lint-staged 与 Husky 是天生一对:Husky 负责把门,lint-staged 负责高效地完成检查。大刘拿着清单抽查新菜,既守住了门,又不耽误出菜速度。这也是为什么现代前端脚手架(如 Vite 初始化项目)生成的默认模板里,总会同时出现 Husky 与 lint-staged 这对搭档。

S={f∣f∈git diff −−cached}S = \{ f \mid f \in \mathrm{git\ diff\ --cached} \}
公式解读:变量 S 表示本次检查的文件集合,它来源于 git diff --cached 列出的所有暂存文件,而非整个项目。
  1. git diff --cached 列出暂存文件Git 命令输出所有已加入暂存区、待提交的文件名单。
    git diff −−cached\mathrm{git\ diff\ --cached}
  2. 集合描述用集合语言声明 S 由“属于暂存文件列表的文件”组成。
    S={f∣f∈… }S = \{ f \mid f \in \dots \}
  3. 检查范围收敛lint-staged 只对 S 里的文件执行 lint 与格式化命令。
json1 行
{ "lint-staged": { "*.js": ["eslint --fix", "prettier --write"] } }

自定义 ESLint 规则

陈姐的《后厨守则》是总部发的通用版,但太阳餐厅还有自己的特色:比如“不用花生油”“小葱必须切三毫米段”。这些独家规矩总部不会替她想,她得自己写。自定义 ESLint 规则也是如此:当团队有特殊约定时,内置规则不管用,就需要自己动手写一条。

要理解自定义规则,必须先认识 AST(抽象语法树)。ESLint 在分析代码时,会把 const a = 1 拆解成一颗结构树:顶层是一个变量声明节点,下面是标识符、数字字面量等子节点。每条 ESLint 规则本质上就是一个“AST 巡逻员”——声明自己关心哪些节点类型,当遍历到该类型节点时触发回调,再决定是否报错。

一个自定义规则的结构可以概括为:

写一条规则的典型模板是导出 { meta, create } 对象:meta 描述规则的元信息(如文档、错误消息模板),create 返回一个监听 AST 节点的事件表。例如禁止在代码里写 TODO 注释的规则,就监听注释节点并检查文本内容。开发时可以先写测试用例,跑 eslint . --debug 观察 AST 结构,再逐步完善规则逻辑。陈姐的独家守则,就这样一条条写进了后厨的墙上。

rule(n) ⁣:match(n, AST)⇒report(message)\mathrm{rule}(n) \colon \mathrm{match}(n,\ \mathrm{AST}) \Rightarrow \mathrm{report}(\text{message})
公式解读:一条规则对节点 n 生效:当 AST 遍历命中 n 时,规则根据上下文判断是否需要输出一条报错信息。
  1. 规则接收节点 nESLint 遍历 AST 树时,把当前访问的节点传给规则的回调函数。
    rule(n)\mathrm{rule}(n)
  2. match 匹配节点类型规则声明关心哪些节点,如 VariableDeclarator 代表变量声明。
    match(n, AST)\mathrm{match}(n,\ \mathrm{AST})
  3. report 输出问题命中后调用 context.report 上报一条带有位置和消息的错误。
    report(message)\mathrm{report}(\text{message})

Monorepo 规范配置

太阳餐厅生意红火,陆续开了三家分店。每家分店都有自己的后厨,但店长发现:如果每家各写各的守则,总店的质量标准根本无法落地。于是中央厨房发布了一本《太阳餐厅统一守则》,各分店在此基础上只允许补充、不允许删减。Monorepo 的规范配置,就是给多包项目准备的一本“总店守则”。

在 Monorepo(单体仓库)中,多个子项目放在同一个 Git 仓库里管理。如果每个子包都复制一份 .eslintrc.json,一旦规则更新就得逐个改,苦不堪言。正确的做法是抽出一个共享配置包,比如 @sun/eslint-config,里面写好公共规则;子项目在各自的 ESLint 配置中通过 extends 继承它。这样总店改守则,分店自动生效。

共享配置的继承关系可以写成:

实践上,共享配置包通常采用“从主入口引入规则,子包用 extends 指向它”的模式。当公共规则升级时,只需修改并发布共享包,所有子包在安装依赖更新时同步获得新规范。这种“中央集权、地方自治”的配置结构,让上百个子包也依然能保持同一套代码风格,正如开遍全城的分店,端出来的都是同一道太阳招牌菜。

config=extends[common, workspace]\text{config} = \mathrm{extends} \left[ \text{common},\ \text{workspace} \right]
公式解读:子项目的最终 ESLint 配置等于继承“总店公共配置”与“本工作区私有配置”后的合并结果,公共部分被所有包复用。
  1. extends 继承机制ESLint 配置中的关键字段,声明要继承的一个或多个基础配置。
    extends\mathrm{extends}
  2. common 公共配置抽到独立 npm 包里的统一规则集合,所有子包共享。
    common\text{common}
  3. workspace 工作区配置某个子包在自己目录下追加的补充规则,只影响本包。
    workspace\text{workspace}
  4. 配置合并最终配置是公共配置与私有配置叠加后的结果。
    config=extends[… ]\text{config} = \mathrm{extends} \left[ \dots \right]

团队落地实践

守则写好了,工具装上了,钩子也挂了,但规范真正落地,还得靠每一位厨师习惯成自然。新来的厨师小张入职第一天,老王的带教方式很简单:让他提交一次代码试试。小张刚敲完 git commit 就被 Husky 弹了回来——ESLint 报了两个错,Prettier 提示三处格式不一致。他改完再提交,顺利通过。前后只花了十分钟,他已经记住了团队的全部规矩。

这就是工具链落地的意义:规范不需要靠口口相传,而是通过机械化的检查强制执行。团队落地时通常还会做三件事:第一,在 CI(持续集成)中加一步 npm run lint,让合并到主分支的代码也必须通过检查;第二,把规范沉淀进代码审查清单,审查者重点看工具查不出的逻辑与设计问题;第三,为新人准备一份“快速开始”文档,把安装、配置、常用命令一次写清。

整个团队的规范一致性,可以看作是所有成员都执行同一检查函数的结果:

一个月后,小张已经能闭着眼改完代码、提交、一气呵成。刘姐的质检、小美的摆盘、大刘的把关,早已融入他的肌肉记忆。他后来在笔记里写道:“所谓代码规范,不是束缚,而是让一群素不相识的厨师,能在同一口锅里炒出同一种味道的默契。”而这,正是工程化带给团队最宝贵的礼物。

consistency=∀d∈team, check(d)\mathrm{consistency} = \forall d \in \mathrm{team},\ \mathrm{check}(d)
公式解读:团队的一致性定义为:对团队中的每一位开发者 d,规范化检查 check(d) 都必须成立。任何一个人破坏规则,整体一致性就归零。
  1. 团队集合d 表示团队中的单个开发者,team 是全体成员集合。
    ∀d∈team\forall d \in \mathrm{team}
  2. check 检查函数代表提交前自动执行的封装流程,包含 ESLint、Prettier、Husky 全链路。
    check(d)\mathrm{check}(d)
  3. 全称量化保证\forall 表示对所有人成立,这正是“一致性”的本质含义。
    consistency\mathrm{consistency}

AI 应用场景

场景 1:AI 脚手架一键初始化项目规范

阶段:前端工程化初始化

解决的问题:新项目启动时,手动配置 ESLint、Prettier、Husky 至少需要半小时,且不同开发者配置结果五花八门。AI 可以根据团队预设模板自动生成整套规范配置,落地“后厨守则”零成本。

具体案例:某团队使用 AI 代码助手创建新前端项目,输入“采用 Vite + TypeScript,按太阳餐厅规范配置 ESLint/Prettier/Husky”,AI 自动生成 .eslintrc.json、.prettierrc、.husky/pre-commit,并安装 lint-staged,一条命令完成全部初始化。

场景 2:AI 辅助老项目规范迁移

阶段:存量代码治理

解决的问题:老项目往往没有规范,几千个文件全是“黑历史”,人工整改成本极高。借助 Prettier 全量格式化 + ESLint 自动修复,再配合 AI Review 提示风险点,可以低风险地逐步收紧规范,相当于给老后厨做一次“标准化改造”。

具体案例:一个维护三年的 React 项目需要引入规范。团队先运行 prettier --write src 全量统一格式,再用 eslint --fix 自动修复可修问题,对剩余问题按“error 阻断、warn 观察”分级处理。Husky 上岗后,新增代码全部合规,老代码逐渐被重构覆盖。

场景 3:AI 代码助手与规范检查联动

阶段:AI 生成代码的合规校验

解决的问题:AI 生成的代码往往风格各异,直接提交会污染仓库。将规范工具链接入 AI 代码生成流程,让每次 AI 产出的代码都自动通过 ESLint 与 Prettier 关卡,保证 AI 交付物与团队风格一致,就像让 AI 厨师也遵守太阳餐厅守则。

具体案例:开发者在 IDE 中让 AI 生成一个工具函数,插件自动保存后,pre-commit 钩子检测到格式不合规,一键调用 Prettier 完成格式化,再提示 "no-console" 规则被触发,开发者顺手删掉调试语句后提交通过。

Python 演示

python52 行
import re # 模拟项目中的提交文件FILES = {    "src/utils.py": "def foo(a):\n    return a\n",    "src/main.py": "x = 1000000\nprint( '提交成功' )\n",    "README.md": "# demo project\n",}MAX_LINE_LEN = 88FORBIDDEN_WORDS = ("foo", "todo")QUOTE_PATTERN = re.compile(r"'([^']*)'")  class LintError(Exception):    pass  def prettier_format(src):    src = QUOTE_PATTERN.sub(r'"\1"', src)    src = re.sub(r"[ \t]+\n", "\n", src)    return src  def eslint_check(filename, src):    for line_no, line in enumerate(src.splitlines(), 1):        if len(line) > MAX_LINE_LEN:            raise LintError("line too long: {}:{} ({}>{})".format(                filename, line_no, len(line), MAX_LINE_LEN))        for word in FORBIDDEN_WORDS:            if word in line.lower():                raise LintError("forbidden word '{}' at {}:{}".format(                    word, filename, line_no))  def husky_pre_commit(files):    for filename, source in files.items():        if not filename.endswith(".py"):            print("skip non-python: {}".format(filename))            continue        eslint_check(filename, source)        if prettier_format(source) != source:            raise LintError("please run prettier first: {}".format(filename))        print("pass: {}".format(filename))  if __name__ == "__main__":    try:        husky_pre_commit(FILES)        print("ALL PASS, commit allowed")    except LintError as err:        print("commit blocked: {}".format(err))        raise SystemExit(1)
目的:用 Python 模拟“提交前检查流水线”——对暂存文件依次执行 ESLint 风格与 Prettier 格式检查,任意一项不通过即以非零退出码拦截提交。
  1. if not filename.endswith(".py")只检查代码文件,非代码文件直接跳过,模拟 lint-staged 的基础行为
  2. eslint_check(filename, source)模拟 ESLint 扫描:检查行长度与禁用词,命中即抛异常
  3. raise LintError("line too long: ...")触发拦截机制,异常携带具体文件与行列号,方便定位
  4. QUOTE_PATTERN.sub(r'"\1"', src)用正则统一字符串引号风格,对应 Prettier 的引号配置
  5. prettier_format(source) != source格式化结果与原文件不一致就判定格式不合规,这是 Prettier 检查的常用做法
  6. raise SystemExit(1)以退出码 1 结束进程,模拟 Husky 钩子失败导致 commit 中止

常见误区

误区“ESLint 不是也能管引号和分号吗?既然它俩都能改格式,那我只装 ESLint 不就行了?”

正解两者关注点完全不同。ESLint 的核心是“查错”——未使用变量、重复导入、危险语法,格式规则只是它的附属功能;Prettier 的核心是“排版”——引号、缩进、换行,理论上就算代码写错了它也会照常格式化。就像陈姐查食材问题时顺手整理了下砧板,但你不能让她替代小美去给所有菜精雕细琢地摆盘。

为何常见:因为两边都有一些修改格式的功能,初学者容易看到“重叠”就误以为完全等价,忽视了各自真正擅长的领域。

误区“反正一个 console.log 不影响运行,git commit 照样能提上去,等有空再改吧。”

正解一旦接入了 Husky 的 pre-commit 钩子,error 级别的 lint 错误会让提交直接被中止,代码根本进不了仓库。更重要的是,lint 的很多规则(如 no-unused-vars、no-dupe-keys)预防的是运行时才爆发的隐性 Bug。大刘把关不是为了找茬,而是防止一道夹生的菜被端上桌。

为何常见:早期工作流没有钩子拦截时,lint 只是“建议”,久而久之大家养成了“可以不听”的错觉。

误区“我把 husky 添加到 devDependencies 里了,怎么提交时完全没有拦截效果?”

正解Husky 的工作原理是修改 Git 钩子配置。新版本需要先运行 npx husky init 生成 .husky/pre-commit 文件,或者执行 npm run prepare 触发安装脚本,钩子才会真正挂到 Git 上。只装包不初始化,相当于大刘只是口头答应来看门,但从没拿到钥匙。

为何常见:依赖安装通常会自动执行脚本,很多人误以为装完依赖就万事大吉,忽略了初始化钩子这个关键动作。

误区“lint-staged 就是对项目所有文件跑 eslint,项目大了肯定会很慢吧?”

正解lint-staged 的名字已经说明了它的行为——它只针对暂存区(staged)中本次要提交的文件执行检查,没有 git add 过的文件一概不碰。大刘只查今天新出锅的菜,仓库里存放了一年的旧菜他不会去翻,这正是它高效的原因。

为何常见:“lint“ 和 ”staged“ 两个词拆开都认识,但合在一起的含义——”只检查暂存区文件”——往往被人忽略。

面试题

Q1. ESLint 主要解决什么问题?

单选题 · easy

  • A.格式化代码缩进
  • B.发现代码质量问题← 正确答案
  • C.打包压缩 JS 文件
  • D.启动本地开发服务

答案:B。ESLint 的核心职责是静态分析,找出代码中的质量与风格问题。格式化缩进是 Prettier 的强项,打包压缩是构建工具(如 Webpack/Vite)的事,启动服务是开发服务器的事。正如质检员陈姐管的是食材合不合规,而不是摆盘和上菜。

Q2. ESLint 规则配置中,"error" 等级对应的数值是?

单选题 · easy

  • A.0 表示关闭
  • B.1 表示警告
  • C.2 表示错误← 正确答案
  • D.3 表示致命

答案:C。ESLint 规则等级中,0 为 off(关闭),1 为 warn(警告),2 为 error(报错),没有 3 这个等级。error 级别的错误会导致 lint 命令以非零状态退出,从而在 CI 或 Git 钩子中拦截提交。

Q3. 关于 Prettier 与 ESLint 的分工,下列说法正确的是?

单选题 · mid

  • A.两者都可发现语法错误
  • B.Prettier 专注风格美化← 正确答案
  • C.ESLint 负责自动排版
  • D.两者职责完全相同

答案:B。Prettier 只做代码风格的统一格式化,不关心逻辑与潜在错误;ESLint 的主要职责是发现代码静态问题(未使用变量、危险 API 等),格式只是附带。小美负责摆盘,陈姐负责查食材,各司其职,二者互补。

Q4. Husky 的 pre-commit 钩子在什么时机触发?

单选题 · mid

  • A.执行 commit 时← 正确答案
  • B.执行 push 时
  • C.执行 merge 时
  • D.执行 add 时

答案:A。pre-commit 钩子由 git commit 命令触发,在提交写入历史之前执行检查;push 对应 pre-push,merge 对应 pre-merge-commit,add 不触发任何 Git 钩子。这就好比大刘守在厨房门口,只在菜要端出去的瞬间拦截。

Q5. lint-staged 默认检查的对象是?

单选题 · mid

  • A.项目里的全部文件
  • B.暂存区里的待提交文件← 正确答案
  • C.最近一次提交的文件
  • D.当前未跟踪的新文件

答案:B。lint-staged 的名字来自 “staged“——它只读取暂存区中本次 git add 过的文件,再按匹配规则执行检查命令。全量检查会导致仓库越大越慢,lint-staged 的精髓就是”只看新菜,不翻旧账”。

Q6. 自定义 ESLint 规则主要通过分析什么结构实现?

单选题 · mid

  • A.正则表达式匹配源码
  • B.抽象语法树 AST 结构← 正确答案
  • C.对源码字符串精确查找
  • D.分析编译后的二进制文件

答案:B。ESLint 在运行时先把源码解析成 AST(抽象语法树),每条规则通过监听特定节点类型来判断并报告问题。正则与字符串查找无法准确理解代码的嵌套语义,AST 才能让规则精准定位“哪一行、哪个节点”出了问题。

Q7. Monorepo 中共享 ESLint 配置最推荐的方式是?

单选题 · extreme

  • A.各包复制一份配置
  • B.发布共享配置并用 extends← 正确答案
  • C.给每个包单独安装 ESLint
  • D.全局安装并跳过配置

答案:B。Monorepo 的最佳实践是把公共规则抽成独立配置包,子包通过 extends 继承。复制配置会导致规则更新时改 N 份文件;单包安装只是环境问题,仍无法解决规则统一。公共配置包就像总店守则,分店只继承不复制。

Q8. 下列哪些工具属于前端代码规范工具链?

多选题 · mid

  • A.ESLint 检查代码问题← 正确答案
  • B.Prettier 统一代码风格← 正确答案
  • C.Husky 绑定 Git 钩子← 正确答案
  • D.Webpack 打包静态资源

答案:ABC。ESLint、Prettier、Husky 正是“规范三板斧”,分别负责检查、格式化和提交拦截;Webpack 是模块打包构建工具,解决的是资源编译与打包问题,不属于代码规范范畴,相当于传菜卡车而不是守门员。

Q9. 下列哪些时机适合用 Husky 钩子?

多选题 · mid

  • A.pre-commit 执行 lint← 正确答案
  • B.commit-msg 校验提交信息← 正确答案
  • C.pre-push 执行测试← 正确答案
  • D.dev-server 支持热更新

答案:ABC。Git 钩子可以在提交前(pre-commit)、提交信息写入前(commit-msg)、推送前(pre-push)介入并执行脚本。dev-server 热更新是本地开发服务器功能,走的是文件监听与 WebSocket,与 Git 钩子无关。

Q10. 关于 ESLint 配置,下列说法正确的有?

多选题 · hard

  • A.配置可通过 extends 继承← 正确答案
  • B.规则值可为 off/warn/error← 正确答案
  • C.配置文件只能叫 .eslintrc
  • D.error 级别会阻止 CI 通过← 正确答案

答案:ABD。extends 是配置继承的核心机制;规则等级就是 off/warn/error 三档;error 级别让命令以非零退出码结束,CI 因此失败。配置文件除 .eslintrc 外还支持 .eslintrc.js、eslint.config.js 等多种形式,C 的说法以偏概全。