從零構建前端 Lint 工作流
-
eslint prettier stylelint husky lint-staged typescript eslint-config-alloy
為構建專案程式碼質量保駕護航。 - 傳承 ESLint 推崇的外掛化、配置化的理念,滿足個性化需求(即讓專業的工具做擅長的事)因此,各位可以選擇自己需要的功能進行整合。
- 文章只發表于思否(不喜歡到處copy)各位有緣的老鐵,點贊收藏支援一波哈(看到點贊收藏關注,有一種小鹿亂撞的美好)
選擇性閱讀
- 新手建議從頭開始,都是手把手,按步驟配置一遍
- 有 ESLint 使用經驗的,直接上 AlloyTeam 配置
- 讓 ESLint 檢查 TypeScript
- VSCode 整合 ESLint 檢查 => 自動提示/修復錯誤
- 結合 Prettier => 格式化檔案
- 繼續整合 Stylelint => 檢查樣式
- 本地 Lint 已構建,缺少 Git 提交程式碼預檢
什麼是程式碼檢查
- 程式碼檢查主要是用來發現程式碼錯誤、統一程式碼風格。
- 在 JavaScript 專案中,我們一般使用 ESLint 來進行程式碼檢查,它通過外掛化的特性極大的豐富了適用範圍,搭配 typescript-eslint 之後,甚至可以用來檢查 TypeScript 程式碼。
配置 ESLint
小試牛刀
1、新建一個資料夾,開啟命令列,npm init -y
建立package.json
2、安裝依賴npm install --save-dev eslint babel-eslint eslint-config-alloy
3、在專案根目錄下建立一個.eslintrc.js
或 .eslintrc.json
的配置檔案:
// .eslintrc.js
module.exports = {
extends: [
'alloy',
],
};
4、在專案根目錄下建立一個index.js
,複製下面內容:
var myName = 'Tom';
console.log(`My name is ${myNane}`);
5、在命令列輸入npx eslint index.js
// eslint 報錯資訊:
✖ 2 problems (2 errors, 0 warnings)
error Unexpected var, use let or const instead no-var
error 'myNane' is not defined no-undef
6、使用npx eslint index.js --fix
自動修復某些規則
// 這時 var 變成了 let
// 還剩下一個無法自動修復的錯誤
✖ 1 problem (1 error, 0 warnings)
error 'myNane' is not defined no-undef
配合 TypeScript
1、由於 ESLint 預設使用 Espree 進行語法解析,無法識別 TypeScript 的一些語法,故我們需要安裝 @typescript-eslint/parser,替代掉預設的解析器,別忘了同時安裝 typescript:
npm install --save-dev typescript @typescript-eslint/parser
2、接下來需要安裝對應的外掛 @typescript-eslint/eslint-plugin 它作為 eslint 預設規則的補充,提供了一些額外的適用於 ts 語法的規則。
npm install --save-dev @typescript-eslint/eslint-plugin
3、修改配置檔案
module.exports = {
extends: [
'alloy',
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
// 禁止使用 var
'no-var': "error",
// 優先使用 interface 而不是 type
'@typescript-eslint/consistent-type-definitions': [
"error",
"interface"
]
}
}
- 以上配置中,我們自定義了兩個規則,其中
no-var
是 ESLint 原生的規則(我們剛剛已經用到了這個規則,它被包含在alloy
中,此處會覆蓋),@typescript-eslint/consistent-type-definitions 是 @typescript-eslint/eslint-plugin 新增的規則 - 規則的取值一般是一個陣列(上例中的 @typescript-eslint/consistent-type-definitions),其中第一項是 off、warn 或 error 中的一個,表示關閉、警告和報錯。後面的項都是該規則的其他配置。
- 如果沒有其他配置的話,則可以將規則的取值簡寫為陣列中的第一項(上例中的 no-var)。
關閉、警告和報錯的含義如下:
- 關閉:禁用此規則
- 警告:程式碼檢查時輸出錯誤資訊,但是不會影響到 exit code
- 報錯:發現錯誤時,不僅會輸出錯誤資訊,而且 exit code 將被設為 1(一般 exit code 不為 0 則表示執行出現錯誤)
4、新建index.ts
檔案:
var myName = 'Tom';
console.log(`My name is ${myNane}`);
console.log(`My name is ${myName.toStrng()}`);
type Foo = {};
5、在命令列輸入npx eslint index.ts
,如下可以看到報錯資訊以及可修復項
1:1 error Unexpected var, use let or const instead no-var
2:27 error 'myNane' is not defined no-undef
4:6 error Use an `interface` instead of a `type` @typescript-eslint/consistent-type-definitions
✖ 3 problems (3 errors, 0 warnings)
2 errors and 0 warnings potentially fixable with the `--fix` option.
指令碼命令檢查整個專案
1、根目錄新建一個src資料夾,將我們的index.js
和index.ts
放進去
2、在package.json
中的scripts
新增:
{
"scripts": {
// 因為eslint不是全域性安裝的,所以要使用npx
"lint": "npx eslint src --ext .js,.ts,tsx"
// eslint 預設不會檢查 .ts 字尾的檔案,所以需要加上引數 --ext .ts
}
}
3、然後npm run lint
就可以看到src下所有指定字尾檔案的報錯資訊
推薦使用 AlloyTeam 的配置
- 上面手把手完成了
ESLint
的配置過程 - 有一定經驗的推薦直接使用
AlloyTeam
實現可自定義擴充的ESLint
規則 - AlloyTeam/eslint-config-alloy已經幫我們整合了各種技術棧
1、安裝技術棧相關依賴
// Eslint
npm install --save-dev eslint babel-eslint eslint-config-alloy
// React
npm install --save-dev eslint babel-eslint eslint-plugin-react eslint-config-alloy
// Vue
npm install --save-dev eslint babel-eslint vue-eslint-parser eslint-plugin-vue eslint-config-alloy
// TypeScript
npm install --save-dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-alloy
// TypeScript React
npm install --save-dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-config-alloy
2、配置.eslintrc.js
檔案
/* .eslintrc.js */
module.exports = {
extends: [
'alloy', // 都需要
'alloy/vue', //vue專案需要
'alloy/react', //react專案需要
'alloy/typescript', //ts專案需要
],
env: {
// 你的環境變數(包含多個預定義的全域性變數)
//
// browser: true,
// node: true,
// mocha: true,
// jest: true,
// jquery: true
},
globals: {
// 你的全域性變數(設定為 false 表示它不允許被重新賦值)
//
// myGlobal: false
},
rules: {
// 自定義你的規則
}
};
3、接下來就可以直接用eslint
命令檢查檔案了
4、這樣就引入了alloy團隊的lint規則了,然後可以用rules覆蓋你不爽的規則,直接採用開源規則是為了避免重複造輪子,你也可以選擇別的團隊,或者自己定義一套
VSCode 整合 ESLint 檢查
在編輯器中整合 ESLint 檢查,可以在開發過程中就發現錯誤,甚至可以在儲存時自動修復錯誤,極大的增加了開發效率
1、先安裝 ESLint 外掛,開啟 VSCode 點選「擴充套件」按鈕,搜尋 ESLint,然後安裝即可
2、在「檔案 => 首選項 => 設定 => 工作區」中(也可以在專案根目錄下建立一個配置檔案 .vscode/settings.json
),新增以下配置:
{
// VSCode 中的 ESLint 外掛預設是不會檢查 `.vue`、`.ts` 或 `.tsx` 字尾的
"eslint.validate": [
"javascript",
"javascriptreact",
"vue",
"typescript",
"typescriptreact"
],
// 開啟儲存時自動修復
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// 指定VSCode用於IntelliSense(智慧感知)的ts版本,將內建版本更換為工作區版本
"typescript.tsdk": "node_modules/typescript/lib"
}
結合 Prettier 使用
Prettier 是一個程式碼格式化工具,相比於 ESLint 中的程式碼格式規則,它提供了更少的選項,但是卻更加專業。
AlloyTeam 推薦用 Prettier 管理格式化相關的規則,用 ESLint 來檢查它更擅長的邏輯錯誤。
配置 Prettier
1、安裝 Prettier
npm install --save-dev prettier
2、配置 .prettierrc.js
僅供參考:
// .prettierrc.js
module.exports = {
// 一行最多 100 字元
printWidth: 100,
// 使用 4 個空格縮排
tabWidth: 4,
// 不使用縮排符,而使用空格
useTabs: false,
// 行尾需要有分號
semi: true,
// 使用單引號
singleQuote: true,
// 物件的 key 僅在必要時用引號
quoteProps: 'as-needed',
// jsx 不使用單引號,而使用雙引號
jsxSingleQuote: false,
// 末尾不需要逗號
trailingComma: 'none',
// 大括號內的首尾需要空格
bracketSpacing: true,
// jsx 標籤的反尖括號需要換行
jsxBracketSameLine: false,
// 箭頭函式,只有一個引數的時候,也需要括號
arrowParens: 'always',
// 每個檔案格式化的範圍是檔案的全部內容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要寫檔案開頭的 @prettier
requirePragma: false,
// 不需要自動在檔案開頭插入 @prettier
insertPragma: false,
// 使用預設的折行標準
proseWrap: 'preserve',
// 根據顯示樣式決定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 換行符使用 lf
endOfLine: 'lf'
};
VSCode 整合 Prettier
1、在.vscode/settings.json
中新增配置:
{
// 儲存時自動格式化所有支援檔案 javascript/javascriptreact/typescript/typescriptreact/json/graphql
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
}
2、這時我們儲存檔案的時候,已經可以自動格式化了
3、也可以指定格式化檔案型別:
{
// Set the default
"editor.formatOnSave": false,
// Enable per-language
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
}
繼續整合 Stylelint
顧名思義,Stylelint
幫我們解決類css檔案樣式問題
Stylelint 規則分為三個類別
- Possible errors: 可以使用stylelint-config-recommended啟用這些規則
- Stylistic issues: stylelint-config-standard擴充了Possible errors,並啟用此類的規則
- Limit language features: 其他規則,如果有需要,可以在rules裡面配置
- 詳盡的配置規則
Stylelint 配置
1、安裝依賴
npm install --save-dev stylelint stylelint-config-standard stylelint-order
2、在專案根目錄中建立一個.stylelintrc.js
配置檔案:
module.exports = {
extends: [
'stylelint-config-standard',
],
plugins: ['stylelint-order'],
rules: {
// ...
}
};
-
stylelint-config-standard
是stylelint
的推薦配置 -
stylelint-order
是 css 屬性排序外掛
3、npx stylelint "**/*.css"
嘗試檢查 css 檔案
支援 SCSS
1、安裝依賴
npm install --save-dev stylelint-config-sass-guidelines stylelint-scss
2、調整.stylelintrc.js
配置檔案:
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-sass-guidelines',
],
plugins: ['stylelint-order', 'stylelint-scss'],
rules: {
// ...
}
};
3、更多配置及外掛
VSCode 整合 Stylelint
1、在.vscode/settings.json
中新增配置:
{
"editor.codeActionsOnSave": {
// 開啟儲存自動修復所有stylelint可修復的選項
"source.fixAll.stylelint": true
},
}
2、這時我們儲存檔案的時候,Stylelint
已經可以自動修復,但是我們上面讓Prettier
負責了所有檔案的格式化,因此可能會導致衝突,倒騰了好久VSCode
配置項,效果並不好,比如去除了 css 等檔案的格式化,到.vue
內聯<style>
依然有問題:
{
// 去除其他外掛
"css.validate": false,
"less.validate": false,
"scss.validate": false,
// 編輯器格式化全部交給 Prettiern 配置
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// 針對某種語言,配置替代編輯器設定,取消自動儲存格式化
"[css]": {
"editor.formatOnSave": false
},
"[less]": {
"editor.formatOnSave": false
},
"[scss]": {
"editor.formatOnSave": false
},
}
3、如上還是會有問題,換個思路,讓Stylelint
禁用所有與Prettiern
有關的規則,很符合我們整篇文章「各司其職」的思想
4、安裝依賴npm install --save-dev stylelint-config-prettier
5、調整.stylelintrc.js
配置檔案:
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-sass-guidelines',
'stylelint-config-prettier'
],
plugins: ['stylelint-order', 'stylelint-scss'],
rules: {
// ...
}
};
6、至此,我們讓Prettiern
負責格式化,讓Stylelint
檢查樣式,讓ESLint
檢查語法邏輯,完成了整個前端 Lint 工作流
Git 程式碼預檢
- 上面我們配置了
ESLint、Prettier、Stylelint
整合了VSCode外掛,實現了錯誤提示和儲存自動修復 - 然而自動修復的只是小部分,如果團隊成員不按規範,依然可以將不規範的程式碼推送至遠端程式碼庫
- 我們通過Git 程式碼預檢,一定程度防止不規範的程式碼被提交
實現過程
- 待提交的程式碼
- git add 新增到暫存區
- 執行 git commit(這時進行程式碼預檢)
- husky註冊在git pre-commit的鉤子調起 lint-staged
- lint-staged 取得所有被提交的檔案依次執行寫好的任務
- 如果有錯誤(沒通過ESlint檢查)則停止任務,等待下次commit,同時列印錯誤資訊
- 成功提交後,git push推送到遠端庫
什麼是 git hook
-
git hook
就是.git資料夾的hooks下的一些鉤子函式,特定時機他們將被呼叫 - 檢視所有 git 鉤子函式:
cd .git/hooks
ls -l
// 列印如下:
total 96
-rwxr-xr-x 1 zzc staff 478 10 21 2019 applypatch-msg.sample
-rwxr-xr-x 1 zzc staff 896 10 21 2019 commit-msg.sample
-rwxr-xr-x 1 zzc staff 3327 10 21 2019 fsmonitor-watchman.sample
-rwxr-xr-x 1 zzc staff 189 10 21 2019 post-update.sample
-rwxr-xr-x 1 zzc staff 424 10 21 2019 pre-applypatch.sample
-rwxr-xr-x 1 zzc staff 1638 10 21 2019 pre-commit.sample
-rwxr-xr-x 1 zzc staff 1348 10 21 2019 pre-push.sample
-rwxr-xr-x 1 zzc staff 4898 10 21 2019 pre-rebase.sample
-rwxr-xr-x 1 zzc staff 544 10 21 2019 pre-receive.sample
-rwxr-xr-x 1 zzc staff 1492 10 21 2019 prepare-commit-msg.sample
-rwxr-xr-x 1 zzc staff 3610 10 21 2019 update.sample
-
.sample
為各個鉤子的案例指令碼,可以把sample去掉,直接編寫shell指令碼來執行。 - 而前端可以用外掛husky與pre-commit,來使鉤子生效。
husky 註冊 git hook
Requires Node >= 10 and Git >= 2.13.0.
-
husky
新老版本的配置方式和使用變化較大,老版本請自行升級,詳見 husky
1、安裝 husky
npm install husky --save-dev
2、編輯 package.json
檔案:
{
"husky": {
"hooks": {
"pre-commit": "eslint src/**/*.js"
}
},
}
3、嘗試 git commit
提交,就會先執行eslint src/**/*.js
,程式碼沒有問題才會被真正提交
4、這樣每次提交程式碼,eslint
都會檢查所有檔案,如果報錯過多,一定會崩潰
lint-staged 只 Lint 改動程式碼
lint-staged requires Node.js version 10.13.0 or later.
- v10.0.0 以後對原始暫存檔案的任何新修改都將自動新增到提交中。如果您的任務以前包含一個git add步驟,請刪除此步驟,同時執行多個git操作通常會導致錯誤,詳見 lint-staged
1、安裝 lint-staged
npm install lint-staged --save-dev
2、新增 package.json
配置:
{
"lint-staged": {
"src/**/*.js": "eslint"
}
}
3、如此husky
只負責註冊git hook
,後續操作交給lint-staged
,只對改動的檔案執行任務,而且可以很方便
地配置多條命令:
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.js": ["eslint --fix", "prettier --write"]
}
}
4、如上,我們提交程式碼之前,程式會自動修復eslint
配置,格式化prettier
配置
幾點建議
- 建議程式碼提交只做檢查和測試,攔截問題程式碼比較好,還是在儲存時,自動修復
eslint、prettier
配置,而且大部分還需要手動修復才行 - 實在緊急,也可通過
git commit -m -n "跳過程式碼程式碼預檢"
跳過檢查,慎用 - 和構建有關的包建議使用
--save-dev
安裝在專案內部 - 使用
VSCode
開啟專案,path
不要巢狀過深,可能導致Lint
工具失效 - 老版本
husky lint-staged
配置都放在package.json
中,現在eslint prettier husky lint-staged
都支援多種字尾配置檔案,建議採用.js
統一格式,也方便擴充:
統一配置檔案格式
// .eslintrc.js
module.exports = {
extends: [
'alloy',
],
};
// .prettierrc.js
module.exports = {
// 一行最多 100 字元
printWidth: 100,
// 使用 4 個空格縮排
tabWidth: 4,
// ...
};
// .huskyrc.js
module.exports = {
'hooks': {
'pre-commit': "lint-staged"
}
}
// .lintstagedrc.js
module.exports = {
"src/**/*.{js,ts}": "eslint"
}
// .stylelintrc.js
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-sass-guidelines',
'stylelint-config-prettier'
],
plugins: ['stylelint-order', 'stylelint-scss'],
rules: {
// ...
}
};
擴充示例
.huskyrc.js
// 陣列方式配置多條命令
const tasks = arr => arr.join(' && ')
module.exports = {
'hooks': {
'pre-commit': tasks([
'npm run lint',
'npm test'
])
}
}
.lintstagedrc.js
module.exports = {
// 如果超過10個暫存檔案,則在整個儲存庫上執行eslint
'**/*.js?(x)': (filenames) =>
filenames.length > 10 ? 'eslint .' : `eslint ${filenames.join(' ')}`,
"*.css": "stylelint",
"*.scss": "stylelint --syntax=scss",
// 對ts檔案執行tsc,但不傳遞任何引數
'**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit'
}