JavaScript 程式碼格式化:工具與標準
隨意打開一個 Pull Request,如果一半的差異都是空白字元的變動,你馬上就能感受到那股挫折感。格式不一致浪費時間、拖累程式碼審查,還會製造出與邏輯完全無關的合併衝突。解決方法很直接:選定一套格式化標準,透過工具自動執行,從此不必再為此費心。
本指南將帶你認識那些讓 JavaScript 格式化成為已解決問題的工具與標準。
為什麼統一格式化如此重要
格式化與美感無關——它攸關團隊效率。當每位開發者使用不同的縮排、大括號位置和行寬時,三個問題隨之而來:
程式碼審查變慢。 審查者必須消耗心力去理解陌生的格式,無法專注於評估邏輯。一個與程式碼庫其他部分風格截然不同的函式,會因為錯誤的原因而引人注目。
合併衝突大量增加。 開發者 A 用 Tab 重新格式化了檔案,開發者 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 開發者將 \r\n 行尾字元引入以 Unix 為基礎的專案。
使用 Husky 和 lint-staged 設定 Git Hooks
自動化格式化唯有在確實被執行時才有意義。仰賴開發者每次提交前都記得執行 npx prettier --write,根本無法規模化。Git hooks 能解決這個問題。
安裝設定
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 hook(.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 中的 Tab 與空格之爭
JavaScript 生態系統壓倒性地使用空格——具體來說是兩個空格的縮排。這並非放諸四海皆準的慣例(Go 使用 Tab,Python 使用四個空格),但在 JavaScript 中這是主流共識。
為什麼用兩個空格? JavaScript 中巢狀回呼和鏈式方法呼叫很快就會產生深層縮排。兩個空格的縮排既能維持可讀性,又不會把程式碼推到螢幕右側。
為什麼不用 Tab? Tab 容許每位開發者自行設定顯示寬度,聽起來不錯,但這意味著程式碼的對齊效果會因編輯器而異。使用空格的話,你看到的就是所有人看到的。
在 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 — 將同樣的格式化準則套用到資料庫查詢中
統一的程式碼格式化是工程領域中少數能立即見效且持續產生回報的投資。設定一次,自動化執行,然後把心力投注在真正重要的程式碼上。