兩個(gè)月之前在項(xiàng)目中就開始使用 Eslint ,當(dāng)時(shí)直接 copy 別人的 .eslintrc.js 文件,感覺好復(fù)雜,一直沒吃透,中間去把 Eslint 官方文檔看了數(shù)遍,依然無果。今天剛好沒事,回過頭來想整理一下 Eslint 的使用,突然發(fā)現(xiàn)變得好簡單。總結(jié)下在這過程中走得彎路。
- 先要知道 Lint 是什么,Eslint 又是什么;
- 因?yàn)橄胍奶啵ㄏ氚?Eslint 官方文檔里每個(gè)字都記住)就容易抓不住重點(diǎn)(目的是在項(xiàng)目中使用 Eslint,這僅僅是個(gè)工具,工具又怎么會(huì)搞的太難)。
目標(biāo)
學(xué)習(xí)完本教程希望對下面這個(gè) .eslintrc.js
文件能夠做到心里有數(shù)。
// .eslintrc.js
module.exports = {
"extends": "airbnb",
"rules": {
"semi": [2, "never"],
"no-console": 0,
"comma-dangle": [2, "always-multiline"],
"max-len": 0,
"react/jsx-first-prop-new-line": 0,
"react/jsx-filename-extension": 0,
"space-before-function-paren": [2, "always"],
"no-unused-expressions": [0, {
"allowShortCircuit": true,
"allowTernary": true
}],
"arrow-body-style": [0, "never"],
"func-names": 0,
"prefer-const": 0,
"no-extend-native": 0,
"no-param-reassign": 0,
"no-restricted-syntax": 0,
"no-eval": 0,
"no-continue": 0,
"react/jsx-no-bind": 0,
"no-unused-vars": [2, { "ignoreRestSiblings": true }],
"no-underscore-dangle": 0,
"global-require": 0,
"import/no-unresolved": 0,
"import/extensions": 0,
"jsx-a11y/href-no-hash": 0,
"react/no-array-index-key": 0,
"react/require-default-props": 0,
"react/forbid-prop-types": 0,
"react/no-string-refs": 0,
"react/no-find-dom-node": 0,
"import/no-extraneous-dependencies": 0,
"import/prefer-default-export": 0,
"react/no-danger": 0,
"jsx-a11y/no-static-element-interactions": 0,
},
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 8,
"ecmaFeatures": {
"jsx": true,
"experimentalObjectRestSpread": true
}
},
"settings": {
"import/resolver": "node"
}
};
Eslint 是什么
編碼規(guī)范
每個(gè)程序員都有自己的編碼習(xí)慣,最常見的莫過于:
- 有的人寫代碼一行代碼結(jié)尾必須加分號
;
,有的人覺得不加分號;
更好看; - 有的人寫代碼一行代碼不會(huì)超過 80 個(gè)字符,認(rèn)為這樣看起來簡潔明了,有的人喜歡把所有邏輯都寫在一行代碼上,覺得別人看不懂的代碼很牛逼;
- 有的人使用變量必然會(huì)先定義
var a = 10;
,而粗心的人寫變量可能沒有定義過就直接使用b = 10;
;
Lint 的含義
如果你寫自己的項(xiàng)目怎么折騰都沒關(guān)系,但是在公司中老板希望每個(gè)人寫出的代碼都要符合一個(gè)統(tǒng)一的規(guī)則,這樣別人看源碼就能夠看得懂,因?yàn)樵创a是符合統(tǒng)一的編碼規(guī)范制定的。
那么問題來了,總不能每個(gè)人寫的代碼老板都要一行行代碼去檢查吧,這是一件很蠢的事情。凡是重復(fù)性的工作,都應(yīng)該被制作成工具來節(jié)約成本。這個(gè)工具應(yīng)該做兩件事情:
- 提供編碼規(guī)范;
- 提供自動(dòng)檢驗(yàn)代碼的程序,并打印檢驗(yàn)結(jié)果:告訴你哪一個(gè)文件哪一行代碼不符合哪一條編碼規(guī)范,方便你去修改代碼。
Lint 因此而誕生。
Eslint 的含義
Lint 是檢驗(yàn)代碼格式工具的一個(gè)統(tǒng)稱,具體的工具有 Jslint
、 Eslint
等等 ...........
我們可以形象地將 Lint 看成是電商行業(yè),而電商行業(yè)具體表現(xiàn)有淘寶(Eslint)、京東(Jslint)等。
使用 Eslint
確保你的電腦安裝了 node 和 npm 環(huán)境
創(chuàng)建項(xiàng)目
npm init
指令會(huì)在項(xiàng)目根目錄下生成 package.json
文件。
$ d:
$ cd d:
$ mkdir test-eslint
$ cd test-eslint
$ npm init
安裝依賴包
--save-dev
會(huì)把 eslint 安裝到 package.json 文件中的 devDependencies 屬性中,意思是只是開發(fā)階段用到這個(gè)包,上線時(shí)就不需要這個(gè)包了。
$ npm install eslint --save-dev
設(shè)置 package.json 文件
打開 package.json 文件,修改 script 屬性如下:
"scripts": {
"test": "react-scripts test --env=jsdom",
"lint": "eslint src",
"lint:create": "eslint --init"
}
- script 屬性的意思是腳本,使用方法是在 cmd 窗口中輸入
npm run 指令
的形式,如:npm run lint:create
; -
"lint:create": "eslint --init"
這個(gè)腳本是為了生成 .eslintrc.js 文件,在介紹 Lint 的時(shí)候說到 Lint 應(yīng)該提供編碼規(guī)范,規(guī)范寫在哪里,就寫在這個(gè)文件,所以我們需要?jiǎng)?chuàng)建它; -
"lint": "eslint src"
在介紹 Lint 的時(shí)候也說到 Lint 應(yīng)該提供自動(dòng)校驗(yàn)代碼的程序,這個(gè)腳本是讓 Lint 自動(dòng)檢驗(yàn) src 目錄下所有的 .js 文件。
創(chuàng)建 .eslint.js 文件
$ npm run lint:create
> 20170811@0.1.0 lint:create D:\code\test\20170811
> eslint --init
? How would you like to configure ESLint? Answer questions about your style // 以問答的形式創(chuàng)建配置文件
? Are you using ECMAScript 6 features? Yes // 是否校驗(yàn) Es6 語法
? Are you using ES6 modules? Yes // 是否校驗(yàn) Es6 模塊語法
? Where will your code run? Browser // 代碼運(yùn)行環(huán)境,Browser 指瀏覽器
? Do you use CommonJS? Yes // 是否校驗(yàn) CommonJs 語法
? Do you use JSX? Yes // 是否校驗(yàn) JSX 語法
? Do you use React? Yes // 是否校驗(yàn) React 語法
? What style of indentation do you use? Tabs // 首行空白選擇 Tab 鍵還是 Space
? What quotes do you use for strings? Double // 字符串使用單引號 'string' 還是雙引號 "string"
? What line endings do you use? Windows // 操作系統(tǒng)
? Do you require semicolons? Yes // 每行代碼結(jié)尾是否校驗(yàn)加分號 ;
? What format do you want your config file to be in? JavaScript // 以 .js 格式生成配置文件
Installing eslint-plugin-react@latest // 因?yàn)橐r?yàn) Reac 語法,所以這里需要下載一個(gè) React 語法規(guī)則的包
創(chuàng)建完成后根目錄下應(yīng)該會(huì)出現(xiàn) .eslintrc.js
文件
創(chuàng)建 index.js 文件
在根目錄下創(chuàng)建 src/index.js 文件,內(nèi)容如下,接下來就使用 Eslint 來檢驗(yàn)這個(gè) .js 文件是否符合編碼規(guī)范。
const lint = 'eslint'
此時(shí)的目錄結(jié)構(gòu)應(yīng)該為:
- test-eslint
+ .eslintrc.js
+ package.json
- src
+ index.js
校驗(yàn)代碼
$ npm run lint
1:7 error 'lint' is assigned a value but never used no-unused-vars
1:14 error Strings must use doublequote quotes
1:22 error Missing semicolon semi
3 problems (3 errors, 0 warnings)
2 errors, 0 warnings potentially fixable with the `--fix` option.
這里報(bào)了三個(gè)錯(cuò)誤,分別是:
- index.js 第1行第7個(gè)字符,報(bào)錯(cuò)編碼規(guī)則為
no-unused-vars
:變量 lint 只定義了,但是未使用; - index.js 第1行第14個(gè)字符,報(bào)錯(cuò)編碼規(guī)則為
quotes
:編碼規(guī)范字符串只能使用雙引號,這里卻使用了單引號; - index.js 第1行第22個(gè)字符,報(bào)錯(cuò)編碼規(guī)則為
semi
:編碼規(guī)范每行代碼結(jié)尾必須加分號,這里沒有加分號。
當(dāng)我們熟悉了編碼規(guī)范之后,只需進(jìn)行響應(yīng)的修改就可以使代碼形成統(tǒng)一的風(fēng)格。剛開始如果對編碼規(guī)范具體某一條規(guī)則不了解的話,可以在 eslint規(guī)則表 查看用法。(不建議去背規(guī)則表,而是用到什么查什么,把它當(dāng)成字典來用,不那么累)
設(shè)置 --fix 參數(shù)
打開 package.json 文件,修改 script 屬性如下:
"scripts": {
"test": "react-scripts test --env=jsdom",
"lint": "eslint src --fix",
"lint:create": "eslint --init"
}
說明:這里給 "lint": "eslint src --fix",
加上 --fix
參數(shù),是 Eslint 提供的自動(dòng)修復(fù)基礎(chǔ)錯(cuò)誤的功能。
此時(shí)運(yùn)行 npm run lint
會(huì)看到少了兩條報(bào)錯(cuò)信息,并不是說編碼規(guī)范變了,而是 Eslint 自動(dòng)修復(fù)了基礎(chǔ)錯(cuò)誤,打開 index.js 文件,可看到字符串自動(dòng)變成了雙引號,并且代碼末尾也加上了分號。可惜的是 --fix 只能修復(fù)基礎(chǔ)的不影響代碼邏輯的錯(cuò)誤,像 no-unused-vars 這種錯(cuò)誤只能手動(dòng)修改。
配置文件講解
按照上述操作,會(huì)生成默認(rèn) .eslintrc.js
配置文件,內(nèi)容如下:
// .eslintrc.js
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"always"
]
}
}
該文件導(dǎo)出一個(gè)對象,對象包含屬性 env
、extends
、parserOptions
、plugins
、rules
五個(gè)屬性,作為剛學(xué)習(xí) Eslint 的新手,我們總是想要竭盡所能的詳細(xì)了解每一個(gè)屬性是什么,干嘛用的,以獲取小小的安全感。
env、parserOptions、plugins
這三個(gè)放在一起將是因?yàn)槟阒恍枰浪鼈兪歉陕锏模何业某绦蚶镆玫?ES6 、React 、JSX 語法,這幾個(gè)屬性就是讓 Eslint 能夠檢驗(yàn)到這些語法的。其余的你不需要知道更多的哪怕一丟丟的東東了。
作者在學(xué)習(xí)之初在這塊浪費(fèi)了很多時(shí)間,官方文檔看了很多遍,大多不能理解什么意思,后來想它既然提供這么一個(gè)自動(dòng)生成配置文件的工具,并且是命令行交互的方式,我只需要告訴它我要用 ES6 、React 、JSX 語法,它會(huì)自動(dòng)進(jìn)行相關(guān)配置滿足我的要求即可。
extends
前面一直說檢驗(yàn)代碼遵循編碼規(guī)范,那到底是什么規(guī)范呢。
值為 "eslint:recommended" 的 extends 屬性啟用一系列核心規(guī)則,這些規(guī)則是經(jīng)過前人驗(yàn)證的最佳實(shí)踐(所謂最佳實(shí)踐,就是大家伙都覺得應(yīng)該遵循的編碼規(guī)范),想知道最佳實(shí)踐具體有哪些編碼規(guī)范,可以在 eslint規(guī)則表 中查看被標(biāo)記為 √ 的規(guī)則項(xiàng)。
如果覺得官方提供的默認(rèn)規(guī)則不好用,可以自定義規(guī)則配置文件,然后發(fā)布成 Npm 包,eslint-config-airbnb 就是別人自定義的編碼規(guī)范,使用 npm 安裝后,在我們自己的 .eslintrc.js 中進(jìn)行配置 "extends": "airbnb",eslint-config 這個(gè)前綴可以省略不寫,這樣我們就使用了 eslint-config-airbnb 中的規(guī)則,而不是官方的規(guī)則 "extends":"eslint:recommended" 了。關(guān)于如何創(chuàng)建自定義規(guī)則配置并共享可以參考: 如何自定義規(guī)則配置
關(guān)于 "airbnb" 編碼規(guī)范說兩句,在接觸到大多數(shù)開源項(xiàng)目中,大多數(shù)的作者都會(huì)使用 "airbnb" 編碼規(guī)范而不是 官方的 "extends": "eslint:recommended" 編碼規(guī)范。
如果我們覺得 eslint-config-airbnb 規(guī)則配置中個(gè)別規(guī)則并不符合當(dāng)前項(xiàng)目的要求,可以直接在 .eslintrc.js 配置 rules 屬性,優(yōu)先級高于共享規(guī)則 airbnb。
rules
配置文件也生成了,我們怎么知道我們的系統(tǒng)會(huì)遵循什么規(guī)則呢??
在前面的列子中,使用 npm run lint
校驗(yàn)出了三處錯(cuò)誤,假如我們的項(xiàng)目中字符串就是要使用單引號而不是雙引號,代碼結(jié)尾就是要不加分號才好看,變量就是定義了可能不會(huì)使用,我們可以通過設(shè)置 rules 來定義我們自己的編碼規(guī)范,即規(guī)則。
ESLint 附帶有大量的規(guī)則,修改規(guī)則應(yīng)遵循如下要求:
- "off" 或 0 - 關(guān)閉規(guī)則
- "warn" 或 1 - 開啟規(guī)則,使用警告級別的錯(cuò)誤:warn (不會(huì)導(dǎo)致程序退出)
- "error" 或 2 - 開啟規(guī)則,使用錯(cuò)誤級別的錯(cuò)誤:error (當(dāng)被觸發(fā)的時(shí)候,程序會(huì)退出)
有的規(guī)則沒有屬性,只需控制是開啟還是關(guān)閉,像這樣:"eqeqeq": "off",有的規(guī)則有自己的屬性,使用起來像這樣:"quotes": ["error", "double"],具體有沒有自帶屬性,可查看 eslint規(guī)則表。
修改 .eslintrc.js 文件中的 rules 屬性:
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"single" // 改成字符串必須由單引號括起來而不是雙引號,'string'不報(bào)錯(cuò),"string"報(bào)錯(cuò)
],
"semi": [
"error",
"never" // 改成代碼結(jié)尾不再加分號,加了分號報(bào)錯(cuò),不加分號不報(bào)錯(cuò)
],
"no-unused-vars": 0 // 0 相當(dāng)于 off,表示關(guān)閉規(guī)則,相當(dāng)于不再校驗(yàn)這條規(guī)則:變量定義了必須使用
}
此時(shí)再使用 npm run lint
進(jìn)行代碼校驗(yàn),沒有報(bào)錯(cuò)就說明校驗(yàn)通過,代碼符合統(tǒng)一編碼規(guī)范。
D:\code\test\20170811>npm run lint
> 20170811@0.1.0 lint D:\code\test\20170811
> eslint src
D:\code\test\20170811>
可能存在的疑問
剛接觸 ESlint ,并不清楚有哪些規(guī)則怎么辦,要去 eslint規(guī)則表 一條條記憶嗎?答案是 no。
個(gè)人認(rèn)為 ESlint 的配置文件并不是一次性完成的,而是在項(xiàng)目過程中慢慢完善的。你可以放心大膽的先進(jìn)行編碼,然后使用 npm run lint
校驗(yàn)代碼的編碼規(guī)范,如果這時(shí)候報(bào)錯(cuò),可以在報(bào)錯(cuò)信息中知道是哪一條編碼規(guī)范報(bào)錯(cuò)了,你可能并不認(rèn)識它們,此時(shí)去 eslint規(guī)則表 查詢相應(yīng)規(guī)則的使用方法,如:no-unused-vars
,從而進(jìn)一步確定項(xiàng)目中是否需要這條編碼規(guī)范,如果需要,進(jìn)行局部調(diào)整即可。
$ npm run lint
1:7 error 'lint' is assigned a value but never used no-unused-vars
1:14 error Strings must use doublequote quotes
1:22 error Missing semicolon semi
3 problems (3 errors, 0 warnings)
2 errors, 0 warnings potentially fixable with the `--fix` option.
更新部分
全局變量配置。
如使用 window 對象,默認(rèn)情況下會(huì)報(bào) no-undef 的錯(cuò)誤,需要在 .eslintrc 中進(jìn)行相應(yīng)配置。
{
"rules": {
// ...
},
"globals": {
"window": true
}
}
單行跳過 lint 校驗(yàn)
在實(shí)際編碼時(shí),可能會(huì)出現(xiàn)以下的代碼。
const apple = "apple";
const balana = "balana";
module.exports = {
fruit: balana ;
}
在最上面定義了兩個(gè)變量,在底部的配置文件中只可能用到其中一個(gè)變量,另一個(gè)用不到的在 eslint 校驗(yàn)時(shí)就會(huì)報(bào)錯(cuò) no-unused-vars
的錯(cuò)誤,意思是變量定義了但是沒有被用到。
其中一種解決方案是在 .eslintrc 文件中配置 rules no-unused-vars: 0
,意思是項(xiàng)目中不檢驗(yàn)變量定義未使用這條規(guī)則。強(qiáng)烈不建議這樣做,這個(gè)規(guī)則十分有用,可以規(guī)避編寫代碼時(shí)遺漏的變量。
另一種解決方案就是使用行內(nèi)注釋跳過對 apple 和 balana 兩個(gè)變量跳過 eslint 校驗(yàn),只影響這兩個(gè)變量,不影響外部。
const apple = "apple"; // eslint-disable-line
const balana = "balana"; // eslint-disable-line
module.exports = {
fruit: balana ;
}
此時(shí)使用 eslint 校驗(yàn)時(shí)就不會(huì)再報(bào)錯(cuò)了。
常見規(guī)則含義解釋
-
object-shorthand
設(shè)置該規(guī)則,表示對象屬性要簡寫。
var foo = {x: x}; // 會(huì)報(bào)錯(cuò)
var bar = {a: function () {}}; // 會(huì)報(bào)錯(cuò)
var foo = {x}; // 不會(huì)報(bào)錯(cuò)
var bar = {a () {}}; // 不會(huì)報(bào)錯(cuò)
-
prefer-arrow-callback
要求回調(diào)函數(shù)使用箭頭函數(shù)
// 回調(diào)函數(shù),函數(shù)的參數(shù)是個(gè)函數(shù),這個(gè)參數(shù)函數(shù)就是回調(diào)函數(shù)
funciton bar () {} // 不是回調(diào)函數(shù),不會(huì)報(bào)錯(cuò)
// setTimeout 的第一個(gè)參數(shù)就是回調(diào)函數(shù),不用箭頭函數(shù)會(huì)報(bào)錯(cuò)
setTimeout(() => {
// .......
}, 1000)
-
no-param-reassign
禁止對函數(shù)的參數(shù)重新賦值
function bar ({ data = {} }) {
data.num = 12; // 會(huì)報(bào)錯(cuò)
}
/*
雖然報(bào)錯(cuò),又不想要將該校驗(yàn)關(guān)閉,如果代碼中只有 data 這個(gè)屬性有這種情形,
可以在 .eslintrc.js 中的 rules 屬性中添加配置(可忽略 data 屬性做此校驗(yàn)):
'no-param-reassign': ['error', { 'props': true, 'ignorePropertyModificationsFor': ['data'] }],
'no-param-reassign': 0, // 關(guān)閉該屬性校驗(yàn)
常用的幾個(gè)規(guī)則
"quotes": [1, "single"], # 單引號
"quote-props":[2, "as-needed"], # 雙引號自動(dòng)變單引號
"semi": [2, "never"], # 一行結(jié)尾不要寫分號
"comma-dangle": [1,"always-multiline"] # 對象或數(shù)組多行寫法時(shí),最后一個(gè)值加逗號