從 0 到 1 搭建一個(gè)企業(yè)級(jí)前端開(kāi)發(fā)規(guī)范

來(lái)源:大古同學(xué)
https://juejin.cn/post/6947872709208457253
前端代碼規(guī)范對(duì)于團(tuán)隊(duì)而言至關(guān)重要,既可以提高代碼的可維護(hù)性,也可以降低代碼多人維護(hù)的成本
那如何搭建一個(gè)規(guī)范的前端項(xiàng)目基礎(chǔ)呢?接下來(lái)讓我告訴你
創(chuàng)建一個(gè)基礎(chǔ)項(xiàng)目
使用 npm init \-y 初始化一個(gè)前端項(xiàng)目,這會(huì)自動(dòng)生成package.json 文件。當(dāng)我們安裝項(xiàng)目依賴的時(shí)候,這個(gè)文件會(huì)自動(dòng)更新
接下來(lái)我們創(chuàng)建以下文件目錄
└── src/
├── index.ts // 項(xiàng)目入口文件
├── package.json
添加 TypeScript
我們?yōu)槭裁葱枰?nbsp;TypeScript 呢?
TypeScript 會(huì)在編譯代碼時(shí),進(jìn)行嚴(yán)格的靜態(tài)類型檢查。意味著可以在編碼階段發(fā)現(xiàn)存在的隱患,而不用把隱患帶到線上去 TypeScript 會(huì)包括來(lái)自 ES6 和未來(lái)提案中的特性,比如異步操作和裝飾器,也會(huì)從其他語(yǔ)言借鑒特性,比如接口和抽象類 TypeScript 編譯成 JavaScript 后,可以在任何瀏覽器/操作系統(tǒng)上運(yùn)行。無(wú)需任何運(yùn)行時(shí)的額外開(kāi)銷 TypeScript 接口定義后,可以充分利用 VSCode 的自動(dòng)補(bǔ)全/自動(dòng)提示功能.因此可以直接代替文檔,同時(shí)可以提高開(kāi)發(fā)效率,降低維護(hù)成本
接下來(lái)我們?cè)?CLI 中安裝 TypeScript
yarn add typescript --dev
然后進(jìn)行 TypeScript 配置!在項(xiàng)目根目錄通過(guò)tsc \--init命令來(lái)創(chuàng)建 tsconfig.json 文件并替換為以下內(nèi)容
有關(guān) TypeScript 的詳細(xì)配置可以查看這篇文章
{
"compilerOptions": {
"module": "esnext",
"target": "esnext",
"lib": ["esnext", "dom"],
"baseUrl": ".",
"jsx": "react-jsx",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"paths": {
"@/*": ["./src/*"]
},
"noEmit": true
},
"include": [
"src/**/*",
"typings/**/*",
"config/**/*",
".eslintrc.js",
".stylelintrc.js",
".prettierrc.js"
],
"exclude": ["node_modules", "build", "dist"]
}
在我們的React Build項(xiàng)目中, 使用 Webpack 的 Babel 對(duì)項(xiàng)目代碼進(jìn)行編譯,因此使用 TypeScript 的唯一目的僅僅是對(duì)項(xiàng)目代碼進(jìn)行類型檢查。
因?yàn)?nbsp;tsconfig.json 中的編譯選項(xiàng)僅僅針對(duì)代碼類型檢查,而不是代碼編譯,因此不需要讓 TypeScript 生成編譯文件
以下是tsconfig.json中一些設(shè)置的解釋
lib: TS 需要引用的庫(kù),即聲明文件,ES5 默認(rèn) dom,es5,scripthostallowJs: 允許編譯 JS 文件(js,jsx)allowSyntheticDefaultImports: 允許從沒(méi)有設(shè)置默認(rèn)導(dǎo)出的模塊中默認(rèn)導(dǎo)入. 參考文檔esModuleInterop: 參考文檔skipLibCheck:忽略所有的聲明文件( *.d.ts)的類型檢查strict:開(kāi)啟所有嚴(yán)格的類型檢查.如果 strict=true,則 所有 strict 相關(guān)的配置都應(yīng)該為 trueforceConsistentCasingInFileNames:禁止對(duì)同一個(gè)文件的不一致的引用.例如:引用文件時(shí)大小寫(xiě)必須一致moduleResolution:使用哪種模塊解析策略.參考文檔resolveJsonModule:是否可以導(dǎo)入 JSON 模塊.參考文檔isolatedModules:每個(gè)文件必須是模塊.參考文檔noEmit:不生成輸出文件jsx: 支持 JSX.參考文檔include:編譯器需要編譯的文件或者目錄
添加 ESLint 代碼規(guī)范校驗(yàn)
ESLint 可以幫助我們找出有問(wèn)題的編碼模式或不符合規(guī)則的代碼
有關(guān) ESLint 的詳細(xì)討論可以查看這篇文章
OK!讓我們開(kāi)始安裝 ESLint 的相關(guān)依賴
yarn add eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/parser @typescript-eslint/eslint-plugin --dev
以下是一些 ESLint 依賴的解釋
eslint: ESLint 核心庫(kù)eslint-plugin-react: React 代碼規(guī)范的校驗(yàn)規(guī)則react/jsx-key:用來(lái)檢查是否聲明了 key 屬性no-array-index-key:用來(lái)檢查是否使用了數(shù)組索引聲明 key 屬性....其他 React 相關(guān)規(guī)范 eslint-plugin-react-hooks:React hooks 代碼規(guī)范的校驗(yàn)規(guī)則rules-of-hooks: 用來(lái)檢查 Hook 的規(guī)則(不能 if/循環(huán)中使用 Hooks) exhaustive-deps 規(guī)則,此規(guī)則會(huì)在 useEffct添加錯(cuò)誤依賴時(shí)發(fā)出警告并給出修復(fù)建議@typescript-eslint/parser:將 TypeScript 代碼納入 ESLint 校驗(yàn)范圍@typescript-eslint/eslint-plugin:TypeScript 代碼規(guī)范的校驗(yàn)規(guī)則
在根目錄創(chuàng)建.eslintrc.json文件并加入以下內(nèi)容
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "react-hooks"],
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}
我們?cè)?ESLint 配置文件中做了下面的事情
將 @typescript-eslint/parser作為ESLint 解析器使用 plugin:react/recommended/plugin:@typescript-eslint/recommended作為基本規(guī)則集添加了兩個(gè) React Hooks 規(guī)則,并取消了 react/prop-types 規(guī)則,因?yàn)?prop 類型與 React 和 TypeScript 項(xiàng)目無(wú)關(guān)。 關(guān)閉了 explicit-module-boundary-types,Typescript 中,必須明確指定函數(shù)的返回值類型。并且函數(shù)中的return后的類型必須與指定的類型一致 參考文檔
下面是一個(gè) "explicit-module-boundary-types" 規(guī)則的栗子
// 會(huì)出現(xiàn) explicit-module-boundary-types警告
export default function () {
return 1;
}
// 下面的函數(shù)不會(huì)出現(xiàn)警告
export var fn = function (): number {
return 1;
};
TS 中可以通過(guò)類型推斷判斷出函數(shù)的返回值類型,因此可以關(guān)閉此 Lint
添加 NPM 腳本
{
"script": {
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx ./src",
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx"
}
}
lint-staged:js: 只校驗(yàn)后綴名為".js,.jsx,.ts,.tsx"的文件lint:js: 只校驗(yàn)src目錄下,后綴名為".js,.jsx,.ts,.tsx"的文件中,被修改過(guò)的文件。這會(huì)生成一個(gè).eslintcache文件用來(lái)緩存已校驗(yàn)過(guò)的文件lint:fix: 根據(jù).eslintcache文件,校驗(yàn)被修改過(guò)的文件。并且自動(dòng)修復(fù)
如果需要屏蔽不需要檢測(cè)的文件或目錄,可以在項(xiàng)目根目錄添加 .eslintignore
.DS_Store
node_modules
dist
build
public
添加 Prettier 代碼自動(dòng)格式化工具
Prettier 是一個(gè)代碼格式化的工具.某些與代碼校驗(yàn)有關(guān)的規(guī)則(例如,語(yǔ)句末尾是否加分號(hào))就可以由 Prettier 自動(dòng)處理。
有關(guān) Prettier 的詳細(xì)討論可以查看這篇文章
接下來(lái)我們?cè)?CLI 中安裝 Prettier
yarn add prettier --dev
在項(xiàng)目根目錄新建.prettierrc 并加入以下內(nèi)容
{
"printWidth": 80,
"singleQuote": true,
"semi": true,
"tabWidth": 2,
"trailingComma": "all"
}
為VSCode 安裝 Prettier 插件

