CI failed… 它說我 coding style 不一致!我明明有裝 Prettier 呀!
你在 local 用你自己的 Prettier、 其他人在 local 用他們自己的 Prettier,當然不一致囉。 多人協作可不是讓人來展現雅量的場合。 要在專案層級,也就是 repo 內設定好 Prettier config,統一標準才行。
也是… 給予不一致的 code 雅量,那份寬容遲早變成技術債! 可是 Prettier 選項那麼多,到底要怎麼設啦!
來,這邊 28 個設定範例給你看!
Prettier 官方預設值
依照最新(截至本篇發文日期為止)官方文件 Options · Prettier,預設值列出如下
/** @type {import("prettier").Config} */
const prettierDefaults = {
experimentalTernaries: false,
experimentalOperatorPosition: 'end',
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: false,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'all',
bracketSpacing: true,
objectWrap: 'preserve',
bracketSameLine: false,
jsxBracketSameLine: false,
arrowParens: 'always',
rangeStart: 0,
rangeEnd: Infinity,
parser: null,
filepath: null,
requirePragma: false,
insertPragma: false,
checkIgnorePragma: false,
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
vueIndentScriptAndStyle: false,
endOfLine: 'lf',
embeddedLanguageFormatting: 'auto',
singleAttributePerLine: false,
};
export default prettierDefaults;那一個一個來看範例吧!
Experimental Ternaries
Prettier v3.1 引入的,為了解決巢狀三元運算子看起來像麵條的問題。目前還是實驗性質。
// 原始碼
const color = status === 'success' ? 'green' : status === 'warning' ? 'yellow' : status === 'error' ? 'red' : 'gray';
// false ⇀ 預設,無限縮排。
const color =
status === 'success'
? 'green'
: status === 'warning'
? 'yellow'
: status === 'error'
? 'red'
: 'gray';
// true ⇀ curious ternaries 風格,都垂直對齊。
const color =
status === 'success' ? 'green'
: status === 'warning' ? 'yellow'
: status === 'error' ? 'red'
: 'gray';Experimental Operator Position
Prettier v3.5 推出的,也還是實驗性質。
主要想解決的問題是:當邏輯運算太長需要斷行時,運算子(如 &&、||)應該出現在行首還是行尾。
// 原始碼
const isEligible = age >= 18 && hasValidId && isNotBanned;
// end ⇀ 預設,運算子在行尾
const isEligible =
age >= 18 &&
hasValidId &&
isNotBanned;
// start ⇀ 運算子在行首,一眼就能看出這行是接續上一個表達式
const isEligible =
age >= 18
&& hasValidId
&& isNotBanned;Print Width
這應該是大家最熟悉的啦!就是:一行超過幾個字元,就要換行,沒有什麼好給範例的。預設是 80 字元。實際上要設定多少好,就看團隊共識或個人偏好是什麼了。
近年官方文件載明了,推薦使用比預設值 80 更低的數字,原因是 printWidth 其實是偏好長度,不是最大長度。也就是說,Prettier 只是會參考這個數字決定要不要換行,屬於軟性設定:
- 超過了:不一定換行
- 沒超過:可能還是換行
跟 ESLint 的 max-len 只要超過就必報錯,是不一樣的。
我生涯至今,待過的團隊全都用 100 或 120,沒遇過用 80 的呀!
Tab Width
縮排寬度。大家應該也很熟悉。
我身邊有同樣是 Web 前端的朋友,曾經因為用了 tabWidth: 4,遭到面試官嫌棄。聽聞這件事情後,我此生只敢用 2。
// 2 ⇀ 預設
function greet(name) {
if (name) {
console.log('Hello, ' + name);
}
}
// 4 ⇀ 寬寬的
function greet(name) {
if (name) {
console.log('Hello, ' + name);
}
}PS. 如果沒裝 Prettier,VSCode / Cursor 本身也有縮排寬度設定 editor.tabSize 預設是 4。
Use Tabs
縮排用空格還是用 tab 字元。
tab 字元是什麼呢?是 \t,實際顯示寬度由 VSCode / Cursor 的 tab size 設定決定。
// false ⇀ 空格,每個 · 代表一個空格
function greet(name) {
··if (name) {
····console.log('Hello, ' + name);
··}
}
// true ⇀ tab,每個 → 代表一個 tab 字元
function greet(name) {
→if (name) {
→→console.log('Hello, ' + name);
→}
}一樣,我生涯至今還沒遇過有人縮排用 tab 字元,大家都用空格。
Semi
也是大家很熟悉的:行尾是否加分號。
// true ⇀ 預設,一律有分號
const name = 'Yuzu';
const age = 3;
function greet() {
console.log('Hello!');
}
// false ⇀ 基本沒分號。只有在避免 ASI 陷阱的時候,才補分號。
const name = 'Yuzu'
const age = 3
function greet() {
console.log('Hello!')
}2019 入行之初,受到導師、同儕影響,大家都耳提面命的互相提醒大家不加分號可能踩到 ASI 陷阱。所以我通常都用預設值 true,一律加分號就沒煩惱了。
不過隨著在業界打滾久了,發現不喜歡分號的也是大有人在,並且相當信任現今的 JS 引擎跟 Prettier 機制。所以到底要不要加分號呢?一樣,看團隊共識或個人偏好吧。
什麼是 ASI 陷阱?
ASI(Automatic Semicolon Insertion)是 JS 引擎自動幫你補分號的機制,但它不是每個換行都補,它有自己的規則。
舉個例,當下一行開頭是 [ 或 ( 時,JS 引擎就不會補了,會把上下兩行視為同一個表達式:
const a = 1
[1, 2, 3].forEach(console.log)
// JS 引擎看到的是
const a = 1[1, 2, 3].forEach(console.log) // 報錯
Single Quote
這也是大家熟悉的!有人認為單引號比較簡潔,也有人 don't care。它跟 JSX Quote 互相獨立,互不影響。
// false ⇀ 預設,雙引號
const name = "Yuzu";
const greeting = "Hello, world!";
// true ⇀ 單引號
const name = 'Yuzu';
const greeting = 'Hello, world!';例外情況,當字串本身包含所選引號時,Prettier 會自動換用另一種避免字元跳脫了。
// 假如設 true,照理應該換成單引號,但因為字串內有用到 ',所以就沒換,保留雙引號。
const msg = "It's a cat!";
// 假如設 false,照理應該換成雙引號,但因為字串內有用到 ",因此保留單引號。
const msg = 'Say "hello"!';Quote Props
這項設定,中文還真不知道怎麼說,就叫它要不要給物件的 key 加引號吧。
// "as-needed" ⇀ 預設,只在必要時加引號
const obj = {
name: 'Yuzu',
123: 'number key',
'my-key': 'value', // 有連字號,不加引號會報錯
};
// "consistent" ⇀ 只要有一個 key 需要加引號,全部都加
const obj = {
'name': 'Yuzu',
'123': 'number key',
'my-key': 'value',
};
// "preserve" ⇀ 照你原本寫的,不動
const obj = {
'name': 'Yuzu', // 沒必要加引號,但你加了,它保留
123: 'number key',
'my-key': 'value',
};JSX Single Quote
JSX 屬性,要單引號還是雙引號。它跟 Single Quote 互相獨立,互不影響。
// false ⇀ 預設,雙引號
<Button variant="outlined" aria-label="submit" />
// true ⇀ 單引號
<Button variant='outlined' aria-label='submit' />Trailing Comma
多行結構最後一個元素後面,加不加逗號。
// "all" ⇀ 預設,所有地方都加,包括函式參數
function greet(
name,
age,
) {
console.log(name, age);
}
const obj = {
name: 'Yuzu',
age: 3,
};
// "es5" ⇀ 函式參數不加,陣列、物件照加
function greet(
name,
age
) {
console.log(name, age);
}
const obj = {
name: 'Yuzu',
age: 3,
};
// "none" ⇀ 全部不加
function greet(
name,
age
) {
console.log(name, age);
}
const obj = {
name: 'Yuzu',
age: 3
};Bracket Spacing
物件的大括號內側要不要加空格。
// true ⇀ 預設,有空格
const cat = { name: 'Yuzu', age: 3 };
// false ⇀ 沒空格
const cat = {name: 'Yuzu', age: 3};這肯定是要加空格的吧!也是生涯至今還沒遇過有人不加空格的。
Object Wrap
Prettier v3.5 新增的,是在控制物件要不要變一行。
// "preserve" ⇀ 預設,保留你的寫法
// 你寫一行就一行:
const cat = { name: 'Yuzu', age: 3 };
// 你寫多行就多行:
const cat = {
name: 'Yuzu',
age: 3,
};
// "collapse" ⇀ 能變一行就變一行
// 不管你怎麼寫,只要不超過 printWidth,Prettier 都會把你變一行:
const cat = { name: 'Yuzu', age: 3 };Bracket Same Line
當 JSX / HTML 元素的屬性變成多行的時候,關閉元素的 > 要不要跟最後一個屬性處在同一行。
// false ⇀ 預設,> 單獨一行
<Button
className="btn"
onClick={handleClick}
>
Click me
</Button>
// true ⇀ > 跟最後一個屬性同行
<Button
className="btn"
onClick={handleClick}>
Click me
</Button>原本我也是無偏好的,但曾有前同事,不會用 Prettier,又看不慣 > 單獨一行,他覺得看起來很奇怪,要求我解決這個問題。
從此之後,團隊大家都同意的話,我就一律把這項改設 true。後來我自己也覺得,設 true 比較省空間啦。
JSX Bracket Same Line(Deprecated)
這項已經被 deprecated 啦。功能跟 bracketSameLine 是一樣的,但只針對 JSX。現在直接用 bracketSameLine 就好。
Arrow Parens
當箭頭函式只有一個參數時,要不要給參數加括號。
// "always" ⇀ 預設,永遠加括號
const double = (x) => x * 2;
const identity = (x) => x;
// "avoid" ⇀ 單一參數省略括號
const double = x => x * 2;
const identity = x => x;曾遇過有主管堅持要 avoid,他的理由是這寫法本來就是被允許的,程式不會報錯,沒必要加,看著也比較簡潔。
不過對於比較注重標準化的我,覺得統一看著才整齊!而且有時還要寫 type、default value,加了可讀性才是比較好的是吧?大家覺得呢?
Range Start / Range End
這兩個的意思是:只格式化檔案的某個範圍。單位是字元 offset,不是行號。
通常不需要也不會在設定檔裡寫這個,主要是在呼叫 Prettier API 時才可能會用到。
預設是 0 / Infinity
Parser
Prettier 在幫你 format code 之前,要先讀懂你的程式碼,這個讀懂的過程就叫 parse(解析)。
我們可以指定 Prettier 用哪個 parser 解析檔案。通常 Prettier 會根據副檔名自動推斷,也是不需要自己設定。常見設定值:babel、typescript、css、json、markdown、html、yaml
預設是 null(自動偵測)
Filepath
透過檔案路徑推斷要用哪個 parser,通常在呼叫 Prettier API 時使用,不需要自己設定再 + 1。
預設是 null(不要用檔案路徑來推斷 parser,用上面預設的看副檔名自動偵測)
Require Pragma
設為 true 時,只 format 頂端有寫 @prettier 或 @format 的檔案。
適合用在大型舊專案要漸進式導入 formatter 的時候——先只格式化有加標記的檔案,其餘先不動。
// false ⇀ 預設,所有檔案都 format
// true ⇀ 認標記、局部 format
// 沒有 pragma,不會被 format
const a=1;const b=2
// 有 pragma,會被 format
// 格式化前
/** @prettier */
const a=1;const b=2
// 格式化後
/** @prettier */
const a = 1;
const b = 2;Insert Pragma
格式化後自動在檔案頂端插入 @format pragma。
通常跟 requirePragma 搭配使用,先用 insertPragma 幫舊檔案批次加上標記,再開啟 requirePragma。
// false ⇀ 預設,不插入
// true ⇀ 格式化後自動插入
// 格式化前
const a = 1;
const b = 2;
// 格式化後
/** @format */
const a = 1;
const b = 2;Check Ignore Pragma
Prettier v3.6 新增的。設為 true 時,檔案頂端有 @noformat 或 @noprettier 的檔案,Prettier 會跳過不 format。
// false ⇀ 預設,忽略 pragma 照樣都幫你 format
// true ⇀ 這個檔案會被跳過
/** @noformat */
const a=1; const b=2;Prose Wrap
控制 Markdown 文字段落的換行行為。
<!-- "preserve" ⇀ 預設,保留你的換行 -->
這是一段很長的文字,Prettier 不會幫你換行,維持你原本的寫法。
<!-- "always" ⇀ 超過 printWidth 就換行 -->
這是一段很長的文字,如果超過 printWidth 設定的長度,Prettier 會自動
幫你在適當的地方換行。
<!-- "never" ⇀ 移除換行,強制合成一行 -->
不管你原本怎麼斷行,Prettier 都會把段落合成一行。HTML Whitespace Sensitivity
控制 HTML format 時如何處理元素之間的空白。
HTML 的空白有時候有視覺意義(比如兩個 <span> 之間的空格),這個設定決定 Prettier 怎麼看待它。
<!-- "css" ⇀ 預設,根據 CSS display 屬性決定 -->
<!-- span 是 inline,它們之間的空白有意義,Prettier 會保留 -->
<div><span>Hello</span> <span>World</span></div>
<!-- "strict" ⇀ 所有空白都視為有意義,更保守 -->
<div><span>Hello</span> <span>World</span></div>
<!-- "ignore" ⇀ 忽略所有空白 -->
<div><span>Hello</span><span>World</span></div>Vue Indent Script and Style
Vue SFC 的 <script> 和 <style> 區塊裡的內容要不要縮排一層。
<!-- false ⇀ 預設 -->
<script setup>
const name = 'Yuzu';
</script>
<style scoped>
.cat {
color: orange;
}
</style>
<!-- true ⇀ 內容縮排一層 -->
<script setup>
const name = 'Yuzu';
</script>
<style scoped>
.cat {
color: orange;
}
</style>End of Line
要用哪個換行字元。
"lf":\n,Unix / macOS / Linux 的預設,也是 Prettier 的預設。"crlf":\r\n,Windows 的預設。"cr":\r,舊版 macOS 用的,現在很少見。"auto":保留原本檔案用的換行格式。
我覺得一律用 "lf"比較省事。最好再搭配:
- 團隊統一使用 Editor Config,在專案 repo 內創建
.editorconfig設定end_of_line = lf,控制 VSCode / Cursor 的行為 - 專案 repo 內創建
.gitattributes設定 `* text=auto eol=lf,控制 Git 的行為
避免用 Windows 開發的協作者,弄出成千上萬的假 changes。…對,我就是被雷過。
Embedded Language Formatting
是否 format 嵌入在字串裡的其他語言,例如 template literal 裡的 GraphQL、SQL、HTML。
// "auto" ⇀ 預設,自動 format
const query = /* graphql */ `
query GetUser {
user {
name
age
}
}
`;
// "off" ⇀ 不 format 嵌入語言
const query = /* graphql */ `
query GetUser { user { name age } }
`;Single Attribute Per Line
HTML、Vue、JSX 每個屬性要不要強制各佔一行。
// false ⇀ 預設,屬性可以同行
<Button variant="outlined" onClick={handleClick} disabled={isLoading}>
Submit
</Button>
// true ⇀ 每個屬性各佔一行
<Button
variant="outlined"
onClick={handleClick}
disabled={isLoading}>
Submit
</Button>總結:分享我的偏好設定
我自己實際上只會動 4 個:
/** @type {import("prettier").Config} */
const prettierConfig = {
plugins: ['prettier-plugin-tailwindcss'],
printWidth: 100, // 預設 80
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true, // 預設 false
jsxSingleQuote: false,
trailingComma: 'es5', // 預設 'all'
bracketSameLine: true, // 預設 false
arrowParens: 'always',
};
export default prettierConfig;逐條解說:
printWidth:現在螢幕都很寬,我覺得設定長一點好啊!而且生涯至今遇到同事們都沒意見。tabWidth:剛才提到過的,因為有朋友用 4 被面試官嫌棄,所以即便預設就是 2 了,我還是都會把這項特別寫出來表態(看人被蛇咬,十年怕草蛇)。useTabs:遇過有天兵調到 tab 不自知,也是寫出來表態的。semi:也是剛才有提到過的,都用 true 省事,如果有同事是不加分號派的話,那我會配合改,沒堅持的。singleQuote:單純是想跟 JSX Quote 有一個區別的感覺,生涯至今遇到的同事們也都覺得單引號看起來比較潮。jsxQuote:保留雙引號的原因是,這樣跟 HTML 保持一致,也是寫出來表態的。trailingComma:其實也沒堅持,只是好像使用舊時代的設定,有莫名的安全感?bracketSameLine:上面提到過的,原先是配合同事改,加上後來自己覺得改成true比較省空間。arrowParens:上面提到過的,寫出來表態的。
加映 prettier-plugin-tailwindcss
有在用 Tailwind CSS 的話強烈推薦安裝它。
這是 Tailwind CSS 官方出的 Prettier plugin,功能是自動排序 class names,順序會按照官方推薦的規則來排。
// 原本亂排的 class 們
<div className="text-white flex p-4 bg-blue-500 items-center rounded-lg">
// format 後會變成
<div className="flex items-center rounded-lg bg-blue-500 p-4 text-white">安裝方法自己點連結看 tailwindlabs / prettier-plugin-tailwindcss。
那麼就這樣!