当前位置: 首页 > news >正文

代码规范体系(EditorConfig、ESLint、Prettier、Husky)

代码规范体系(EditorConfig、ESLint、Prettier、Husky)

EditorConfig

属于编辑器层,在最底层统一编辑器行为,解决跨编辑器的格式差异。当你用 VS Code 打开一个文件,缩进是空格还是 Tab、宽度是2还是4,这些基础行为由 EditorConfig 说了算。EditorConfig 是一个跨编辑器的代码风格配置文件。它的设计理念是:无论你用 VS Code、IntelliJ IDEA 还是 Vim,打开同一个项目时,基础编辑风格都自动保持一致。这个文件被大多数主流编辑器(VS Code 需要安装插件)和 IDE 原生支持。

ESLint + TSLint

属于代码质量层,检测代码质量和类型规范,发现 bug 风格的写法。比如使用了 `==` 而不是 `===`、使用了被废弃的 API、变量未声明就使用、import 语句没有排序等。

Prettier

属于格式统一层,强制统一代码风格,消除团队内对格式的争议。缩进用空格还是 Tab、引号用单还是双、行尾要不要逗号——这些视觉层面的问题由 Prettier 说了算。

Husky + lint-staged + 自定义脚本

属于提交管控层,在代码进入仓库前执行全面检查,阻断不符合规范的提交,确保仓库代码质量始终如一。

项目配置的优先级对比

1.Git hooks(Husky)。这是最后一道防线,优先级最高。即使你的编辑器配置完美,git commit 时如果 ESLint 报 Error,整个提交仍然会被阻断。这一层的意义是:强制约束,不能绕过。

2.项目配置文件(.eslintrc.js、tslint.json、.prettierrc、tsconfig.json)。这些文件在特定工具运行时生效,覆盖编辑器的格式化行为。比如你的 VS Code 设置了 `tabSize: 4`,但 `.prettierrc` 配置了 `tabWidth: 2`,那么 Prettier 格式化时输出的仍然是 2 空格。触发方式可以选择格式化文档(选使用prettier)或者.vscode>setting.json中配置"editor.formatOnSave": true 保存代码自动格式化

3.EditorConfig + VS Code 用户设置。安装了 EditorConfig 插件后,项目的 `.editorconfig` 会覆盖 VS Code 的默认设置,但不会覆盖你手动在 VS Code settings.json 中配置的同名项。

4.VS Code 默认设置。当没有任何配置文件时,VS Code 使用自己的默认行为(通常 tab 宽度 4、空格缩进等)。

EditorConfig 与 VSCode

代码规范的生效,始于你打开一个文件的那一刻。在这一刻,决定代码"长什么样"的设置来自两个层面:VSCode 原生设置(settings.json)和 EditorConfig(.editorconfig)

VSCode 提供了两套配置体系:UI 界面配置和 JSON 文件配置。UI 配置就是通过 Cmd + , 打开的设置面板,点一点滑块和勾选框就能改。这种方式直观但无法批量共享给团队。JSON 配置则是通过"打开设置 JSON"入口直接编辑 settings.json 文件,好处是可以提交到 Git,团队成员 clone 后直接使用。

注意:VS Code 不会自动读取 .editorconfig 文件,须安装 EditorConfig插件才能生效。很多人因为没有安装插件,误以为项目的 .editorconfig 配置有问题。

那么,既然 VS Code settings.json 也能配置这些选项,为什么还要用 .editorconfig?

.editorconfig 与编辑器无关。如果团队中有人用 WebStorm、有人用 VS Code、有人用 Vim,通过 .editorconfig 就能统一管理,不需要每个人都去改自己的编辑器设置;

.editorconfig 支持目录层级的差异化配置。你可以在根目录设置全局规则,同时在子目录中用不同的 .editorconfig 覆盖父级规则,这种继承和覆盖机制是 settings.json 不支持的

VSCode配置与EditorConfig 的关系

VSCode 不会自动读取和应用 .editorconfig配置,须安装EditorConfig插件才会生效。

安装 EditorConfig 插件后,项目的 .editorconfig 会覆盖 VSCode默认设置和用户设置

