iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
Modern Web

從零開始學習 Next.js系列 第 19

Day19 - 寫出更有品質的程式碼,信 eslint 得永生

前言

俗話說:「一千個人,就有一千種程式碼的寫法」,而且我們身在 JavaScript 的世界中,一段程式碼可以用各種不同的方式來表達,例如該用單引號或雙引號、空格或 Tab 等等的議題。

JavaScript 是世界上最棒的語言

如果深入到 React 中,會有使用 map 渲染 component 時忘記加 key ,或是者是 <Index></Index> 沒有 children 卻不使用 close tag 等情況,而且有一些程式碼可能會導致 bug,或是這種寫法可能會引發一些問題,但是難以發現。再加上導入 TypeScript、Next.js 後,有數不清的規則,只有文件規範一般人根本無法完全記住。

為了解決這些問題,所以我們需要 ESLint 幫助我們規範程式碼的撰寫方式,團隊都必須遵照規範,讓團隊成員不會寫出 ninja code。

除了 ESLint 之外,為了讓團隊撰寫的程式碼風格更為一致,還會再導入 Prettier,處理像是:

  • 單引號、雙引號
  • 空格、Tab
  • 是否需要分號
  • 物件的最後一個欄位需不需要逗點

以上等等的問題都是我們經常遇到的問題,如同文章開頭所述,每個人都有自己的程式碼風格,隨著專案逐漸擴大,將會使得程式碼不易維護。所以使用 ESLint 搭配 Prettier 可以讓程式自動幫我們維護程式碼的風格,免得還需要用人工的方式檢驗程式碼,不僅沒有效率,而且也較不可靠。

在這篇文章中將會介紹如何在 Next.js + TypeScript 的環境中導入 ESLint 跟 Prettier 的設定,並且在文末將會介紹如何 husky 這項工具,在每次 git commit 時都可以自動幫助我們檢查是否符合 ESLint 及 Prettier 的規範,讓開發事半功倍。

建立一個全新的 Next.js + TypeScript 專案

首先,我們先來建立一個新的專案,可以使用以下指令 npxyarn 開啟一個 Next.js + TypeScript 的專案:

npx create-next-app --typescript
# or
yarn create next-app --typescript

在 Next.js 11 版以後,ESLint 已經成為專案的標配,在建立完專案後就可以在資料夾中看到 .eslintrc.json 。預設的 ESLint 內容如下, 這是 Next.js 規範的 ESLint:

{
	"extends": "next/core-web-vitals"
}

這個設定基本上已經包含了大部分 React 會需要的 ESLint plugin:

  • [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react)
  • [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)
  • [eslint-plugin-next](https://www.npmjs.com/package/@next/eslint-plugin-next)

如同這個設定的名稱所述,它將會禁止兩種會影響 core web vitals 的寫法,其 ESLint 的等級都設置為 error,因此絕對無法通過 lint:

  • No sync scripts

    在 component 中使用 <script> 引入其他的 JavaScript 檔案時一定要加上 asyncdefer ,否則同步的 <script> 將會影響 component 的效能:

    // bad
    <script src="some.js"></script>
    
    // good
    <script src="some.js" defer></script>
    
    // good
    <script src="some.js" async></script>
    

    還有另外一種合法的寫法是使用目前在實驗中的 next/script,它也會讓在 component 中載入外部檔案變成非同步的:

    import Script from "next/script";
    
    const Home = () => {
      return (
        <div class="container">
          <Script src="https://third-party-script.js"></Script>
          <div>Home Page</div>
        </div>
      );
    };
    
    export default Home;
    
  • No html link for pages
    這個規則禁止在 component 中使用原生 HTML 的 <a> 切換頁面,如果需要切換頁面,則是使用 next/link 提供的 <Link>

    ```javascript
    // bad
    <a href="/about">About Us</a>
    
    // good
    <Link href="/about">
    
    ```
    

如果是全新的 Next.js 專案,官方建議直接導入 next/core-web-vitals ,這個設定已經包含了 next 這個 config,你可能會在其他地方看到 extends 中同時包含 next/core-web-vitalsnext ,這個是較為舊的寫法,大概是幾個月前 XD

安裝 Prettier

首先,我們需要安裝以下兩個套件:

yarn add -D prettier eslint-config-prettier

因為 ESLint 已經包含程式碼 formatting 的規則,這些規則可能會與 Prettier 的設定發生衝突,所以為了解衝突,可以使用 eslint-config-prettier 關閉一些 ESLint 會與 Prettier 發生衝突的規則。

接著,我們需要為程式碼風格做一些配置,例如單引號、分號、tab 幾格、程式碼寬度等等,至於其他設定就看團隊的需求了:

// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "useTabs": false,
  "printWidth": 120
}

