SSShooter

SSShooter

Write like you're running out of time.

前端程式碼品質與團隊協作終極指南

注意事項#

本文預設開發環境是 VSCode,團隊內盡可能統一開發環境,避免編輯器差異、插件不相容的問題。

以下提到的工具核心都是配置檔,並且這些配置檔通常都能透過多種格式配置,例如 .js.yaml.json 或者直接在 package.json 中配置。後面不再贅述相關設定,具體配置形式可以在官方文檔查詢,本文僅解析部分配置項目。

程式碼風格#

程式碼風格一致是專案協作的基石,使用 ESLint 和 Prettier 可以避免由於程式碼格式不一致帶來的程式碼合併衝突,也可以提高程式碼可讀性和可維護性。雖然在認識 ESLint 和 Prettier提到過,但是下面想要作為升級版講得更完整一點。

ESlint#

本文介紹的第一個工具是 ESlint,其功能是:

  1. 提供程式碼規範,例如:建議你使用 === 而不是 ==,建議你未修改過的變數使用 const 而不是 let 等這樣的規則
  2. 其次是格式化程式碼,控制縮排換行之類的問題

安裝#

npm install --save-dev eslint

VSCode 插件#

ESlint VSCode 插件

在專案中安裝了 ESlint 確實提供了檔案校驗和整理的介面,但實際上在寫程式碼過程中格式化總不能每次都自己調一下 api 處理當前檔案,這個時候就需要使用 ESlint 插件。安裝插件後可以透過快捷鍵調用 eslint api 處理當前檔案,甚至在保存時自動格式化當前檔案,在編碼窗口也會使用黃線和紅線標註警告和錯誤程式碼。

安裝完 ESlint 本體和 VSCode 插件你可以使用一些基本功能,但要與 React、Vue、TS 等檔案配合使用需要安裝相應插件(注意這裡說的是 ESlint 的 plugins,跟前面提到的 VSCode 插件沒有關係),後面會詳細介紹插件(plugins)相關問題。

配置#

先看一眼 ESlint 的配置檔,它用於配置和擴充 ESLint 規則,這是一個例子:

{
  "root": true,
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "parser": "@typescript-eslint/parser",
  "parserOptions": { "project": ["./tsconfig.json"] },
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/strict-boolean-expressions": [
      2,
      {
        "allowString": false,
        "allowNumber": false
      }
    ]
  },
  "ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]
}

接著詳細解析一下 extends、plugins、rules 三個配置。

Extends#

上面提到的規則有一兩百條,要每條手動配置不現實,我們可以使用 Extends。

Extends 是配置的集合,添加了 Extends 等於添加了一組配置。配置 extends 的值時可以忽略包名的 eslint-config- 前綴。

很多大廠都有自己的一套規範,例如前端程式碼規範領域著名的 airbnb,他們的配置檔是 eslint-config-airbnb,安裝之後使用時只需要這麼寫:

{
  "extends": "airbnb"
}

不過…… 其實個人建議用 eslint:recommended 或者 standard 而不是 airbnb,因為 airbnb 實屬管太多,例如 no-plusplusno-underscore-dangle 正常使用並沒有什麼問題,他也給開了,跟原來的編碼習慣差距比較大所以選擇不用了 😂。

當然,配置集合還有其他選擇,例如 eslint-config-alloy;你還可以發布自己的配置集合,其實這也是官方推薦的做法。

Plugins#

Plugins 比 Extends 更強勁,不止可以補充配置,更能新增 ESLint 自定義規則。配置 plugins 的值時可以忽略包名的 eslint-plugin- 前綴;因為 plugin 中也可以包含配置集合,使用 plugin 中的配置集合時可以使用plugin:包名/配置名 的格式,如 plugin/essential

在安裝 eslint-plugin-vue 之後可以這樣添加插件,就能在 ESLint 中新增一大堆 Vue 的規則(注意只是新增規則,並未配置規則是否使用):

{
  // ...
  "extends": ["plugin:vue/essential"],
  "plugins": ["vue"]
  // ...
}

各種語言詳細的配置這裡就不一一贅述了,eslint-plugin-xxx 的文檔一般會提供比較完善的幫助。

Rules#

在 extends 添加完規則集合之後,很可能還要根據自己習慣微調一些規則,這時候就可以在 rules 配置一些單條規則。

規則的等級有三種:

  • "off" or 0 - turn the rule off
  • "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
  • "error" or 2 - turn the rule on as an error (exit code is 1 when triggered)

配置方式大概長這樣(一些特殊規則會有其他配置項,可以在規則對應頁面獲取相關資訊):

