博客 / 詳情

返回

誰動了我的代碼!(協同倉庫該有的規範)🔥

前言

公司一個老項目,沒有做代碼提交前的校驗,我拿到後,想着老項目嘛也沒時間幫它弄這些,反正就是改一點點小東西;儘量跟着它的代碼風格寫,寫完提交就行;

直到某一天,又有一個人加入了進來。好傢伙,直接幹出事了。
很多個文件一起提交的,然後 commit-msg 不規範,代碼縮進也不規範,換行符也不一致,裏面還有很多沒用的代碼,可讀性極差;

當你某一天 fetch 代碼時,發現很多文件是這樣的 👆 你是不是很崩潰?
但問題終歸是得去解決的;治理污水的最好方法就是從能污水產生的地方開始整治。我來幫它加上吧。🥰

下面我將帶大家一起,先通過husky+eslint完成一個最簡單的代碼規範校驗,然後逐步優化,最後通過husky+eslint+lint-stage+commitlint+prettier實現一個強有力的限制。最後再配合commitizen,通過commitlint-config-cz+cz-customizable實現自定義的提交模板和限制規則來實現團隊最終的項目提交限制規約

eslint+prettier

這裏我為該項目適配了一套 eslit 規則,按照這套規則提交的代碼不會有衝突。
eslint 和 prettier 大家平時項目應該都用到過,應該都很熟悉了,這裏將不佔用篇幅。(想看的可以私聊我,我可以單獨出一篇 eslint 和 prettier 主題的文章)

🦄 在這篇文章中,主要講解如何在團隊協同工作時,在 git 提交代碼更改前,對不規範的代碼和提交信息進行校驗,修復,並限制不規範的提交。

husky

首先要介紹的是husky,搞工程化,我們肯定都少不了 husky,它能很方便的幫我們阻擋小可愛們的進攻,不,是為我們的項目添加git hooks

具體方法

首先我們將 husky 安裝到開發依賴中

npm i husky -D
# or
yarn add husky -D

注意

目前我所安裝的版本是husky@7.0.4,由於husky@6.0.0 後做了breaking change,所以在6.0.0版本之前的那種設置鈎子的方法已經不適用了,這裏我們只介紹最新的方式

安裝完後,我們需要在當前項目中創建一個.husky目錄並指定該目錄為 git hooks 所在的目錄。

使用以下命令快速創建 👇

#--no-install 參數表示強制npx使用項目中node_modules目錄下的husky依賴包
npx --no-install husky install

為了讓其他人在此項目中安裝依賴後也能自動創建.husky目錄並指定該目錄為 git hooks 所在的目錄,我們需要在 package.json 裏面添加一條腳本"prepare": "husky install"

使用以下命令快速添加 👇

npm set-script prepare "husky install"
prepare 腳本會在 npm i或者其他yarn or yarn add 之後自動執行。也就是説當我們安裝依賴後會自動執行 husky install 命令,從而自動創建.husky目錄並指定該目錄為 git hooks 所在的目錄。

使用以下命令快速創建 👇

npx --no-instal husky add .husky/pre-commit "npm run [你要執行的命令]"

完成後可以看到.husky目錄下新增了一個pre-commit文件,其中的內容為 👇

這裏我用的是npm run lint,這樣我們就可以配合 Eslint 的代碼校驗,來限制不規範代碼的提交了


可以看到,不符合 Eslint 校驗規則的代碼是沒法提交的;

當然,這裏的報錯問題只是由於縮進不規範引起的,類似這種的問題還有引號,句尾分號,換行符等等...都可以通過 eslint 的參數 --fix來自動修復,這樣就可以在提交前,先將能實現自動修復的簡單代碼風格問題後 commit。複雜的情況還是要自己去手動處理的。

説到換行符,這裏我們需要了解的是:在 Windows 上默認的是回車換行(Carriage Return Line Feed, CRLF),然而,在 Linux/MacOS 上則是換行(Line Feed, LF)。

我們可以試一下將原先換行符為crlf的文件格式化為換行符為lf後,執行git add .的情況。

可以看到最終 LF 換行符還是被 CRLF 轉化了;

如果你們不會跨平台協作(都在 Mac/Linux,或者都在 Windows 上協同),只需要在當前項目中通過git config core.autocrlf false來阻止這種情況的發生。

為了保險起見,你需要新建一個.gitattributes文件(主要用於定義每種文件的屬性,以方便 git 幫我們統一管理。),設置 eol(end of line)為指定的換行符(lf/crlf),這裏我把所有文件*.*的換行符都設置為了 LF,並且將一些非文本文件進行了標記(排除它們),你也可以對每一種文件類型分別設置對應的屬性 => *.js eol=lf*.ts eol=lf...

*.* eol=lf
*.jpg -text
*.png -text
  ...
# 或者👇

*.js eol=lf
*.ts eol=lf
*.json eol=lf
  ...

文件內容如下 👇

image.png
這樣,我們不管在什麼平台上開發,文件換行符都統一為 LF。

