Appearance
Prettier代码格式化
概述
Prettier是一个固执己见(Opinionated)的代码格式化工具,支持多种语言和框架。它能够自动格式化代码,统一团队代码风格,减少代码审查中关于格式的讨论。本文将全面介绍Prettier在React项目中的配置和使用。
Prettier核心特性
工作原理
源代码 -> 解析(Parse) -> AST -> 重新打印(Print) -> 格式化后的代码核心理念
- 固执己见: 提供最少的配置选项,避免无休止的代码风格讨论
- 一致性: 确保整个项目的代码风格完全一致
- 自动化: 保存时自动格式化,无需手动调整
- 语言支持: 支持JavaScript、TypeScript、JSX、CSS、JSON等多种格式
安装与配置
基础安装
bash
# 安装Prettier
npm install --save-dev prettier
# 安装ESLint集成
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
# 初始化配置
npx prettier --write .配置文件
.prettierrc.json
json
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": false,
"endOfLine": "lf",
"embeddedLanguageFormatting": "auto",
"singleAttributePerLine": false
}.prettierrc.js
javascript
module.exports = {
// 每行最大字符数
printWidth: 100,
// 缩进
tabWidth: 2,
useTabs: false,
// 分号
semi: true,
// 引号
singleQuote: true,
jsxSingleQuote: false,
quoteProps: 'as-needed',
// 尾随逗号
trailingComma: 'es5',
// 括号空格
bracketSpacing: true,
bracketSameLine: false,
// 箭头函数参数括号
arrowParens: 'always',
// 换行符
endOfLine: 'lf',
// 其他
requirePragma: false,
insertPragma: false,
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
embeddedLanguageFormatting: 'auto',
singleAttributePerLine: false,
};忽略文件(.prettierignore)
# 构建产物
dist/
build/
.next/
out/
# 依赖
node_modules/
# 生成文件
*.generated.ts
*.d.ts
# 配置文件
package-lock.json
yarn.lock
pnpm-lock.yaml
# 其他
coverage/
.cache/
public/配置选项详解
printWidth - 每行最大字符数
javascript
// printWidth: 80
const result = someFunction(arg1, arg2, arg3, arg4, arg5);
// printWidth: 40
const result = someFunction(
arg1,
arg2,
arg3,
arg4,
arg5
);tabWidth 和 useTabs - 缩进设置
javascript
// tabWidth: 2, useTabs: false
function example() {
if (true) {
console.log('Hello');
}
}
// tabWidth: 4, useTabs: false
function example() {
if (true) {
console.log('Hello');
}
}
// useTabs: true
function example() {
→ if (true) {
→ → console.log('Hello');
→ }
}semi - 分号
javascript
// semi: true
const x = 1;
const y = 2;
// semi: false
const x = 1
const y = 2singleQuote - 单引号
javascript
// singleQuote: true
const str = 'Hello';
const jsx = <div className="container" />;
// singleQuote: false
const str = "Hello";
const jsx = <div className="container" />;jsxSingleQuote - JSX单引号
javascript
// jsxSingleQuote: false (默认)
const element = <div className="container">Hello</div>;
// jsxSingleQuote: true
const element = <div className='container'>Hello</div>;trailingComma - 尾随逗号
javascript
// trailingComma: 'none'
const obj = {
a: 1,
b: 2
};
// trailingComma: 'es5'
const obj = {
a: 1,
b: 2,
};
// trailingComma: 'all'
const obj = {
a: 1,
b: 2,
};
function example(
arg1,
arg2,
) {}bracketSpacing - 对象括号空格
javascript
// bracketSpacing: true
const obj = { a: 1, b: 2 };
// bracketSpacing: false
const obj = {a: 1, b: 2};bracketSameLine - JSX括号位置
javascript
// bracketSameLine: false (默认)
<Button
onClick={handleClick}
disabled={isDisabled}
>
Click Me
</Button>
// bracketSameLine: true
<Button
onClick={handleClick}
disabled={isDisabled}>
Click Me
</Button>arrowParens - 箭头函数参数括号
javascript
// arrowParens: 'always'
const fn = (x) => x;
// arrowParens: 'avoid'
const fn = x => x;endOfLine - 换行符
javascript
// endOfLine: 'lf' (Unix/Linux)
// 每行以 \n 结尾
// endOfLine: 'crlf' (Windows)
// 每行以 \r\n 结尾
// endOfLine: 'cr' (Old Mac)
// 每行以 \r 结尾
// endOfLine: 'auto'
// 自动检测与ESLint集成
解决冲突
bash
# 安装集成包
npm install --save-dev eslint-config-prettier eslint-plugin-prettierjavascript
// .eslintrc.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'prettier', // 必须放在最后,关闭ESLint中与Prettier冲突的规则
],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error', // Prettier错误显示为ESLint错误
},
};推荐配置
javascript
// .eslintrc.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended',
'prettier', // 关闭冲突规则
],
plugins: ['prettier'],
rules: {
'prettier/prettier': [
'error',
{
endOfLine: 'auto', // 避免Windows/Unix换行符问题
},
],
},
};编辑器集成
VSCode配置
settings.json
json
{
// Prettier设置
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
// 特定语言配置
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// Prettier配置文件
"prettier.configPath": ".prettierrc.json",
"prettier.requireConfig": true,
"prettier.useEditorConfig": false,
// 保存时的操作顺序
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}extensions.json
json
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
]
}WebStorm配置
Settings/Preferences -> Languages & Frameworks -> JavaScript -> Prettier
- Prettier package: [项目路径]/node_modules/prettier
- Run on save for files: {**/*,*}.{js,ts,jsx,tsx,css,scss,json,md}
- On 'Reformat Code' action: √
- On save: √NPM脚本
package.json
json
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"format:staged": "prettier --write",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix && prettier --write ."
}
}详细脚本示例
json
{
"scripts": {
// 格式化所有文件
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
// 检查格式(不修改文件)
"format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
// 格式化特定目录
"format:src": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
"format:tests": "prettier --write \"tests/**/*.{js,jsx,ts,tsx}\"",
// 格式化已修改的文件
"format:changed": "git diff --name-only --diff-filter=ACMR | grep -E '\\.(js|jsx|ts|tsx)$' | xargs prettier --write",
// 组合命令
"lint:format": "npm run lint:fix && npm run format",
// 验证格式和lint
"validate": "npm run format:check && npm run lint"
}
}Git Hooks集成
Husky + lint-staged
bash
# 安装
npm install --save-dev husky lint-staged
# 初始化husky
npx husky install
npm pkg set scripts.prepare="husky install"
# 创建pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"lint-staged配置
package.json
json
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,css,scss,md}": [
"prettier --write"
]
}
}.lintstagedrc.js
javascript
module.exports = {
// JavaScript/TypeScript文件
'*.{js,jsx,ts,tsx}': [
'eslint --fix',
'prettier --write',
'git add',
],
// 样式文件
'*.{css,scss,less}': [
'prettier --write',
'git add',
],
// JSON文件
'*.json': [
'prettier --write',
'git add',
],
// Markdown文件
'*.md': [
'prettier --write',
'git add',
],
};pre-commit配置示例
bash
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
echo "🚀 Running pre-commit checks..."
# 1. Lint-staged
npx lint-staged
# 2. 类型检查
npm run type-check
# 3. 测试
npm run test:staged
echo "✅ Pre-commit checks passed!"CI/CD集成
GitHub Actions
yaml
name: Format Check
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Check formatting
run: npm run format:check
- name: Comment on PR
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ Code formatting check failed. Please run `npm run format` locally.'
})Auto-format Action
yaml
name: Auto Format
on:
pull_request:
branches: [main]
jobs:
format:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Format code
run: npm run format
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: 'style: auto-format code with Prettier'
file_pattern: '*.js *.jsx *.ts *.tsx *.json *.css *.scss *.md'高级用法
针对不同文件的配置
javascript
// .prettierrc.js
module.exports = {
// 默认配置
printWidth: 100,
singleQuote: true,
// 针对特定文件的配置
overrides: [
{
files: '*.json',
options: {
printWidth: 80,
tabWidth: 2,
},
},
{
files: '*.md',
options: {
proseWrap: 'always',
printWidth: 80,
},
},
{
files: ['*.css', '*.scss'],
options: {
singleQuote: false,
},
},
{
files: 'package.json',
options: {
tabWidth: 2,
},
},
],
};EditorConfig集成
# .editorconfig
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{js,jsx,ts,tsx,json}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = falsejavascript
// .prettierrc.js
module.exports = {
// Prettier会读取.editorconfig的设置
// 如果设置了相同的选项,Prettier配置优先
printWidth: 100,
singleQuote: true,
};格式化忽略
代码块忽略
javascript
// prettier-ignore
const matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
// JSX
{/* prettier-ignore */}
<div className="formatted" >
Badly formatted but preserved
</div>
// CSS
/* prettier-ignore */
.selector {
color: red;
padding: 10px;
}
// HTML
<!-- prettier-ignore -->
<div class="preserved" >
Content
</div>范围忽略
javascript
// prettier-ignore-start
const matrix = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
const vector = [
1,
2,
3
];
// prettier-ignore-end
const formatted = { a: 1, b: 2 };Pragma标记
javascript
// .prettierrc.js
module.exports = {
requirePragma: true,
insertPragma: true,
};
// 需要格式化的文件顶部添加
/**
* @prettier
*/
// 或
/**
* @format
*/团队协作
共享配置包
bash
# 创建共享配置包
mkdir prettier-config-company
cd prettier-config-company
npm init -yjavascript
// prettier-config-company/index.js
module.exports = {
printWidth: 100,
tabWidth: 2,
singleQuote: true,
trailingComma: 'es5',
semi: true,
arrowParens: 'always',
endOfLine: 'lf',
};
// prettier-config-company/package.json
{
"name": "@company/prettier-config",
"version": "1.0.0",
"main": "index.js"
}bash
# 发布配置包
npm publish --access public
# 在项目中使用
npm install --save-dev @company/prettier-configjson
// 项目的 package.json
{
"prettier": "@company/prettier-config"
}
// 或 .prettierrc.json
"@company/prettier-config"
// 或 .prettierrc.js
module.exports = require('@company/prettier-config');扩展共享配置
javascript
// .prettierrc.js
module.exports = {
...require('@company/prettier-config'),
// 项目特定覆盖
printWidth: 120,
};常见问题
Windows换行符问题
javascript
// .prettierrc.js
module.exports = {
endOfLine: 'auto', // 自动检测
// 或
endOfLine: 'lf', // 统一使用Unix换行符
};
// .gitattributes
* text=auto
*.js text eol=lf
*.jsx text eol=lf
*.ts text eol=lf
*.tsx text eol=lf与其他工具冲突
javascript
// 1. 与ESLint冲突
// 使用 eslint-config-prettier
module.exports = {
extends: ['eslint:recommended', 'prettier'],
};
// 2. 与stylelint冲突
// 使用 stylelint-config-prettier
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
};性能优化
bash
# 使用缓存
prettier --write --cache .
# 只格式化变更的文件
git diff --name-only --diff-filter=ACMR | xargs prettier --write
# 并行处理
prettier --write . --loglevel warn大型项目迁移
bash
# 1. 创建配置文件
cat > .prettierrc.json << EOF
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "es5"
}
EOF
# 2. 格式化所有文件
npm run format
# 3. 提交为单独的commit
git add .
git commit -m "chore: format code with Prettier"
# 4. 配置Git blame忽略
echo "$(git rev-parse HEAD) # Prettier formatting" >> .git-blame-ignore-revs
# 5. Git配置
git config blame.ignoreRevsFile .git-blame-ignore-revs实战示例
完整项目配置
javascript
// .prettierrc.js
module.exports = {
// 基础配置
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'es5',
bracketSpacing: true,
bracketSameLine: false,
arrowParens: 'always',
endOfLine: 'lf',
// 针对不同文件的配置
overrides: [
{
files: '*.json',
options: {
printWidth: 80,
},
},
{
files: '*.md',
options: {
proseWrap: 'always',
printWidth: 80,
},
},
],
};# .prettierignore
# 依赖
node_modules/
# 构建产物
dist/
build/
.next/
out/
# 配置文件
package-lock.json
yarn.lock
pnpm-lock.yaml
# 生成文件
*.generated.ts
*.d.ts
# 其他
coverage/
.cache/
public/
.env
.env.localjavascript
// .eslintrc.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'prettier', // 必须放在最后
],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error',
},
};json
// package.json
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "npm run lint -- --fix && npm run format",
"validate": "npm run format:check && npm run lint"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,css,scss,md}": [
"prettier --write"
]
},
"devDependencies": {
"prettier": "^3.0.0",
"eslint": "^8.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"husky": "^8.0.0",
"lint-staged": "^15.0.0"
}
}bash
# .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged最佳实践
1. 从一开始就使用
在项目初始化时就配置Prettier,避免后期大规模代码格式化。
2. 团队统一配置
使用共享配置包确保整个团队使用相同的格式化规则。
3. 自动化执行
通过Git Hooks和CI/CD自动检查和格式化代码。
4. 最小化配置
Prettier的默认配置已经很好,尽量减少自定义配置。
5. 与ESLint配合
Prettier负责格式化,ESLint负责代码质量检查,两者配合使用。
6. 编辑器集成
确保团队成员都配置了编辑器的自动格式化功能。
7. 忽略不必要的文件
使用.prettierignore忽略生成文件和第三方代码。
8. 文档化
在项目README中说明格式化规则和使用方法。
总结
Prettier是现代前端项目必不可少的工具,它能够:
- 统一代码风格: 消除团队成员之间的格式差异
- 提高效率: 自动格式化,节省手动调整的时间
- 减少争议: 固执己见的配置避免无休止的代码风格讨论
- 提升代码质量: 一致的格式使代码更易读
- 简化代码审查: 减少关于格式的评论,专注于逻辑
通过合理配置和使用Prettier,可以显著提升团队的开发效率和代码质量。