{
  "plugins": ["plugin1"],
  "rules": {
    "eqeqeq": "off",
    "curly": "error",
    "quotes": ["error", "double"],
    "plugin1/rule1": "error"
  }
}

其他問題#

配置未生效:

在更新完 .eslintrc 卻發現新配置在 VSCode 沒有生效時,可以透過 ctrl shift P 然後選 Reload Window 快速重啟 ESlint 插件。

VSCode 保存時自動格式化配置,修改 settings.json

// settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
  // "editor.formatOnSave": true,
}

Prettier#

Prettier 是一個現代化的程式碼格式化工具。相比 ESLint,它專注於程式碼格式化,可以處理多種語言,包括 JavaScript、CSS、SCSS、markdown、yaml 等。Prettier 補全了 ESLint 只處理 js 系檔案的問題,但對於 js 這個兩者皆可處理的交集,仍然需要一些額外的相容操作。

安裝#

npm install --save-dev --save-exact prettier

注意一定要加 --save-exact,因為不同版本的 prettier 處理某些格式時會有差異,為了保證團隊全員格式相同,必須統一 prettier 版本。

VSCode 插件#

Prettier VSCode 插件

原理跟 ESLint 一樣,npm 安裝只提供 api,安裝 VSCode 插件才能在編輯器方便格式化。

配置#

By far the biggest reason for adopting Prettier is to stop all the ongoing debates over styles.

Prettier 為了讓大家少在格式上爭吵,只提供了少數配置項,這樣大家只要在這幾個項目中爭吵就可以了(誤)。下面這些是 Prettier 幾乎全部配置,檔名為 .prettierrc

{
  "arrowParens": "always",
  "bracketSameLine": true,
  "bracketSpacing": true,
  "embeddedLanguageFormatting": "auto",
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxSingleQuote": false,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": true,
  "singleAttributePerLine": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "useTabs": false,
  "vueIndentScriptAndStyle": false
}

ESlint 相容#

之前有在用 eslint 和 prettier 讓跨 IDE 協作更舒服提到可以手動讓 ESlint 和 Prettier 相容,不過那只是因為使用不同編輯器方便同步的一種方法,這裡再介紹一下用 ESlint 插件相容的方法。

要完整使用 ESLint 接替 Prettier 的工作需要 eslint-plugin-prettier。它的原理是使用 eslint-config-prettier 僅關閉所有 prettier 相關規則,然後透過插件把 Prettier 的規則轉到 ESlint 一起校驗。要做到上述全部操作只需要下面一行配置:

{
  "extends": ["plugin:prettier/recommended"]
}

因為配置 "extends": ["plugin:prettier/recommended"] 後相當於填充了以下一組配置:

{
  "extends": ["prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error",
    "arrow-body-style": "off",
    "prefer-arrow-callback": "off"
  }
}

其他問題#

特殊規則:

注意 prettier 覆蓋了的一些特殊規則,例如會預設關掉 vue/html-self-closing 等規則,請再另外根據自己需求再 rules 配置。

提交門禁#

以上是統一程式碼格式相關的工具,但沒有保證提交到程式碼庫的程式碼經過格式化,使用 husky 可以在提交前對程式碼檢查,保存提交的程式碼符合團隊規範。

Husky#

Git hooks made easy 🐶 woof!

husky 為 npm 專案提供介入 Git hook 的能力,它支持所有 Git hook,但我們一般只會用到 pre-commitcommit-msg

pre-commit 用於提交前的檢查,commit-msg 用於提交信息檢測。

# 安裝 husky
npm install husky --save-dev
# 啟用 Git hook
npx husky install
# 配置 npm 的 prepare 命令,用於依賴安裝後自動運行特定腳本
# 注意,使用 pkg 需要 npm 7 以上版本
npm pkg set scripts.prepare="husky install"

無法使用 pkg 可以直接在 package.json 配置:

{
  "scripts": {
    "prepare": "husky install"
  }
}

之後使用 husky add <file> [cmd] 的格式添加 hook 觸發的命令即可,接著介紹一下在 pre-commitcommit-msg 分別要使用的 lint-stagedcommitlint

lint-staged#

Run linters against staged git files and don't let 💩 slip into your code base!

對於新接入 ESLint 的程式碼庫,每次提交都檢測所有檔案,不通過就不允許提交的話,是不是有點過分了?使用 lint-staged 可以只校驗當前提交的檔案,讓你的專案漸進式更新程式碼風格。

安裝#

npm install --save-dev lint-staged

配置#

配置檔名 .lintstagedrc,因為 lint-staged 的配置比較簡短,可以直接寫在 package.jsonlint-staged 對象裡。