通過(guò)在“設(shè)置”中勾選“保存時(shí)進(jìn)行格式化”選項(xiàng), 就可以在文件保存時(shí)使用 Prettier 進(jìn)行自動(dòng)格式化

如果需要屏蔽不必要的文件,可以在項(xiàng)目根目錄添加 .prettierignore并加入以下內(nèi)容
*.svg
package.json
.DS_Store
.eslintignore
*.png
*.toml
.editorconfig
.gitignore
.prettierignore
LICENSE
.eslintcache
*.lock
yarn-error.log
/build
/public
添加 npm 腳本
"script":{
"lint:prettier": "prettier --check \"src/**/*\" --end-of-line auto",
"prettier": "prettier -c --write \"src/**/*\""
}
解釋一下腳本的含義
lint:prettier:當(dāng)想要檢查文件是否已被格式化時(shí),則可以使用--check標(biāo)志(或-c)運(yùn)行 Prettier。這將輸出一條語(yǔ)義化的消息和未格式化文件的列表。上面腳本的意思是格式化src目錄下的所有文件prettier:重新格式化所有已被處理過(guò)的文件。類似于eslint --fix的工作。上面腳本的意思是重新格式化src目錄下的所有文件
添加 EditorConfig 代碼風(fēng)格統(tǒng)一工具
EditorConfig 有助于維護(hù)跨多個(gè)編輯器和 IDE 從事同一項(xiàng)目的多個(gè)開(kāi)發(fā)人員的一致編碼風(fēng)格,團(tuán)隊(duì)必備神器。
有關(guān) EditorConfig 的詳細(xì)討論可以查看這篇文章
為VSCode 安裝 EditorConfig 插件