然後我們需要在 .eslintrc.json 中設定 eslint-config-prettier ,在 extends 的最後一個位置加上 "prettier" 的設定,如此一來才能覆蓋 "next/core-web-vitals" 引入的 ESLint 設定:

// .eslintrc.json
{
  "extends": [
    "next/core-web-vitals",
    "prettier"
  ],
}

最後,我們會在 package.json 中設定 prettier 的指令,為的是可以在文章後面串接 git hook。 團隊在開發時,一定會執行 git commit 將程式碼記錄在 git 上,這時可以觸發 git hook,同時跑 prettier 幫我們把所有的程式碼都「整理」過一次,如此一來可以確保推到 git 上的程式碼都是符合團隊規範的。

// package.json
{
  "scripts": {
    "prettier": "prettier '**/*.{js,jsx,ts,tsx}' --write"
  }
}

安裝 @typescript-eslint/eslint-plugin

TypeScript 做為現在的趨勢,許多專案都是用 TypeScript 撰寫,強型別能夠帶來非常多的好處,能讓前端的應用更為穩健,可維護性也更高。但是 TypeScript 也是有許多需要規範的寫法,所以我們需要另外設置 @typescript-eslint/eslint-plugin ,讓它幫我們糾察出 TypeScript 程式碼不符合規範的地方。

你可能會聽過 TSLint 這個套件,但是現在如果上 GitHub 上看 TSLint 這個 repo,它已經被 archived 了,主要是因為 ESLint 的處理效能比 TSLint 更好,而且許多專案都仍然用 ESLint,所以最後官方團隊就把 TSLint 移植到 @typescript-eslint/eslint-plugin 上面了。

首先,我們使用以下指令安裝套件:

yarn add -D @typescript-eslint/eslint-plugin

然後,同樣在 .eslintrc.json 設定 @typescript-eslint/eslint-plugin

{
  "extends": [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
}

使用 husky + lint-staged 在 git commit 時執行 ESLint 與 Prettier

husky (哈士奇) 是一個容易設定 git hook 的套件,像是我們希望在 pre-commit 時觸發 eslint 與 prettier,就可以透過 husky 來幫我們執行。然而,如果 prettier 跟 ESLint 每次 commit 時都需要處理在專案中所有的檔案,將會耗費非常多的時間在這些事情上,我們當然不會希望這樣的情況發生,難以想像這樣的開發流程。

所以,為了讓開發體驗更好,希望在 commit 時一樣可以讓 husky 幫我們觸發 git hook,但可以只針對在 stage 的檔案執行 ESLint 與 prettier。廣大的社群已經想過一樣的情況,有相對應的解決方案,那就是 lint-staged 這個套件。

我們使用以下指令安裝 husky 跟 lint-staged 這兩個套件:

yarn add -D husky lint-staged

在安裝完畢後,接著初始化 hucky 的環境:

yarn husky install

你會在專案資料夾中看到多了 .husky 這個資料夾,在 hucky 7 以後設定 git hook 的方式不太一樣,以前在前端我們可以寫在 package.json 裡面,但後來變成放在 .hucky 資料夾裡面,好處是讓設定更加彈性,可以直接在裡面寫 shell script。

接著,我們想要在 commit 時觸發 prettier 跟 eslint 兩個功能,可以使用以下指令建立 git hook,在 pre-commit 的時候會執行 lint-staged 這個指令,並且只針對在 git stage 的檔案做處理:

yarn husky add .husky/pre-commit "yarn lint-staged"

最後,我們需要設定 lint-staged 會執行哪些指令,可以直接在 package.json 這個檔案中設定跑已經設定好的 prettier 指令,以及 eslint 指令。

package.json

{
  "lint-staged": {
    "**/*.{ts,tsx,js,jsx}": [
      "yarn prettier",
      "eslint"
    ]
  }
}

也許你會想問為什麼這邊不是用 yarn lint 這個指令,yarn lint 對應是 next lint ,但我們不能使用它,而是要用原生的 eslint

這是一個存在於 next.js 官方的 GitHub issue ,在 next.js 11.1.2 版本之前在 lint-staged 使用 yarn lint 會發生錯誤,目前在 11.1.3 版可以用原生的 eslint 指令解決,未來還需要等待更新的版本發布, 也許才能解決 lint-staged 不能執行 next lint 的問題。

小結

以上就是在 next.js 11.1.2 版本下設定 ESLint + Prettier + TypeScript + husky 的流程,現在我們知道了如何在專案中加入這些設定,讓團隊開發時能夠寫處更好維護的程式碼,讓程式幫我們維護程式。

Reference


上一篇
Day18 - 如何在頁面中預先載入其他的頁面 (prefetch)
下一篇
Day20 - 提開發者體驗 (DX),使用 path alias
系列文
從零開始學習 Next.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言