配置範例:

{
  "src/**/*.{ts,js}": ["eslint --cache --fix"],
  "src/**/*.{json,less}": ["prettier --write"]
}

husky 觸發#

# 在已安裝 husky 的前提下運行
npx husky add .husky/pre-commit "npx lint-staged"

commitlint#

commitlint 可以檢測提交信息是否符合規範。

簡單來說就是這樣的格式:type(scope?): subject,實際例子可能是這樣的:feat(blog): add comment section。詳細規範可以查看理解語義化 Commit

安裝#

npm install --save-dev @commitlint/config-conventional @commitlint/cli

配置#

配置檔名 .commitlintrc,配置範例:

{
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat', // 新功能(feature)
        'fix', // 修補bug
        'docs', // 文檔(documentation)
        'style', // 格式(不影響程式碼運行的變動)
        'refactor', // 重構(即不是新增功能,也不是修改bug的程式碼變動)
        'test', // 增加測試
        'revert', // 回滾
        'chore', // 構建過程或輔助工具的變動
        'perf', // 性能優化
        'types' // typescript類型定義檔更改
      ]
    ],
    'type-case': [0],
    'type-empty': [0],
    'scope-empty': [0],
    'scope-case': [0],
    'subject-full-stop': [0, 'never'],
    'subject-case': [0, 'never'],
    'header-max-length': [0, 'always', 72]
  }
}

可以在 Github 查看更完整的填寫範例

husky 觸發#

# 在已安裝 husky 的前提下運行
npx husky add .husky/commit-msg  'npx --no -- commitlint --edit ${1}'

包管理器#

使用固定包管理器,一般選擇有以下幾種:

想快速了解幾種管理器的差異可以查看《速通 npm、yarn、pnpm》。同時強烈推薦《JavaScript package managers compared: npm, Yarn, or pnpm?》,講得全面且清楚,下面貼一張來自此文的性能對比圖:

npm,yarn,pnpm performance

可以按照喜好和相容性自行選擇,但是無論選擇哪個管理器,非必要時,不要刪除 lock 檔!!! 在安裝依賴時,管理器需要計算依賴版本,很耗時,有 lock 可以直接按 lock 列表安裝。

但是有 lock 的時候,依賴的下載地址是固定的,所以配倉庫會不生效,持續集成時需要注意。

P.S. 不同管理器在配 husky 的時候可能有些差異

引擎版本#

如果保留了依賴的 lock 檔,那麼記錄當前應用適配的 node 版本也是十分必要的,不同 node 版本會造成下載的依賴不一樣、甚至依賴根本不適配當前 node 版本;另外,還有一些舊專案,必須用舊的 node 版本才能運行,但是接手的時候根本不知道用的那個版本,就十分讓人無奈。

為解決這個問題,我們可以在 package.json 指定 node 和 npm 版本:

{
  "engines": {
    "node": ">=0.10.3 <15",
    "npm": "~1.0.20"
  }
}

團隊人多、專案多時,很可能出現大家版本不一樣的情況,在必要時,可以使用 node 版本管理器,這裡推薦三款:

基本上可以做到一行命令安裝版本、一行命令切換版本,十分方便。

.npmrc#

相信檔名帶著 rc 已經很熟悉了,.npmrc 作用就是添加專案級別的 npm 配置,例如:

  • 改倉庫,有的公司有自己的鏡像,可以在 .npmrc 配置,不用每次都在安裝的時候帶一串參數,也不會影響全局配置
  • 還可以添加一些二進制包的下載地址,例如那個煩人的 sass
registry=https://mirrors.huaweicloud.com/repository/npm/
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

分支策略#

選擇一種分支策略,可以選擇以下三種策略,也可以以這些策略為基礎調整出一個最適合自己團隊的策略:

總結#

  • 使用 ESlint 和 Prettier 控制程式碼規範和程式碼風格
  • 添加 VSCode 插件极速格式化當前檔案
  • 添加提交門禁,保證上傳到版本控制的程式碼符合規範
  • 使用 husky 添加提交鉤子,控制提交是否成功
  • lint-staged 用於只檢查本次提交檔案是否符合要求,利於專案漸進式控制程式碼規範
  • commitlint 保證提交信息可讀性
  • 團隊內使用統一的包管理器,保留依賴 lock 檔
  • 固定引擎版本,防止依賴變化,也能避免舊專案不知道使用什麼版本運行的窘境
  • 使用 .npmrc 固定倉庫等配置
  • 選擇一種分支策略,使程式碼歷史更有序

各位看完覺得有遺漏或者有什麼不懂我再補充!

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。