可以看到使用.gitattributes配置文件後執行git add,所有不是指定換行符的文件都會被自動更換為你指定的換行符,例如我這裏指定了lf,那麼 git add . 後,不是以 lf 換行的文件都會被轉換為 lf ,並在終端輸出warning: CRLF will be replaced by LF in xxxx/filename,如圖 👇

.gitattributes有很多用處,具體可以查看 👉gitattributes。

🥰 到這裏,一個最簡單的代碼風格限制方法就已經實現了。

既然做了,就肯定要做一套完整的,且好用的。下面我們來繼續完善其他功能 ~


lint-staged

什麼是lint-staged?顧名思義,藉助這個工具只是用來檢查 git 暫存區文件的,就是在你git add file1,2,3... 後的暫存區文件中運行 lint 的一個工具。

每次提交一兩個文件,卻都要 lint 所有文件話,是很沒有必要的,我們只對需要提交的代碼進行 lint,這樣可以減少很多沒必要的時間開銷。(如果你每次修改一個文件,都要去 lint 所有文件,這個工具對你來説就沒有什麼意義了,husky 就管夠 🤐)

使用方法

我們將.husky/pre-commit中之前寫代碼改為 👇

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
- npm run lint
+ npx --no-install lint-staged

然後在 package.json 中添加以下代碼,lint-staged對象中採用鍵值對的方式進行配置,鍵名是你想處理的單個文件或一個文件類型,多個類型可以寫在{}中,用逗號分隔;鍵值是一個數組,數組中為 lint 時需要執行的命令組。

{
  "lint-staged": {
    "*.{ts,js,vue}": ["eslint", "echo '沒問題!'"]
  }
}

添加玩上述代碼後,我們通過測試,將兩個文件的縮進改為不符合規範的情況,然後將其中一個文件暫存後,我們運行git commit後發現終端的報錯中,只有一個文件的 lint 報錯信息,另一個文件的報錯並沒有出現。

當所有暫存區代碼都符合規範時 👇,才會通過校驗執行提交。

✔ Preparing lint-staged...
✔ Hiding unstaged changes to partially staged files...
✔ Running tasks for staged files...
✔ Restoring unstaged changes to partially staged files...
✔ Cleaning up temporary files...

commitizen

Commitizen 是一個撰寫符合上面 Commit Message 標準的一款工具。通過它可以實現交互式撰寫規範的 Commit Message。

如果只在本倉庫使用 👇

npm install commitizen -D

如果你想全局都用 commitizen 來幫你做 commit

npm install commitizen -g

安裝完成後,一般我們都採用符合 Angular 的 Commit message 格式的提交規範(當然也可以自定義,後面會講到~),運行以下命令生成符合 Angular 提交規範格式的 Commit message。

如果你項目用的是 npm 👇

# 如果你項目用的是npm
npx --no-install commitizen init cz-conventional-changelog --save-dev --save-exact

如果你項目用的是 yarn 👇

# 如果你項目用的是yarn
npx --no-install commitizen init cz-conventional-changelog --save-dev --save-exact

運行了上述命令後,它將為你項目安裝 cz-conventional-changelog 適配器模塊,把 config.commitizen 的密鑰添加到文件的根目錄添加到 package.json

可以在package.json 中看到,自動的新增了以下內容 👇

{
  ...
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  }
  ...
}

完成後,通過命令yarn cz,你如果是全局安裝的 commitizen,那你直接 git cz,都可以通過以下交互式的撰寫 commit messag 然後提交

限制 commitlint

由於 commitizen 並不是強制使用的,仍然可以通過git commit來提交,所以我們必須在不管是通過cz還是git commit提交前,都要對 commit messag 進行一次校驗,不符合規範的情況下是不允許進行 commit 的

首先我們需要安裝commitlint,commitlint/config-conventional

yarn add @commitlint/cli @commitlint/config-conventional -D

使用以下命令快速創建 git hooks 的 commit-msg 鈎子 👇
這樣每次 commit 的時候都會由 commitlint 對 commit message 進行一次檢驗

npx --no-instal husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

然後我們創建一個 commitlint 配置文件到項目根目錄 👇

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

以上將會在項目目錄中生成 commitlint.config.js,代碼如下,他將繼承@commitlint/config-conventional中的 Commit message 規範。("feat", "fix", "perf", "refactor"...)

module.exports = {
  extends: ["@commitlint/config-conventional"],
};

然後我們在終端進行測試

echo 'feat: bar' | npx --no-install commitlint

如果你執行上面這個行命令後出現了以上這種報錯。

將文件更改問 UTF-8 的格式即可解決;這個問題目前已經在 👉 Issues中,有不少人遇到了(我也是 😂)。
最簡單的方法就是,把文件用記事本打開,選擇另存為,然後在彈窗的右下角更改字符編碼為 UTF-8 (Windows 用户運行echo "xxx" > xx.js時,文件編碼可能為 UTF-16 LE),更改好後,把原來的commitlint.config.js文件替換掉即可。

解決以上問題後,我們再測試一下,可以看到,不符合規範的 commit-msg 是會導致報錯的,也就 commit 不了了,説明我們的 commitlint 已經生效了~ 👏👏👏

到此,commit-msg 的校驗也已經完成 ✔

