Day 22 - Formatter 与 Linter - 提升程序品质工具

前言

昨天讲完 Code Review,团队一致的写 code 风格,可以大幅提升 review 的速度,也可以让每个人的程序码更好阅读。

因此今天来聊聊两个很容易被忽略,但其实在每一次储存的背後,都扮演了重要角色的 Formatter & Linter。

Formatter

Formatter 是什麽呢?简单来说就是「程序码排版神器」啦!

举例来说,对於带有很多参数的 function,有些人是这样写:

const fetchUserOrder(startDate, endDate, userId, orderStatus) {
    // ...
};

但有些人更喜欢分行:

const fetchUserOrder(
    startDate, 
    endDate, 
    userId, 
    orderStatus
) {
    // ...
};

formatter 的优势

如果整个程序都是自己写还没什麽问题,但如果一个团队中出现两种风格,往往 review code 的人会加倍辛苦,因为人毕竟是习惯的动物,举个例来说:

  • 我可以看懂横式书写的文字












但是当
  这
  两
  种混合在一起的时候,我
            会
            很
            痛
            苦

我打这个范例比较痛苦QQ

当然第一个想法就是:那就订个规则,大家都横式书写,或者大家都参数都规定要换行,这样就好了呀!

但事情没那麽简单:

  • 原本用直式书写的人可能已经习惯了,会不自觉用直式
  • 从网路上 copy 回来的范例,可能还要花时间排版

因此,我们需要用到 formatter,可以帮我们自动完成「排版」这件事,就算你习惯直式书写,也可以写好之後按下 format,就自动排版成横式书写了!

工具 - Prettier

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 排版。

触发排版的方式也很多样化,可以根据自己的编辑器偏好设定:

  • 按右键去 format document
  • format on Paste (贴上程序时排版)
  • format on Save (储存程序时排版)

不曾注意过的小细节们

自从开始看别的人的程序码,无论是团队内部的,或者网路上的,都慢慢发现,很多原本习以为常的风格,原来跟别人都不太一样:

箭头函式一个参数要不要括号?

可以透过 arrowParens: "<always|avoid>" 来设定

  • always:要括号
const addOne = (a) => a + 1;
  • avoid:一个参数就不要括号
const addOne = a => a + 1;

字串要用双引号还是单引号?

可以透过 singleQuote: <bool> 来设定

  • true:单引号(')
const fruit = 'apple';
  • false:双引号(")
const fruit = "apple";

tab 是两格空格还是四格?

可以透过 tabWidth: <number> 来设定

  • 2:两格空格
const doOrder = () => {
  console.log('传送交易资料到主机预计 2 秒...');
  setTimeout(() => {
    console.log('传送完成,开始配送预计 10 秒...');
    setTimeout(() => {
      console.log('包裹已送达!');
    }, 10000);
  }, 2000);
};
  • 4:四格空格
const doOrder = () => {
    console.log('传送交易资料到主机预计 2 秒...');
    setTimeout(() => {
        console.log('传送完成,开始配送预计 10 秒...');
        setTimeout(() => {
            console.log('包裹已送达!');
        }, 10000);
    }, 2000);
};

以上只是稍微列举了几个我自己遇到的,甚至也经常遇到在 A 专案用这套,在 B 专案又要换一套的状况。

但在 formatter 面前,一切都像切菜一样简单,只要设定档一次性设定,到哪个专案都可以用自己习惯的写法!

Linter

比起 Prettier 这样,比较偏向美观、排版的工具,Linter 则更像一个「随身教练」,会到处跟着你,纠正关於语法、效能等很实际的问题!

举例来说:

let fruit = 'apple';
const price;

console.log(frult);

这段 code 看起来好像很和平,但其实问题不少,你有发现吗?

  • fruit 没有修改过,其实应该用 const 宣告
  • priceconst 宣告却没有初始值
  • frult 是个 typo,没宣告的变数却直接取值

当程序的规模大起来,这些小细节很容易就会忘记,等到程序跑起来才发现怎麽东错西错,会报错、会 crash 的都还好发现,最恐怖的就是那些不报错的,可能在背景默默消耗你的效能!

工具 - ESLint

用来检查程序码的工具,如果是用在 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.

误改到 function 参数

这个我们在 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'.

改成有效的 function

这个更厉害了,大部分的 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

Formatter 与 Linter

一个在乎美观、排版、风格,另一个则在乎语法、效能、正确性

虽然笔者也确实遇过,Prettier 跟 ESLint 都装上去,套用设定档之後,Prettier 说要做 A,ESLint 说不要做 A 的窘境。

不过大多时候,两者其实是相辅相成的,因为如果团队中的成员都能够套用相同的程序码风格,可以让 code review 容易不少。

reviewer 不用去注意是不是有没用到的变数,或者函式内的参数被修改了,而是可以把心思放在修改的内容上,是否符合 ticket 范围,修改是否合理,能够大幅提升 review 速度,看起来也不会那麽伤眼,formatter 跟 linter 完全是幕後功臣(当然团队成员也要配合啦!)。

结语

不瞒各位说,我在学会这些方便的工具之前,不仅要手动在那边一行一行按 tab 排版,还甚至「矫正」了自己原本用双引号的习惯,慢慢变成习惯单引号,现在看到这些工具真的是相见恨晚啊!

顺带一提,笔者是四格 tab 派(看我的范例不顺眼的人应该有发现XD)

无论晴雨
总是以最美丽的姿态
徜徉大海与天空

参考资料

prettier-vs-eslint


<<:  第15章:管理与设定网路介绍(二)

>>:  EP25 - EKS 日志蒐集使用 Loki 和 Grafana(一)

新新新手阅读 Angular 文件 - Get data from a server(3) - Day12

学习目标 本篇内容为阅读官方文件 Get data from a server 的笔记内容。 接续 ...

使用批次档 (Batch file) 命令自动更新 TortoiseSVN 目录

TortoiseSVN 是一个免费的版本控管工具,我以前在专案开发团队时,我们都是使用这个版控工具在...

Day 26: 载入图片

这系列的程序码在 https://github.com/DanSnow/ironman-2020/...

Day16|什麽是 HEAD ?

在先前的章节里,我们可以常看见 HEAD 这个名词,它指的是什麽呢? // git 恢复文件到初始状...

D18-(9/18)-光磊(2340)-智慧手表供应链

注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...