JavaScript 代码格式化:工具与规范
随便打开一个 Pull Request,如果一半的差异都是空白字符的改动,你立刻就能感受到那种痛苦。格式不一致会浪费时间、干扰代码审查,还会制造出与业务逻辑毫无关系的合并冲突。解决办法其实很简单:选定一套格式化标准,用工具自动执行,然后再也不用操心这件事。
本指南将带你了解那些让 JavaScript 格式化不再成为问题的工具和规范。
为什么统一格式化至关重要
格式化不是审美问题——而是关乎团队效率。当每个开发者使用不同的缩进、大括号位置和行宽时,三个问题接踵而来:
代码审查变慢。 审查者需要耗费精力去解读不熟悉的格式,而不是专注于评估逻辑。一个与代码库中其他部分风格迥异的函数,会因为错误的原因吸引注意力。
合并冲突成倍增加。 开发者 A 用制表符重新格式化了文件,开发者 B 用的是空格,结果 Git 显示每一行都被修改了。真正那三行 bug 修复反而被淹没了。
认知负担加重。 在不同风格的文件之间切换,迫使你的大脑不断重新解析代码结构。统一的格式化让你读代码就像读文章一样流畅——你的目光自然知道该看哪里。
解决方案不是争论哪种风格最好,而是选定一种并将其自动化。
主流 JavaScript 风格指南
在选择工具之前,了解塑造了 JavaScript 格式化惯例的几大主流风格指南会有所帮助。
Airbnb 风格指南
这是使用最广泛的 JavaScript 风格指南。它强制使用分号,偏好单引号,要求多行结构中使用尾逗号,并采用两个空格缩进。对应的 ESLint 配置(eslint-config-airbnb)捆绑了数百条涵盖格式和代码质量的规则。
// Airbnb style
const getUserData = (userId) => {
const user = findUser(userId);
return {
name: user.name,
email: user.email,
};
};
Standard 风格
在分号问题上采取了完全相反的立场——永远不使用分号。同样使用两个空格缩进和单引号。standard 包自带检查器,因此不需要单独的 ESLint 配置。
// Standard style
const getUserData = (userId) => {
const user = findUser(userId)
return {
name: user.name,
email: user.email
}
}
Google 风格指南
与 Airbnb 类似,但在 JSDoc 要求和命名约定上有所不同。同样使用两个空格缩进,要求分号,偏好单引号。
这些风格指南各有取舍。重要的是保持一致性,而不是纠结于选哪一个。而且越来越多的团队干脆跳过这场争论,把决定权交给 Prettier。
Prettier:固执己见的格式化工具
Prettier 通过消除大部分格式化决策,彻底改变了 JavaScript 的格式化方式。它将代码解析为 AST(抽象语法树),然后按照自己的规则重新输出。你不用逐条配置格式化选项——只需设定几个高层参数,其余的交给 Prettier 处理。
安装
npm install --save-dev prettier
创建 .prettierrc 配置文件:
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always"
}
关键配置项
printWidth(默认值:80) — Prettier 换行的行宽阈值。这不是硬性限制——Prettier 将其作为判断何时将表达式拆成多行的参考依据。对于使用宽屏显示器的团队,设为 100 或 120 很常见。
semi(默认值:true) — 是否在语句末尾添加分号。如果你偏好 Standard 风格,设为 false。
singleQuote(默认值:false) — 使用单引号代替双引号。大多数 JavaScript 开发者偏好单引号,因此 true 是更常见的设置。
trailingComma(默认值:"all") — 在所有允许的地方添加尾逗号。这能让 Git 差异更干净,因为在列表末尾添加新项只会显示一行变动,而不是两行。
// Without trailing commas — adding "d" changes two lines
const items = [
- "a",
- "b",
- "c"
+ "a",
+ "b",
+ "c",
+ "d"
];
// With trailing commas — adding "d" changes one line
const items = [
"a",
"b",
"c",
+ "d",
];
arrowParens(默认值:"always") — 是否给单参数箭头函数的参数加括号。"always" 表示写成 (x) => x,"avoid" 表示写成 x => x。
运行 Prettier
# Format all files
npx prettier --write .
# Check without modifying
npx prettier --check .
# Format specific file types
npx prettier --write "src/**/*.{js,jsx,ts,tsx}"
添加 .prettierignore 文件跳过生成的文件:
node_modules
dist
build
coverage
*.min.js
如果你需要处理压缩版 JavaScript,我们的 Code Minifier 可以正确地进行代码压缩——千万不要手动编辑压缩后的代码。
ESLint:超越格式化
ESLint 的用途和 Prettier 不同。Prettier 管的是代码的外观,ESLint 捕捉的是代码的行为。ESLint 的规则分为两类:
格式化规则 — 缩进、空格、大括号位置。这些与 Prettier 重叠,在同时使用两个工具时通常应该禁用。
代码质量规则 — 未使用的变量、不可达代码、隐式类型转换、缺少错误处理。这些才是 ESLint 真正大放异彩的地方。
推荐配置
npm install --save-dev eslint @eslint/js
一个最简化的 eslint.config.js:
import js from '@eslint/js';
export default [
js.configs.recommended,
{
rules: {
'no-unused-vars': 'warn',
'no-console': 'warn',
'eqeqeq': 'error',
'no-implicit-coercion': 'error',
},
},
];
对于 TypeScript 项目,添加 typescript-eslint:
npm install --save-dev typescript-eslint
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
);
Prettier + ESLint 集成
如果两个工具互不协调地同时运行,就会产生冲突——ESLint 的格式化规则会和 Prettier 的输出打架。解决方案是使用 eslint-config-prettier,它会禁用所有与 Prettier 冲突的 ESLint 规则。
npm install --save-dev eslint-config-prettier
import js from '@eslint/js';
import prettierConfig from 'eslint-config-prettier';
export default [
js.configs.recommended,
prettierConfig,
{
rules: {
// Only code quality rules here
'no-unused-vars': 'warn',
'eqeqeq': 'error',
},
},
];
通过这种配置,Prettier 负责格式化,ESLint 负责代码质量。没有冲突,没有混乱。
EditorConfig:跨编辑器一致性
团队成员不一定都使用同一个编辑器。EditorConfig 提供了一套适用于 VS Code、WebStorm、Vim、Sublime Text 及大多数其他编辑器的基础配置。
在项目根目录创建 .editorconfig 文件:
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
EditorConfig 处理基础工作——缩进、行尾符、尾部空格——在 Prettier 运行之前就解决了。这能避免一个常见问题:Windows 开发者往基于 Unix 的项目中引入 \r\n 行尾符。
使用 Husky 和 lint-staged 配置 Git 钩子
自动化格式化只有在真正执行的时候才有用。指望开发者每次提交前都记得运行 npx prettier --write,根本不可持续。Git 钩子能解决这个问题。
安装配置
npm install --save-dev husky lint-staged
npx husky init
在 package.json 中添加:
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix"
],
"*.{json,md,css}": [
"prettier --write"
]
}
}
更新 Husky 的 pre-commit 钩子(.husky/pre-commit):
npx lint-staged
现在每次提交都会自动格式化暂存文件并运行 ESLint 修复。开发者再也不用操心格式化——一切自动完成。
这就是团队项目的黄金标准:Prettier 负责格式化,ESLint 捕获 bug,Husky 在每次提交时强制执行。
分号之争
任何关于 JavaScript 格式化的讨论都绕不开分号问题。务实的答案是:无所谓,只要团队保持一致就行。
支持分号: 显式的语句终止符可以避免 ASI(自动分号插入)的边界情况。Airbnb 和 Google 风格指南都要求使用分号。
反对分号: JavaScript 的 ASI 机制会自动处理语句终止。不写分号的代码更简洁,视觉噪音更少。Standard 风格指南省略分号。
人们常警告的 ASI 陷阱——比如以 ( 或 [ 开头的行——都会被 ESLint 的 no-unexpected-multiline 规则捕获。实践中,在有合适的代码检查工具配合下,不写分号是完全安全的。
选定一种做法,配置好 Prettier 的 semi 选项,然后放下这个话题。无论选哪种,Prettier 都会统一处理。
JavaScript 中的制表符与空格之争
JavaScript 生态系统绝大多数使用空格——具体来说是两个空格的缩进。这不是放之四海而皆准的惯例(Go 使用制表符,Python 使用四个空格),但在 JavaScript 中这是主流共识。
为什么用两个空格? JavaScript 中嵌套回调和链式方法调用会很快产生深层缩进。两个空格的缩进能在保持可读性的同时,避免代码被推到屏幕右边。
为什么不用制表符? 制表符允许每个开发者设置自己的显示宽度,听起来不错,但这意味着代码对齐效果会因编辑器不同而不同。使用空格的话,你看到的就是所有人看到的。
在 Prettier(tabWidth: 2, useTabs: false)和 EditorConfig(indent_style = space, indent_size = 2)中各配置一次,问题就解决了。
常见格式化陷阱
不一致的箭头函数
// Inconsistent — avoid this
const double = x => x * 2;
const add = (a, b) => a + b;
const greet = (name) => {
return `Hello, ${name}`;
};
// Consistent — Prettier with arrowParens: "always"
const double = (x) => x * 2;
const add = (a, b) => a + b;
const greet = (name) => {
return `Hello, ${name}`;
};
对象解构风格
// Cramped — hard to read
const {name,email,role} = user;
// Formatted — Prettier handles this automatically
const { name, email, role } = user;
// Multiline for many properties
const {
name,
email,
role,
department,
startDate,
} = user;
Import 语句组织
Prettier 不会排序 import——那属于代码质量范畴,而非格式化。可以使用 eslint-plugin-import 或 simple-import-sort 插件:
// Organized imports
import React from 'react';
import { useState, useEffect } from 'react';
import { Button } from '@/components/Button';
import { Modal } from '@/components/Modal';
import { formatDate } from '../utils/dates';
import { validateEmail } from '../utils/validation';
过长的三元表达式
// Unreadable single line
const label = isAdmin ? 'Admin Dashboard' : hasEditPermission ? 'Editor View' : 'Read Only';
// Prettier reformats to readable multiline
const label = isAdmin
? 'Admin Dashboard'
: hasEditPermission
? 'Editor View'
: 'Read Only';
新项目配置指南
以下是新 JavaScript 或 TypeScript 项目的完整配置清单:
-
安装依赖:
npm install --save-dev prettier eslint eslint-config-prettier husky lint-staged -
创建
.prettierrc,写入团队约定的偏好配置 -
创建
.editorconfig,建立跨编辑器的基础规范 -
配置 ESLint,只启用代码质量规则(不要格式化规则)
-
配置 Husky + lint-staged,实现提交前自动检查
-
添加 npm 脚本:
{ "scripts": { "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint .", "lint:fix": "eslint --fix ." } } -
对整个代码库执行一次格式化:
npm run format -
将格式化后的代码作为单独一次提交,这样在
git blame中可以通过--ignore-rev轻松跳过
格式化的日常实践
一旦格式化流水线搭建完成,日常开发体验会有翻天覆地的变化:
- 随心所欲地写代码。 Prettier 会在保存或提交时自动重新格式化。
- Pull Request 只显示逻辑变更。 再也没有空白字符的干扰。
- 新成员能立即投入工作。 工具自动执行规范,不需要翻阅风格指南文档。
- 关于代码风格的争论彻底消失。 Prettier 就是权威,而不是某个开发者。
当你需要检查正在处理的 JSON 数据结构时,我们的 JSON Formatter 能让检查变得轻而易举——粘贴任意 JSON 即可在浏览器中即时获得可读的格式化输出。
相关资源
更多关于代码格式化和开发工具的内容:
- Code Minification Guide — 了解何时以及如何压缩 JavaScript 以用于生产环境
- Naming Conventions in Programming — 一致的命名是代码可读性的另一半
- SQL Formatting Best Practices — 将同样的格式化准则应用到数据库查询中
统一的代码格式化是工程领域中少有的能立竿见影且持续受益的投入。配置一次,自动化执行,然后把精力花在真正重要的代码上。