如果,你想自定義 commitlint 的交互文本(不用 feat,fix...,很多人都喜歡在 commit message 前面加一個 emoji 表情符號),當然也可以。

我們需要安裝 cz-customizable來實現自定義 commit message 規則,以及安裝對應的commitlint-config-cz來配套校驗(直接從自定義的文件裏讀取規則)

運行以下命令 👇

yarn add commitlint-config-cz  cz-customizable -D

在項目根目錄,創建一個.cz-config.js文件,並複製cz-config-EXAMPLE.js 中的內容到其中。然後改成你自己想要的規則即可。

當然,你也可以用我寫好的:

module.exports = {
  types: [
    { value: "feat", name: "feat: 一個新的特性" },
    { value: "fix", name: "fix: 修復一個Bug" },
    { value: "docs", name: "docs: 變更的只有文檔" },
    { value: "style", name: "style: 代碼風格,格式修復" },
    { value: "refactor", name: "refactor: 代碼重構,注意和feat、fix區分開" },
    { value: "perf", name: "perf: 碼優化,改善性能" },
    { value: "test", name: "test: 測試" },
    { alue: "chore", name: "chore: 變更構建流程或輔助工具" },
    { value: "revert", name: "revert: 代碼回退" },
    { value: "init", name: "init: 項目初始化" },
    { value: "build", name: "build: 變更項目構建或外部依賴" },
    { value: "WIP", name: "WIP: 進行中的工作" },
  ],
  scopes: [],
  allowTicketNumber: false,
  isTicketNumberRequired: false,
  ticketNumberPrefix: "TICKET-",
  ticketNumberRegExp: "\\d{1,5}",
  // it needs to match the value for field type. Eg.: 'fix'
  /*
  scopeOverrides: {
    fix: [
      {name: 'merge'},
      {name: 'style'},
      {name: 'e2eTest'},
      {name: 'unitTest'}
    ]
  },
  */
  // override the messages, defaults are as follows
  messages: {
    type: "選擇一種你的提交類型:",
    scope: "選擇一個scope (可選):",
    // used if allowCustomScopes is true
    customScope: "Denote the SCOPE of this change:",
    subject: "簡短説明(最多40個字):",
    body: '長説明,使用"|"換行(可選):\n',
    breaking: "非兼容性説明 (可選):\n",
    footer: "關聯關閉的issue,例如:#12, #34(可選):\n",
    confirmCommit: "確定提交?",
  },
  allowCustomScopes: true,
  allowBreakingChanges: ["feat", "fix"],
  // skip any questions you want
  skipQuestions: ["scope", "body", "breaking"],
  // limit subject length
  subjectLimit: 100,
  // breaklineChar: '|', // It is supported for fields body and footer.
  // footerPrefix : 'ISSUES CLOSED:'
  // askForBreakingChangeFirst : true, // default is false
};

創建完.cz-config.js文件後,我們需要回到 package.json 文件中,將 config.commitizen.path 更改為"node_modules/cz-customizable",如果你的.cz-config.js文件在項目根目錄下,那麼可以不配置下面這條,commitlint-config-cz 會自動在項目根目錄下尋找: .cz-config.js.config/cz-config.js

...
{
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"
    },
    // 如果你的.cz-config.js文件在項目根目錄下,那麼可以不配置下面這條,commitlint-config-cz會自動在項目根目錄下尋找: .cz-config.js 或 .config/cz-config.js
    "cz-customizable": {
      "config": "你的文件路徑/xxxconfig.js"
    }
  }
}
...

關於commitlint-config-cz更高級的用法可以查看 👉commitlint-config-cz

最後我們將之前創建過的commitlint.config.js中的代碼進行更改 👇

module.exports = {
- extends: ["@commitlint/config-conventional"],
+ extends: ["cz"],
};

或者你也可以在commitlint.config.js中手動添加自定義的規則 👇,他將覆蓋 extends 中的規則

module.exports = {
  extends: ["@commitlint/config-conventional","cz"],
  rules: {
    "type-enum": [
      2,
      "always",
      [
        "init",
        "build",
        "ci",
        "chore",
        "docs",
        "feat",
        "fix",
        "perf",
        "refactor",
        "revert",
        "style",
        "test",
      ],
    ],
  },
};

到這裏,自定義的 commit message 的校驗也 ok 了 ✅

最後

提醒:項目的代碼風格和規則要和團隊一起制定哦 ~

至此,在團隊協同的項目中,不符合規範的提交就被扼殺在搖籃裏面了。我們大家不管是從書寫代碼還是提交代碼最好都要規範哦~ 不給自己惹麻煩的同時,也不會給他人或公司帶來麻煩。這就是本篇的全部內容啦~如果對你有幫助,記得點贊鼓勵 ~

我是榮頂,一個面向快樂編程的前端開發 🥰
如果你也非常熱愛前端相關技術!掃描二維碼~ 加入前端超人技術交流羣 🦄

回覆 [加羣],將拉你進學習交流羣,與其他前端愛好者共同進步!
回覆 [書籍],獲取大量前端pdf書籍。
朋友圈不定期舉行送書活動。一起加油,衝!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.