团队成员不需要手动调整 VS Code 设置,只要 clone 项目、安装插件、规范就自动生效

.editorconfig 配置(取自实际项目中使用)

root = true

[*]

indent_style = space

indent_size = 2

end_of_line = lf

charset = utf-8

trim_trailing_whitespace = true

insert_final_newline = true

root = true 表示这是最顶层的配置,EditorConfig 不会继续向上级目录查找配置。所有文件(`[*]` 表示匹配所有文件类型)统一使用 2 空格缩进、LF 换行符、UTF-8 编码

settings.json 配置

{

"eslint.validate": [

"javascript",

"javascriptreact",

"typescriptreact",

"typescript"

],

"typescript.tsdk": "node_modules/typescript/lib"

}

配置说明

settings.json.editorconfig(INI 格式)解释
editor.insertSpaces:trueindent_style控制按 Tab 键是插入空格,不是是制表符
editor.tabSizeindent_size控制缩进的宽度
editor.trimTrailingWhitespace:truetrim_trailing_whitespace保存文件会自动删除每行末尾的多余空格

editor.insertFinalNewline: true

insert_final_newline

保存文件自动在文件末尾确保有一个空行( POSIX 标准)

files.eol // 控制换行符样式。`\n` 表示 LF(Unix/macOS 标准),`\r\n` 表示 CRLF(Windows 标准)。团队必须统一,否则同一个文件在不同人的机器上换行符不同,git diff 会变成全文件变更。 项目统一要求 LF

files.encodingcharset设置默认文件编码为 utf8,保持跨平台一致性。

ESLint(Parser、Plugin、Config、extends)

ESLint 是一个代码质量检测工具,它会在你写代码时或提交代码前,检查代码是否符合预设的规则。ESLint 的检查范围很广,既包括代码格式问题(比如缩进、引号),也包括代码质量问题(比如使用了 `==` 而不是 `===`、使用了被废弃的 API、变量未声明就使用等)。

ESLint 的工作方式可以类比为老师批改作业。老师手里有一份"评分标准"(规则集),学生交作业(代码)后,老师对照标准逐条检查,发现问题就打叉并标注原因。ESLint 就是这个"老师",规则集就是 ESLint 的配置文件。

ESLint 在项目中运行时机

开发时(实时检测),在使用 VS Code 编写代码时,编辑器实时调用 ESLint 对当前文件进行检测,发现问题会在代码中直接标出红色或黄色下划线,让你编码的同时就知道哪里有问题;

提交前(git pre-commit hook),当执行 git commit时,Husky 触发 pre-commit hook,lint-staged 只对暂存的代码文件运行 ESLint 检测。如果检测到 Error 级别的问题,提交被阻断

ESLint 由四个关键组件构成,理解它们是掌握 ESLint 的基础:

Parser(解析器):ESLint 本身只能处理标准的 JavaScript 代码,但现代前端项目通常使用 TypeScript、Vue 的 SFC 等非标准语法。Parser 的作用是把这些非标准代码翻译成 ESLint 能理解的"抽象语法树"(AST)。常见的Parser 是 `@typescript-eslint/parser`,它专门负责把 TypeScript 代码解析成 ESLint 能处理的格式。

Plugin(插件):ESLint 本身只包含最基础的规则。Plugin 是规则的扩展包,每个插件都包含一组针对特定场景的规则。比如项目使用了两个插件:`@typescript-eslint` 插件提供了 TypeScript 相关的规则,`@mtfe/video-base` 插件则是团队自研的规则包,包含该团队特有的规范要求。

Config(配置/规则):插件提供的是规则"素材库",配置从这个素材库中选择启用哪些规则、每个规则的严格程度如何。ESLint规则有三种级别:off(不检查)、warn(标出但不阻断)、error(标出且阻断提交)。

extends(继承):在团队项目中,通常会有一份公司级或团队级的 ESLint 配置作为基础,项目在此之上进行个性化调整。本项目通过 extends 继承了三个配置:`plugin:@mrn/eslint-plugin/recommended` 来自 MRN 平台团队,`plugin:@mtfe/mtvideo-base/recommended` 来自美团短视频团队,`prettier` 来自 eslint-config-prettier 用来关闭与 Prettier 冲突的规则。