在項(xiàng)目根目錄創(chuàng)建.editorconfig并加入以下內(nèi)容
# http://editorconfig.org
root = true
[*]
#縮進(jìn)風(fēng)格:空格
indent_style = space
#縮進(jìn)大小2
indent_size = 2
#換行符lf
end_of_line = lf
#字符集utf-8
charset = utf-8
#是否刪除行尾的空格
trim_trailing_whitespace = true
#是否在文件的最后插入一個(gè)空行
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
添加 stylelint
[stylelint][https://stylelint.io/user-guide/get-started]是一個(gè) css 規(guī)范校驗(yàn)工具,也支持 less 等 css 預(yù)處理器
有關(guān) stylelint 的詳細(xì)討論可以查看這篇文章
為VSCode 安裝 stylelint 插件

安裝依賴
yarn add stylelint stylelint-config-standard --dev
新建 .stylelintrc.js并加入以下內(nèi)容
module.exports = {
extends: "stylelint-config-standard",
rules: {
// your rules
},
// 忽略其他文件,只校驗(yàn)樣式相關(guān)的文件
ignoreFiles: [
"node_modules/**/*",
"public/**/*",
"dist/**/*",
"**/*.js",
"**/*.jsx",
"**/*.tsx",
"**/*.ts",
],
};
配置 NPM 腳本
"script":{
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
}
解釋一下腳本的含義: 自動(dòng)修復(fù)src 目錄下的所有 less 文件不規(guī)范的內(nèi)容
ESLint/Prettier/stylelint 工具庫(kù)推薦
推薦一個(gè)集成了 ESLint/Prettier/stylelint 的開(kāi)源庫(kù) umijs/fabric. 它把所有的常見(jiàn)規(guī)范都集成在了一起,而不需要開(kāi)發(fā)者再去單獨(dú)維護(hù)。引用該庫(kù)的方式也很簡(jiǎn)單
讓我們安裝該依賴
yarn add @umijs/fabric --dev
修改 ESLint/Prettier/stylelint 幾個(gè)文件的配置
// .prettierrc.js
const fabric = require("@umijs/fabric");
module.exports = {
...fabric.prettier,
};
// .stylelintrc.js
const fabric = require("@umijs/fabric");
module.exports = {
...fabric.stylelint,
};
// .eslintrc.js
module.exports = {
extends: [require.resolve("@umijs/fabric/dist/eslint")],
globals: {
// 全局變量:在全局中使用 REACT_APP_ENV時(shí) eslint就不會(huì)出現(xiàn)警告
REACT_APP_ENV: true,
},
};
注: 安裝了@umijs/fabric就不能再安裝其他 ESLint 解析器如@typescript-eslint/parser,否則會(huì)產(chǎn)生沖突
添加 Git Hook
只是單純引入代碼規(guī)范校驗(yàn)如果不強(qiáng)制執(zhí)行就等于沒(méi)做,總會(huì)有人偷懶,所以還可以加一道門(mén)檻進(jìn)行約束。
我們可以通過(guò) lint-staged 實(shí)現(xiàn)這道門(mén)檻:在 git commit 命令運(yùn)行時(shí)先校驗(yàn) lint(eslint, stylelint 等)是否通過(guò),未通過(guò)則不予提交
husky 是一個(gè) gitHook 工具,可以配置 git 的一些鉤子,本文主要用來(lái)配置 commit 鉤子
lint-staged 是一個(gè)在 git 暫存文件上運(yùn)行 lint 校驗(yàn)的工具,配合 husky 配置 commit 鉤子,用于 git commit 前的強(qiáng)制校驗(yàn)
有關(guān) Git Hook 的討論可以參考這篇文章
安裝依賴
yarn add husky lint-staged --dev
{
"scripts": {
"precommit": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.less": "stylelint --syntax less",
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
"**/*.{js,jsx,tsx,ts,less,md,json}": ["prettier --write"]
}
}
在每次 git commit 之前會(huì)進(jìn)入工作區(qū)文件掃描,自動(dòng)修復(fù) eslint/stylelint 問(wèn)題再使用 prettier 自動(dòng)格式化,最后再提交到工作區(qū)。
注:
必選先使用git init 初始化 git 倉(cāng)庫(kù),之后使用 husky 才能生效
如果預(yù)提交鉤子不生效可以參考stackoverflow:lint-staged not running on precommit
Commit Message 規(guī)范
大量的代碼提交,必然會(huì)產(chǎn)生大量的 Commit log. 每一條都 Commit log 記錄著某一階段所完成的事以及關(guān)注點(diǎn),應(yīng)該盡可能詳細(xì)具體;在工作中一份清晰規(guī)范的 Commit Message 能讓后續(xù)代碼審查、信息查找、版本回退都更加高效可靠。
Commit message 格式
<type>: <subject> 注意冒號(hào)后面有空格。
type
用于說(shuō)明 commit 的類別,只允許使用下面 7 個(gè)標(biāo)識(shí)。
feat:新功能(feature) fix:修補(bǔ) bug docs:文檔(documentation) style:格式(不影響代碼運(yùn)行的變動(dòng)) refactor:重構(gòu)(即不是新增功能,也不是修改 bug 的代碼變動(dòng)) test:增加測(cè)試 chore:構(gòu)建過(guò)程或輔助工具的變動(dòng)
如果 type 為 feat 和 fix,則該 commit 將肯定出現(xiàn)在 Change log 之中。
subject
subject 是 commit 目的的簡(jiǎn)短描述,不超過(guò) 50 個(gè)字符,且結(jié)尾不加句號(hào)(.)。
栗子
git commit -m 'feat:添加了一個(gè)用戶列表頁(yè)面'
git commit -m 'fix:修復(fù)用戶列表頁(yè)面無(wú)法顯示的bug'
git commit -m 'refactor:用戶列表頁(yè)面代碼重構(gòu)'
這里多一嘴: 網(wǎng)上有許多教程通過(guò)commitizen 實(shí)現(xiàn)強(qiáng)制的 Commit Message 格式規(guī)范,我是不贊同的。規(guī)范應(yīng)該是每個(gè)開(kāi)發(fā)者自發(fā)遵循的,如果規(guī)范過(guò)多記不住,可以通過(guò) ESLint 等強(qiáng)制執(zhí)行養(yǎng)成習(xí)慣。但對(duì)于這種簡(jiǎn)單的規(guī)范我們應(yīng)該自發(fā)遵守,更何況還有規(guī)范校驗(yàn)的最后一道門(mén)檻-團(tuán)隊(duì)代碼審查(輕則被團(tuán)隊(duì)大佬噴的體無(wú)完膚,重則卷鋪蓋走人 ??)
參考文檔
一套標(biāo)準(zhǔn)的前端代碼工作流-掘金 Ant-Design-Pro
最后
如果本文對(duì)你有幫助的話,給本文點(diǎn)個(gè)贊吧。
歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
