昨天讲完 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)
无论晴雨
总是以最美丽的姿态
徜徉大海与天空
>>: EP25 - EKS 日志蒐集使用 Loki 和 Grafana(一)
学习目标 本篇内容为阅读官方文件 Get data from a server 的笔记内容。 接续 ...
TortoiseSVN 是一个免费的版本控管工具,我以前在专案开发团队时,我们都是使用这个版控工具在...
这系列的程序码在 https://github.com/DanSnow/ironman-2020/...
在先前的章节里,我们可以常看见 HEAD 这个名词,它指的是什麽呢? // git 恢复文件到初始状...
注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...