.eslintrc.js(ESLint配置文件)

{

root: true, // 表示这是项目的根配置文件,ESLint 不会向上级目录寻找

parser: '@typescript-eslint/parser', // parser指定使用什么工具来解析代码,如果项目用TypeScript,必须用它对应的解析器

plugins: ['@typescript-eslint', '@mtfe/mtvideo-base'], // plugins声明要加载哪些规则插件

extends: [ // extends:继承已有的配置(按顺序加载,后面的会覆盖前面的)

'plugin:@mrn/eslint-plugin/recommended',

'plugin:@mtfe/mtvideo-base/recommended',

'prettier', // prettier 配置,用于关闭与 Prettier 冲突的 ESLint 规则

],

// rules:在继承的基础上,自定义调整规则

rules: {

'no-restricted-properties': [ //这是一个ERROR 规则,会阻断提交(禁止使用JSON.parse)

'error',

{

object: 'JSON',

property: 'parse',

message: '请使用 @mrn/mtvideo-base-utils的jsonParseSafe代替JSON.parse。直接使用 JSON.parse可能抛错导致崩溃或异常行为',

},

],

'no-eq-null': 'off', // 关闭 no-eq-null 检查,因为 TypeScript 的严格模式已经覆盖了这个场景

'react/prop-types': 'off', // React 的 prop-types 在 TypeScript 下不需要

'@typescript-eslint/member-ordering': 'off', // 关闭成员排序(因为业务代码的成员顺序可能需要按功能组织)

'simple-import-sort/imports': 'error', // import 语句按字母顺序排序

'@typescript-eslint/prefer-readonly': 'error', //prefer-readonly:类成员能用readonly就用readonly

// 以下规则从 error 降级为 warn(代码中有遗留场景,短期内无法全部修复)

'react/no-access-state-in-setstate': 'warn',

'no-nested-ternary': 'warn',

'react/jsx-key': 'warn',

'no-unsafe-finally': 'warn',

'@typescript-eslint/consistent-type-assertions': [

'warn',

{

// 使用 as 断言,不用 <Type> 断言语法

assertionStyle: 'as',

// 禁止对象字面量的类型断言

objectLiteralTypeAssertions: 'never',

},

],

'no-shadow': 'off', //关闭 shadow规则(与 TS 的类型系统可能有冲突)

},

overrides: [// overrides:对特定文件做不同的配置(优先级高于外层 rules)

// *.d.ts 类型声明文件,关闭未使用变量检查

{

files: ['*.d.ts'],

rules: {

'no-unused-vars': 'off',

'@typescript-eslint/no-unused-vars': 'off',

},

},

// *.js 文件不需要 TypeScript 类型检查

{

files: ['*.js'],

parserOptions: {

project: null,

},

extends: ['plugin:@mtfe/mtvideo-base/disableTs'],

},

],

// ignorePatterns:告诉 ESLint 忽略这些文件和目录

ignorePatterns: [

'node_modules/**/*',

'dist/**/*',

'**/Models/**/*', // 自动生成的代码

'cli/**/*', // 命令行脚本

'plugin/**/*',

'src/**/*', // src 目录由 TSLint 负责

],

globals: { // globals:在代码中可以使用的全局变量(不会报未定义错误)

__filename: 'readonly',

__BUILD_TIME__: 'readonly',

__CODE_LINE__: 'readonly',

__dirname: 'readonly',

__jsiExecutorDescription: 'readonly',

},

}

Prettier

Prettier 是一个代码格式化工具(Formatter),它的职责与 ESLint 有部分重叠但定位不同。ESLint 更偏向"代码质量",关注的是代码是否正确、是否安全、是否有潜在bug。Prettier 更偏向"代码风格",关注的是缩进是 tab 还是空格、字符串用单引号还是双引号、行尾是否加逗号等视觉层面的统一。

Prettier 的设计哲学是"做一个固执己见的代码格式化工具"。它只提供少量配置项,强制团队接受统一的格式化风格,不留讨论余地。你不需要纠结"我们团队应该用单引号还是双引号",Prettier 说用单引号就都用单引号,不需要 code review 时因为格式问题争论不休。

为什么ESLint和Prettier需要配合使用

