昨天講完 Code Review,團隊一致的寫 code 風格,可以大幅提升 review 的速度,也可以讓每個人的程式碼更好閱讀。
因此今天來聊聊兩個很容易被忽略,但其實在每一次儲存的背後,都扮演了重要角色的 Formatter & Linter。
Formatter 是什麼呢?簡單來說就是「程式碼排版神器」啦!
舉例來說,對於帶有很多參數的 function,有些人是這樣寫:
const fetchUserOrder(startDate, endDate, userId, orderStatus) {
// ...
};
但有些人更喜歡分行:
const fetchUserOrder(
startDate,
endDate,
userId,
orderStatus
) {
// ...
};
如果整個程式都是自己寫還沒什麼問題,但如果一個團隊中出現兩種風格,往往 review code 的人會加倍辛苦,因為人畢竟是習慣的動物,舉個例來說:
但是當
這
兩
種混合在一起的時候,我
會
很
痛
苦
我打這個範例比較痛苦QQ
當然第一個想法就是:那就訂個規則,大家都橫式書寫,或者大家都參數都規定要換行,這樣就好了呀!
但事情沒那麼簡單:
因此,我們需要用到 formatter,可以幫我們自動完成「排版」這件事,就算你習慣直式書寫,也可以寫好之後按下 format,就自動排版成橫式書寫了!
formatter 裡面最有名的,莫過於 Prettier 了!
Prettier 不僅支援很多語言,適用於常見的框架(如:Vue、React、Angular),也能夠整合進自己喜歡的編輯器(如:VS code、Sublime)。
而 Prettier 能做到的事情,就是上面提到 formatter 做的事情,想體驗可以親自去玩玩看 Playground。
Prettier 會在專案的根部建立一個 .prettierrc
的檔案,裡面就是個很簡單的 json 檔,比如像這樣:
{
"arrowParens": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false,
"tabWidth": 4
}
更多設定參數可以參考 官方Doc。
透過這樣一個簡單的設定檔,Prettier 就能夠根據你的設定,幫你的 code 排版。
觸發排版的方式也很多樣化,可以根據自己的編輯器偏好設定:
自從開始看別的人的程式碼,無論是團隊內部的,或者網路上的,都慢慢發現,很多原本習以為常的風格,原來跟別人都不太一樣:
可以透過 arrowParens: "<always|avoid>"
來設定
const addOne = (a) => a + 1;
const addOne = a => a + 1;
可以透過 singleQuote: <bool>
來設定
'
)const fruit = 'apple';
"
)const fruit = "apple";
可以透過 tabWidth: <number>
來設定
const doOrder = () => {
console.log('傳送交易資料到主機預計 2 秒...');
setTimeout(() => {
console.log('傳送完成,開始配送預計 10 秒...');
setTimeout(() => {
console.log('包裹已送達!');
}, 10000);
}, 2000);
};
const doOrder = () => {
console.log('傳送交易資料到主機預計 2 秒...');
setTimeout(() => {
console.log('傳送完成,開始配送預計 10 秒...');
setTimeout(() => {
console.log('包裹已送達!');
}, 10000);
}, 2000);
};
以上只是稍微列舉了幾個我自己遇到的,甚至也經常遇到在 A 專案用這套,在 B 專案又要換一套的狀況。
但在 formatter 面前,一切都像切菜一樣簡單,只要設定檔一次性設定,到哪個專案都可以用自己習慣的寫法!
比起 Prettier 這樣,比較偏向美觀、排版的工具,Linter 則更像一個「隨身教練」,會到處跟著你,糾正關於語法、效能等很實際的問題!
舉例來說:
let fruit = 'apple';
const price;
console.log(frult);
這段 code 看起來好像很和平,但其實問題不少,你有發現嗎?
fruit
沒有修改過,其實應該用 const
宣告price
用 const
宣告卻沒有初始值frult
是個 typo,沒宣告的變數卻直接取值當程式的規模大起來,這些小細節很容易就會忘記,等到程式跑起來才發現怎麼東錯西錯,會報錯、會 crash 的都還好發現,最恐怖的就是那些不報錯的,可能在背景默默消耗你的效能!
用來檢查程式碼的工具,如果是用在 Javascript 的話,最推的就是 ESLint 了!
一樣也有個 Playground 可以體驗!
同時,我們可以用設定檔(.eslintrc
)來指定,有哪些 rule 要被包含進去:
這應該算是數一數二常遇到的了,可以透過 no-unused-vars
設定:
let price = 500;
let price2 = 1500;
console.log(price2);
// 'price' is assigned a value but never used.
這個我們在 Day 6 談 參數優化時提過,其實也是 side effects 的一種,程式運行上不會出錯,但很容易造成意想不到的結果,可以透過 no-param-reassign
抓出來:
const addSalary = (obj) => {
obj.salary += 2000;
};
const person = {
name: 'Joey',
salary: 30000
};
addSalary(person);
// Assignment to property of function parameter 'props'.
這個更厲害了,大部分的 bug 是因為疏忽,但這個 bug 則是單純因為我們不知道 NaN
如何被判斷,所以寫下這個錯誤的相等比對。
雖然我們在 Day 18 已經學會了,但即便你不知道,仍然可以透過 use-isnan
這個 rule 告訴你:
const price = parseInt('apple', 10);
if (price === NaN) {
console.log('價格有問題');
}
// Use the isNaN function to compare with NaN.
其他還有一卡車的 rule,很多都是真的踩到了才發現「啊!原來這樣不行啊?」,可以自行到官網 Doc 看看。
但其實沒有一定要全部 rule 都遵守,這部分依然是團隊風格的一部份,每個團隊可以自己打造 config!
甚至,我們還可以直接套用其他大型公司的 lint rule,不用自己一條條規則寫,直接讓程式教練跟大公司一樣耶!最有名的就是 Airbnb。
一個在乎美觀、排版、風格,另一個則在乎語法、效能、正確性。
雖然筆者也確實遇過,Prettier 跟 ESLint 都裝上去,套用設定檔之後,Prettier 說要做 A,ESLint 說不要做 A 的窘境。
不過大多時候,兩者其實是相輔相成的,因為如果團隊中的成員都能夠套用相同的程式碼風格,可以讓 code review 容易不少。
reviewer 不用去注意是不是有沒用到的變數,或者函式內的參數被修改了,而是可以把心思放在修改的內容上,是否符合 ticket 範圍,修改是否合理,能夠大幅提升 review 速度,看起來也不會那麼傷眼,formatter 跟 linter 完全是幕後功臣(當然團隊成員也要配合啦!)。
不瞞各位說,我在學會這些方便的工具之前,不僅要手動在那邊一行一行按 tab 排版,還甚至「矯正」了自己原本用雙引號的習慣,慢慢變成習慣單引號,現在看到這些工具真的是相見恨晚啊!
順帶一提,筆者是四格 tab 派(看我的範例不順眼的人應該有發現XD)
無論晴雨
總是以最美麗的姿態
徜徉大海與天空
結果 eslint 用久了,自己就會養成寫出遵循 eslint rules 的習慣了 XD
真的!我看到紅線就很討厭,所以經常不等 eslint,自己就手動調整,久了就連紅線都沒了,覺得舒坦XD