力薦!這些工具可以幫你寫出乾淨的程式碼
作者|Adeel Imran譯者|無明
想寫出好程式碼,卻不知道從哪裡開始?想刪除死程式碼?想在程式碼庫中找出未被使用的變數?想在程式碼中找出有問題的模式?
你是多元化團隊的負責人嗎?你的團隊中有新來的開發人員嗎?你擔心他們會寫出不符合標準的程式碼嗎?在程式碼評審時是否花了一整天的時間去檢查程式碼標準,而不是實際的邏輯實現?
我一直在做這樣的事情,經常忙得像熱鍋上的螞蟻。但從現在開始,我們要保證永遠不再擔心這類問題。在閱讀本文過程中,如果遇到困難,可以參考程式碼庫(https://github.com/adeelibr/react-starter-kit)。
本文更多地是針對 React 應用程式,但同樣適用於其他 Web 專案。
讓我們從 Prettier 開始吧 Prettier 是什麼?
Prettier 是一種程式碼格式化程式,它以特定的方式為你格式化程式碼。
請看這個 GIF:
我們為什麼需要 Prettier?1. 清理現有程式碼庫:通過單個命令列清理程式碼庫。想象一下清理超過 20,000 行程式碼的程式碼庫會是怎樣的一種情景。2. 易於適用:Prettier 在格式化程式碼時使用爭議最少的編碼風格。因為是開源的,很多人已經在修復一些邊緣情況和優化體驗方面進行了多次迭代。3. 編寫程式碼:人們沒有意識到的是,他們花了很多時間用於格式化程式碼,這浪費了他們太多的精神能量。讓 Prettier 來處理格式化的事情,開發人員就可以專注在核心業務邏輯上。Prettier 可以將效率提高 10%。4. 幫助新手:如果你是一位與優秀工程師並肩工作的新手,並且你希望自己看起來很酷,可以寫出乾淨的程式碼,那就使用 Prettier 吧。 如何設定 Prettier?
建立一個叫作 app 的資料夾,進入該資料夾,在命令列中敲入:
npm init -y
這將在 app 資料夾中建立一個 package.json 檔案。
我將在本文中使用 yarn,但你也可以使用 npm。
安裝我們的第一個依賴項:
yarn add --dev prettier
這將安裝 package.json 中指定的開發依賴項,如下所示:
{ "name": "react-boiler-plate", "version": "1.0.0", "description": "A react boiler plate", "main": "src/index.js", "author": "Adeel Imran", "license": "MIT", "scripts": { "prettier": "prettier --write src/**/*.js" }, "devDependencies": { "prettier": "^1.14.3" } }
稍後我會解釋“prettier”: “prettier — write src/**/*.js”
的作用,現在先讓我們在 app 資料夾中建立一個 src/ 資料夾。在 src/ 資料夾中,再建立一個名為 index.js 的檔案——名字可以隨意起。
在 index.js 檔案中,按原樣貼上這句話:
let person = {
name: "Yoda",
designation: 'Jedi Master '
};
function trainJedi (jediWarrion) {
if (jediWarrion.name === 'Yoda') {
console.log('No need! already trained');
}
console.log(`Training ${jediWarrion.name} complete`)
}
trainJedi(person)
trainJedi({ name: 'Adeel',
designation: 'padawan'
});
到目前為止,我們有了一個 src/app/index.js 檔案,包含了一些難看的程式碼。
我們可以做三件事:
-
手動縮排並格式化程式碼;
-
使用自動化工具;
-
保持不變(請不要這麼做)。
我打算選擇第二項,所以我們安裝了一個依賴項,並在 package.json 中聲明瞭 Prettier。
現在在 app 根資料夾中建立一個 prettier.config.js 檔案,並在其中新增一些 Prettier 規則:
module.exports = {
printWidth: 100,
singleQuote: true,
trailingComma: 'all',
bracketSpacing: true,
jsxBracketSameLine: false,
tabWidth: 2,
semi: true,
};
printWidth 將確保你的單行程式碼不會超過 100 個字元。
singleQuote 會將所有雙引號轉換為單引號。
trailingComma 將確保在最後一個物件屬性的末尾會有一個逗號。
bracketSpacing 在物件字面量之間列印空格:
If bracketSpacing is true - Example: { foo: bar }
If bracketSpacing is false - Example: {foo: bar}
jsxBracketSameLine 將在多行 JSX 元素的最後一行放置>:
// true example
<button
className="prettier-class"
id="prettier-id"
onClick={this.handleClick}>
Click Here
</button>
// false example
<button
className="prettier-class"
id="prettier-id"
onClick={this.handleClick}
>
Click Here
</button>
tabWidth 指定單個縮排的空格數。
如果 semi 設定為 true,將在語句末尾加上 ;。
現在讓我們來說說這個指令碼的作用:
“prettier”: “prettier — write src/**/*.js”
它的意思是執行 prettier,並讓它在 src/ 資料夾中查詢所有的.js 檔案。--write 標誌告訴 prettier 要把格式化好的內容儲存到檔案中,並找出格式化過程中發現的任何異常。
現在在終端中執行這個指令碼:
yarn prettier
這是我在執行程式碼時看到的:
ESLint 什麼是程式碼 linter?
程式碼 linting 是一種程式碼靜態分析,通常被用於查詢不符合某些樣式指南的有問題的模式或程式碼。大多數程式語言都有程式碼 linting,編譯器有時會在編譯過程中加入 linting。——來自 ESLint
為什麼 JavaScript 需要 linter?
由於 JavaScript 是動態型別的,而且是一種鬆散型別的語言,因此開發人員在使用這門語言時很容易犯錯。因為不經過編譯,所以通常需要在執行.js 檔案的情況下才能發現語法或其他錯誤。
像 ESLint 這樣的 linting 工具可以幫助開發人員在不執行 JavaScript 程式碼的情況下發現問題。
是什麼讓 ESLint 如此特別?
ESLint 中的所有東西都是可插拔的,你甚至可以在執行時新增規則。你新增的每個 linting 規則都是獨立的,任何一個規則都可以獨自開啟或關閉。每個規則都可以設定為警告或錯誤級別。
現在有 2 個流行的風格指南:
1. Google JavaScript Style Guide(https://google.github.io/styleguide/jsguide.html);
2. Airbnb JavaScript Style Guide(https://github.com/airbnb/javascript#table-of-contents)。
我一直在使用 Airbnb 的風格指南。這個風格指南一直有人在維護,在本文中,我將使用受 Airbnb 風格指南啟發的規則集。
首先更新 package.json 檔案:
{
"name": "react-boiler-plate",
"version": "1.0.0",
"description": "A react boiler plate",
"main": "src/index.js",
"author": "Adeel Imran",
"license": "MIT",
"scripts": {
"lint": "eslint --debug src/",
"lint:write": "eslint --debug src/ --fix",
"prettier": "prettier --write src/**/*.js"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.(js|jsx)": ["npm run lint:write", "git add"]
},
"devDependencies": {
"babel-eslint": "^8.2.3",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^17.0.0",
"eslint-config-jest-enzyme": "^6.0.2",
"eslint-plugin-babel": "^5.1.0",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-jest": "^21.18.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-react": "^7.9.1",
"husky": "^1.1.2",
"lint-staged": "^7.3.0",
"prettier": "^1.14.3"
}
}
在開始進行配置之前,先讓我們來看看每個依賴包的功能。
babel-eslint:這個包讓你可以輕鬆在 Babel 上使用 lint。如果你不使用 ESLint 尚不支援的 Flow 或實驗性功能,則不一定需要這個外掛。
eslint:這是 lint 程式碼所需的主要工具。
eslint-config-airbnb:這個包提供了所有 Airbnb 的 ESLint 配置,你可以修改它們。
eslint-plugin-babel:babel-eslint 的外掛伴侶。
eslint-plugin-import:這個外掛旨在支援 ES2015+(ES6+)的匯入 / 匯出語法,並防止出現拼寫錯誤的檔案路徑和匯入名稱。
eslint-plugin-jsx-a11y:適用於 JSX 元素可訪問性規則的 linting 規則。
eslint-plugin-prettier:讓 ESLint 與 Prettier 的使用更順暢。
eslint-plugin-react:特定於 React 的 linting 規則。
eslint-config-jest-enzyme:用於特定於 React 和 Enzyme 的全域性變數。這個 lint 配置讓 ESLint 知道有哪些全域性變數,並且不會針對它們發出警告——有點像斷言 it 和 describe。
eslint-plugin-jest:Jest 的 ESLint 外掛。
husky:在自動化部分會進行更多介紹。
lint-staged:在自動化部分會進行更多介紹。
現在我們已經有了基本的瞭解,接下來可以開始了。
在 app/ 根目錄建立.eslintrc.js 檔案:
module.exports = {
env: {
es6: true,
browser: true,
node: true,
},
extends: ['airbnb', 'plugin:jest/recommended', 'jest-enzyme'],
plugins: [
'babel',
'import',
'jsx-a11y',
'react',
'prettier',
],
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
rules: {
'linebreak-style': 'off', // Don't play nicely with Windows.
'arrow-parens': 'off', // Incompatible with prettier
'object-curly-newline': 'off', // Incompatible with prettier
'no-mixed-operators': 'off', // Incompatible with prettier
'arrow-body-style': 'off', // Not our taste?
'function-paren-newline': 'off', // Incompatible with prettier
'no-plusplus': 'off',
'space-before-function-paren': 0, // Incompatible with prettier
'max-len': ['error', 100, 2, { ignoreUrls: true, }], // airbnb is allowing some edge cases
'no-console': 'error', // airbnb is using warn
'no-alert': 'error', // airbnb is using warn
'no-param-reassign': 'off', // Not our taste?
"radix": "off", // parseInt, parseFloat radix turned off. Not my taste.
'react/require-default-props': 'off', // airbnb use error
'react/forbid-prop-types': 'off', // airbnb use error
'react/jsx-filename-extension': ['error', { extensions: ['.js'] }], // airbnb is using .jsx
'prefer-destructuring': 'off',
'react/no-find-dom-node': 'off', // I don't know
'react/no-did-mount-set-state': 'off',
'react/no-unused-prop-types': 'off', // Is still buggy
'react/jsx-one-expression-per-line': 'off',
"jsx-a11y/anchor-is-valid": ["error", { "components": ["Link"], "specialLink": ["to"] }],
"jsx-a11y/label-has-for": [2, {
"required": {
"every": ["id"]
}
}], // for nested label htmlFor error
'prettier/prettier': ['error'],
},
};
還要在 app/ 根目錄中新增.eslintignore 檔案:
/.git
/.vscode
node_modules
我們先介紹一下.eslintrc.js 檔案的作用。
先把它拆分一下:
module.exports = {
env:{},
extends: {},
plugin: {},
parser: {},
parserOptions: {},
rules: {},
};
1. env:用於預定義全域性變數。在我們的例子中,可用的環境包括 es6、browser 和 es6。es6 將啟用除模組之外的所有 ECMAScript 6 功能。browser 將新增所有瀏覽器全域性變數,如 Windows。node 將新增 Node 全域性變數和 Node 作用域,比如 global。
2. extends:字串陣列——擴充套件了之面配置的額外配置選項。現在我們正在使用 airbnb 的 linting 規則,這些規則被擴充套件到 jest,然後是 jest-enzyme。
3. plugins:外掛基本上就是我們想要使用的 linting 規則。現在我們正在使用 babel、import、jsx-a11y、react、prettier。
4. parser:預設情況下,ESLint 使用 Espree,但因為我們使用了 babel,我們還需要使用 Babel-ESLint。
5. parserOptions:如果我們將 Espree 的預設解析器更改為 babel-eslint,需要指定 parserOptions——它是必需的。我通過選項告訴 ESLint,ecmaVersion 是 6。因為我們在 EcmaScript 模組(而不是 script)中編寫程式碼,所以我們將 sourceType 指定為 module。由於我們使用了 React,引入了 JSX,所以在 ecmaFeatures 中加了 jsx 選項,並將其設定為 true。
6. rules:我們已經擴充套件並通過外掛新增的所有規則,我們可以更改或覆蓋它們。
現在介紹一下.eslintignore
.eslintignore 裡包含了我們不希望 ESLint 對它們進行 lint 的路徑列表。這裡我只指定三個:
1. /.git——我不希望 Git 相關檔案被 lint。
2. /.vscode——由於我使用的是 VS Code,這個編輯器提供了自己的配置檔案,我不希望配置檔案被 lint。
3. node_modules——我不希望依賴項被 lint,所以把這個目錄也新增到列表中。
接下來讓我們來看看 package.json 中新新增的指令碼。
"lint": "eslint --debug src/"
"lint:write": "eslint --debug src/ --fix"
$ yarn lint——執行這個命令,它將遍歷 src/ 中的所有檔案,並在每個找到錯誤的檔案中提供詳細日誌,你可以手動開啟這些檔案並更正錯誤。
$ yarn lint:write——執行這個命令,它將執行與上述命令相同的操作。不同的地方在於,如果它可以糾正它發現的錯誤,它將糾正它們,並嘗試從程式碼中儘可能多地移除程式碼壞氣味。
讓它更自動化一些
到目前為止,我們設定好了 prettier 和 eslint,但每次我們都要執行指令碼。接下來我們讓它更加自動化一些。
1. 在編輯器中按下 ctrl + s 時格式化和 lint 程式碼。
2. 每次提交程式碼時,自動對程式碼進行 lint 和格式化。
3. 要在儲存程式碼時進行格式化和 lint,需要使用像 VS Code 這樣的編輯器:
安裝 ESLint 擴充套件外掛。在此(https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)下載外掛或在 VS Code 編輯器中按下 ctrl + shift + x 開啟擴充套件模組,搜尋 eslint,將出現一系列外掛。安裝 Dirk Baeumer 開發的那個。安裝完成後,點選 reload 按鈕重新啟動編輯器。
安裝好這個外掛後,在 app/ 根資料夾中建立一個名為.vscode/ 的資料夾 ——不要忘了那個點號,這個非常重要。
在資料夾中建立一個 settings.json 檔案,如下所示:
{
"editor.formatOnSave": false,
"eslint.autoFixOnSave": true,
}
-
editor.formatOnSave——我在這裡將它設定為 false,因為我不希望檔案格式的預設編輯器配置與 ESLint 和 Prettier 發生衝突。
-
eslint.autoFixOnSave——我在這裡將它設定為 true,因為我希望每次在儲存檔案時安裝的外掛都能正常工作。由於 ESLint 的配置關聯了 Prettier 的配置,所以每次在點選儲存時,它都會格式化和 lint 你的程式碼。
需要注意的是,當你執行 yarn lint:write 時,它也會 lint 和美化你的程式碼。
試想一下,如果你有 2 萬行程式碼,然後通過手動的方式進行審計和改進,然後再想象一下用一個命令就可以完成所有事情。手動方法可能需要 30 天,而自動方法可能只需要 30 秒。
指令碼已經設定好了,每次點選儲存時,編輯器都會對特定檔案做出神奇的迴應。但是,並不是團隊中的每個人都會選擇使用 VS Code。不過沒關係,我們可以更自動化一些。
husky husky 是什麼?
husky(https://github.com/typicode/husky)是一個 Git 鉤子,你可以在提交程式碼前或在將程式碼推送到分支時執行某些特定的操作。
你所要做的就是安裝 husky:
yarn add --dev husky
然後在 package.json 檔案中新增以下內容:
"husky": {
"hooks": {
"pre-commit": "YOUR_COMMAND_HERE",
"pre-push": "YOUR_COMMAND_HERE"
}
},
每次在提交或推送程式碼時,它都會執行某個指令碼或命令——比如執行測試用例或格式化程式碼。
lint-staged lint-staged 是什麼?
lint-staged(https://github.com/okonet/lint-staged)可以在暫存(Git staged)檔案上執行 linter,這樣就不會將錯誤的程式碼推送到分支上。
為什麼要用 lint-staged?
在提交程式碼之前進行 lint 是很有意義的,你可以確保沒有錯誤進入到程式碼庫中,並且可以強制應用程式碼樣式。但在整個專案上執行 lint 過程會很慢,而且有些 lint 結果可能無關緊要。你可能只想對要提交的檔案進行 lint。
這個專案提供了一個指令碼,這個指令碼將執行任意的 shell 任務,並將暫存檔案列表作為引數,按指定的通配模式進行檔案過濾。
你要做的是安裝 lint-staged:
yarn add --dev lint-staged
然後在 package.json 檔案中新增:
"lint-staged": {
"*.(js|jsx)": ["npm run lint:write", "git add"]
},
這段配置的意思是先執行 lint:write 命令,然後將檔案新增到暫存區域。它僅針對.js 和.jsx 檔案執行這個命令,但你也可以根據需要針對其他檔案執行這個命令。
husky 與 lint-staged 一起使用
每次提交程式碼之前,都會執行一個叫作 lint-staged 的指令碼,這個指令碼將執行 npm run lint:write 命令,這個將 lint 並格式化你的程式碼,然後將程式碼新增到暫存區並提交。
最終的 package.json 檔案應如下所示。
{
"name": "react-boiler-plate",
"version": "1.0.0",
"description": "A react boiler plate",
"main": "src/index.js",
"author": "Adeel Imran",
"license": "MIT",
"scripts": {
"lint": "eslint --debug src/",
"lint:write": "eslint --debug src/ --fix",
"prettier": "prettier --write src/**/*.js"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.(js|jsx)": ["npm run lint:write", "git add"]
},
"devDependencies": {
"babel-eslint": "^8.2.3",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^17.0.0",
"eslint-config-jest-enzyme": "^6.0.2",
"eslint-plugin-babel": "^5.1.0",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-jest": "^21.18.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-react": "^7.9.1",
"husky": "^1.1.2",
"lint-staged": "^7.3.0",
"prettier": "^1.14.3"
}
}
現在,每當你提交程式碼時:
$ git add .
$ git commit -m "some descriptive message here"
它將根據.eslintrc.js 檔案的所有規則對程式碼進行 lint 和格式化。有了這個,你就可以確保沒有壞程式碼被推到生產環境中。
現在介紹一下 EditorConfig
首先在 app/ 根資料夾中建立一個.editorconfig 檔案,然後在該檔案中貼上以下程式碼:
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*.md]
trim_trailing_whitespace = false
[*.js]
trim_trailing_whitespace = true
# Unix-style newlines with a newline ending every file
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
max_line_length = 100
那麼 EditorConfig 是什麼東西?
並不是每個人都會使用 VS Code,所以為了讓每個人保持統一(例如在製表符空格或換行方面),我們使用.editorconfig,這樣有助於強制執行某些規則。
支援 EditorConfig(https://editorconfig.org/)的編輯器包括 Web Storm、App Code、Atom、Eclipse、Emacs、bbedit,等等。
上述的配置將執行以下操作:
-
去掉.md 和.js 檔案中的尾部空格;
-
將縮排樣式設定為空格而不是製表符;
-
縮排大小為 2;
-
行尾是 lf,這樣每個人不管使用的是哪種作業系統,都會有相同的行尾;
-
檔案末尾應該有一個新行;
-
單行的最大度應為 100 個字元。
英文原文:
https://medium.freecodecamp.org/these-tools-will-help-you-write-clean-code-da4b5401f68e