ESLint 本身也有一部分格式化规则(比如 indent、quotes、semi 等),这些规则和 Prettier 的功能是重叠的。如果两个工具的规则不一致(比如 ESLint 要求加分号,Prettier 要求不加分号),那就会产生冲突——ESLint 检测到不加分号会报错,Prettier 格式化后恰好不加分号,然后 ESLint 又报错,形成死循环。

解决方案是使用 `eslint-config-prettier` 这个包,它的作用是关闭ESLint中所有与 Prettier 冲突的格式化规则,让ESLint专注于代码质量检查,Prettier 专注于代码格式化。各司其职,互不干扰。

Prettier配置(.prettierrc)

{

"semi": true,

"singleQuote": true,

"arrowParens": "always",

"trailingComma": "all",

"preferConst": true,

"tabWidth": 2

}

trailingComma: "all"` 表示在 ES5 合法的地方都加尾逗号。这个配置在现代前端项目中非常常见,因为它可以让 git diff 更加清晰——如果一行只添加了一个字段但原行没有逗号,git diff 只会显示新增的那一行,而如果原行末尾没有逗号,则会显示"修改了原行末尾并新增了一行",导致 diff 变得冗长。

arrowParens: "always" 表示箭头函数的参数无论有多少个都加括号。比如 `(x) => x + 1` 而不是 `x => x + 1`。这样做的好处是代码风格统一,减少视觉干扰。

preferConst的实际执行由 ESLint 的prefer-const规则负责,而非 Prettier。

Husky 与 Git Hooks

在理解 Husky 之前,需要先理解 Git Hooks 是什么。Git 允许你在特定的时机自动执行自定义脚本,这些时机包括"提交之前"、"提交之后"、"推送之前"、"推送之后"等。Git Hooks 就是这些"时机点"上挂载的自定义脚本。

Git 自带了一些默认的 Hooks 脚本模板,位于 `.git/hooks/` 目录下,比如 `pre-commit.sample`、`commit-msg.sample`、`post-commit.sample` 等。这些文件默认是禁用的(因为带 `.sample` 后缀),只需要把 `.sample` 后缀去掉,Git 就会在对应时机执行这些脚本。

Husky 的作用是通过 package.json 配置 Git Hooks,而不需要手动编辑 `.git/hooks/` 目录下的脚本文件。

在没有 Husky 的时代,如果你想实现"每次 git commit 前运行 ESLint",你需要手动编辑 `.git/hooks/pre-commit` 文件,写入 shell 脚本。这有几个问题:这些脚本文件通常不会被 git 跟踪,团队成员每次 clone 项目后都需要手动配置;Windows 系统的 shell 脚本语法不同,兼容性差;不同项目可能有不同的 Hooks 配置,不方便管理。

Husky 通过在package.json中声明式地配置 Git Hooks 解决了这些问题。当运行 npx husky install 时,Husky 会自动在 `.git/hooks/` 目录下生成对应的 hook 脚本,并将团队的配置存储在 `.husky/` 目录下(可以被 git 跟踪)。团队其他成员 clone 项目后运行 `yarn`(依赖 postinstall 脚本)会自动安装 Husky Hooks。

package.jsonz中配置husky

"husky": {

"hooks": {

"pre-commit": "yarn lint-staged && node ./cli/check.js",

"post-merge": "yarn"

}

}

当开发者执行 `git commit` 时,Git 检测到 pre-commit hook 存在并执行它。Husky 生成的 pre-commit 脚本运行 `yarn lint-staged && node ./cli/check.js`。lint-staged 找出本次 commit 涉及的源代码文件并对每个文件执行 ESLint 检测和自动修复。如果有任何 Error 级别的问题,lint-staged 以非零状态码退出,git commit 被阻断。如果所有检查通过,git commit 继续执行。

post-merge: yarn 表示当执行 `git pull` 或 `git merge` 合并别人的代码后,Git 自动运行 `yarn` 来安装可能发生变化的依赖。这个 hook 非常重要,因为如果其他人修改了 `package.json`,你需要立即安装新的依赖,否则项目可能因为依赖不一致而无法运行。

lint-staged的作用就是只对本次 commit 涉及的(即 staged 的)文件运行 ESLint,而不是全量检查整个项目。这大大提高了检查速度,也避免了"别人的代码有问题但你被迫无法提交"的情况。

package.json中配置lint-staged

"lint-staged": {

"Reward/**/*.{tsx,ts,jsx,js}": "eslint --quiet",

"src/Message/Publish/**/*.{tsx,ts}": "eslint --quiet",

"src/Components/VideoUgcCreation/**/*.{tsx,ts}": "eslint --quiet",

"src/TagAggregation/**/*.{tsx,ts}": "eslint --quiet",

"src/Message/NewFocus/**/*.{tsx,ts}": "eslint --quiet",

"src/**/*.{tsx,ts}": "eslint --quiet",

"Models/**/*.{js,ts,tsx}": "node cli/check_model.js"

}

配置格式是"glob 模式: 命令"。每行表示匹配该模式的 staged 文件将执行对应的命令。例如,`src/**/*.tsx` 表示 `src` 目录下所有 `.tsx` 文件执行 `eslint --quiet` 检测。`--quiet` 参数表示只显示 Error 级别的问题,不显示 Warning,这样可以减少噪音同时确保严重的格式问题不被放过。

ESLint和TSLint

部分项目中同时存在 ESLint(`.eslintrc.js`)和 TSLint(`tslint.json`)两套工具。这是因为 TSLint 是 TypeScript 官方曾经推荐的检测工具,后来 TSLint 团队宣布停止维护并推荐用户迁移到 ESLint 的 `@typescript-eslint` 插件。美团短视频团队在迁移过程中采用了"双轨制"过渡策略:TSLint 继续承担对 src 目录的代码检测,ESLint 逐步接管更多场景。从 gamevideo 项目的配置中可以看到,ESLint 的 `ignorePatterns` 里排除了 `src/**/*`,表示 src 目录暂时由 TSLint 负责。

TSLint(tslint.json)配置

{

"extends": ["@hfe/mrn-tslint"],

"linterOptions": {

"exclude": [

"node_modules/**",

"Models/**",

"TSApis/**",

"src/Message/Publish/**",

"src/Components/VideoUgcCreation/**",

"src/TagAggregation/**",

"src/Message/NewFocus/**",

"**/*.bak"

]

},

"rules": {

"semicolon": [true, "always", "ignore-bound-class-methods"],

"quotemark": [true, "single", "jsx-double"],

"no-var-keyword": true,

"prefer-const": true,

"render-with-short-circuit-calculations": false,

"no-channel-in-params": false,

"disallow-null-header": false,

"test-style": false,

"render-function-no-setstate": false,

"endup-with-separator": false,

"no-arrowfunction-in-renderfunction": false,

"avoid-onpress-in-view": false,

"ter-indent": false

}

}

自定义检查脚本 cli/check.js

cli/check.js 不是 ESLint 本身,而是一个Node.js 脚本,串联了多个专项检查工具。当执行 `git commit` 时,这个脚本串行执行以下检查:

tsconfig.json(TypeScript 类型检查配置)

{

"compilerOptions": {

"allowJs": false,

"checkJs": false,

"declaration": true,

"target": "esnext",

"module": "esnext",

"sourceMap": true,

"experimentalDecorators": true,

"jsx": "react-native",

"allowSyntheticDefaultImports": true,

"moduleResolution": "node",

"strict": true,

"skipLibCheck": true,

"noEmit": true,

"baseUrl": ".",

"paths": {

"@assets/*": ["src/assets/*"],

"@src/*": ["src/*"],

"@reward/*": ["Reward/*"]

}

},

"include": [

"types/**/*",

"src/**/*.ts",

"src/**/*.tsx",

"Models/**/*.tsx",

"Reward/**/*.ts",

"Reward/**/*.tsx",

"lib/**/*.ts",

"lib/**/*.tsx",

"declarations.d.ts"

]

}

"strict": true 开启了 TypeScript 的所有严格类型检查,相当于同时设置了 `strictNullChecks`、`strictFunctionTypes`、`noImplicitAny` 等多个检查开关,会大幅提升类型安全性。"skipLibCheck": true 跳过对 `node_modules` 中 `.d.ts` 类型声明文件的检查,可以显著加快类型检查速度。

"noEmit": true 表示只做类型检查,不输出任何文件,因为构建产物由专门的构建工具(metro bundler)处理。

"paths" 配置了路径别名,`@assets/*` 会被解析为 `src/assets/*`,这样就可以用 `import img from '@assets/images/logo.png'` 代替相对路径。

优先级冲突的常见场景:

最常见的冲突场景是 VS Code 格式化与 Prettier 格式化结果不一致。原因通常是 VS Code 的 formatter 设置的不是 Prettier,或者没有安装 Prettier 插件。解决方案是确保 VS Code 配置了 `"editor.defaultFormatter": "esbenp.prettier-vscode"` 并安装 Prettier 插件。

ESLint 检测报错但编辑器没有标红。这通常是因为没有安装 ESLint VS Code 插件,或者 ESLint 插件没有正常工作。可以尝试在 VS Code 中按 `Cmd+Shift+P`,输入 "ESLint: Restart ESLint Server",然后回车重启 ESLint 服务。

第三个常见场景是 git commit 被阻断但本地检测没有报错。可能是因为 lint-staged 只检测 staged 的文件,而完整的 check.js 检测的是整个项目。可以手动运行 `node ./cli/check.js` 查看具体是什么检查失败了。

常用命令参考

yarn lint // 运行 ESLint 检测

yarn fix // 自动修复可修复的 ESLint 问题

yarn cacheClean // 清理缓存

http://www.jsqmd.com/news/1322225/

相关文章:

  • Linux 文本处理常用命令
  • 知识产权代理正规机构怎么选?2026年沈阳知识产权代理正规机构实用排行汇总参考 - 互联网科技品牌测评
  • ArkScript宏编程入门:解锁代码即数据的强大魔力
  • Sunshine游戏串流完整教程:打造你的私人游戏云实战指南
  • AI编程助手选型决策树(附GitHub星标TOP12实测数据):PyCharm/VS Code/Vim开发者该信谁?
  • JGraphX布局算法全解析:自动节点定位与边路由终极指南
  • Ubuntu24.04.2交叉编译(Arm GNU Toolchain 15.3.rel1)环境配置 — 介绍永久生效和临时生效两种方法
  • Unity高性能角色控制器:从原理到自研实现与优化
  • Unity XR开发:手动初始化XR子系统解决黑屏与启动优化
  • 5个实用技巧彻底解锁Wand专业版:告别时间限制的游戏增强方案
  • StartupOS依赖注入:Dagger框架在Java项目中的简单应用教程
  • Unity游戏AI开发:基于NavMeshAgent与状态机实现敌人巡逻与追踪系统
  • Unity资源管理进阶:AssetUsageDetector与Addressables集成实战
  • Python Pygame实战:从零打造粒子系统实现跨年烟花秀
  • 道德经道影书斋注释版 053|大道甚夷
  • REFramework终极指南:5分钟掌握RE引擎游戏模组开发
  • SitemapGenerator深度解析:现代Ruby网站地图架构的技术实现与性能优化
  • Geneva Python API实战:在应用中集成审查规避功能的完整指南
  • 2026年最新教程:视频怎么转成MP3 亲测可用的免费方法 - 效率工具研究所
  • 浅识 后端 系统
  • 2026中山太阳能地埋灯厂家盘点 口碑好、性价比高的源头厂商 - 品牌深度评测
  • 深度解析SeleniumBase:构建企业级Web自动化与反检测解决方案的实战指南
  • 10大高质量HDR环境贴图资源站深度评测与应用指南
  • Selenium自动化测试:webdriver-manager实现浏览器驱动自动管理
  • 告别鼠标!用Spectacle打造专属Mac触摸栏窗口控制中心
  • 终极指南:如何在5分钟内用urdf-viz实现机器人URDF文件可视化
  • 武汉复读学校排名 - 湖北就要学教育
  • 上海古法金、金条回收参考,正规门店不扣工艺磨损溢价 - 日常比对手册
  • CSDN博客下载器:3种模式帮你永久保存技术文章到本地
  • 3个理由为什么CatVTON正在重塑虚拟试衣的游戏规则