利用 ESLint 檢查代碼質量


其實很早的時候就想嘗試ESLint了,但是很多次都是玩了一下就覺得這東西巨復雜,一執行檢查就是滿屏的error,簡直是不堪入目,遂放棄。直到某天終於下定決心深入看了文檔,才發現其實挺簡單的,只是當時沒有看到合適入門教程而已。我相信很多人也有着跟我一樣的經歷,所以希望將自己的踩坑心得記錄下來,讓后來者更輕易地掌握ESLint的使用,因為它確實是個好東西。

JavaScript是一門神奇的動態語言,它在帶給我們編程的靈活性的同時也悄悄埋下了一些地雷。除了基本的語法錯誤能在程序一啟動的時候被檢測到之外,很多隱含的錯誤都是在運行的時候才突然地蹦出來。除非你的程序有着100%的測試覆蓋率,否則說不定哪天就會因為一個xxx is undefined而導致程序崩潰,而為了避免這樣的錯誤可能你只需要在提交代碼的時候用工具靜態分析一下,僅此而已。

ESLint是一個插件化的javascript代碼檢測工具,它可以用於檢查常見的JavaScript代碼錯誤,也可以進行代碼風格檢查,這樣我們就可以根據自己的喜好指定一套ESLint配置,然后應用到所編寫的項目上,從而實現輔助編碼規范的執行,有效控制項目代碼的質量。

手把手入門

在開始使用ESLint之前,我們需要通過NPM來安裝它:

$ npm install -g eslint

我從Gist上找到了自己幾年前寫的一個小函數,將其保存為文件merge.js

function merge () { var ret = {}; for (var i in arguments) { var m = arguments[i]; for (var j in m) ret[j] = m[j]; } return ret; } console.log(merge({a: 123}, {b: 456}));

然后執行node merge.js確保它是可以正確運行的(輸出結果為{ a: 123, b: 456 })。

接着我們執行以下命令來使用ESLint檢查:

$ eslint merge.js

可以看到,沒有任何輸出結果。這是因為我們沒有指定任何的配置,除非這個文件是有語法錯誤,否則應該是不會有任何提示的。現在我們先使用內置的eslint:recommended配置,它包含了一系列核心規則,能報告一些常見的問題。

首先新建ESLint配置文件.eslintrc.js

module.exports = { extends: 'eslint:recommended', };

重新執行eslint merge.js可以看到輸出了2個錯誤:

/example/merge.js 10:1 error Unexpected console statement no-console 10:1 error 'console' is not defined no-undef  2 problem (2 error, 0 warnings)

這兩條提示信息還是足夠我們搞清楚是怎么回事的:

  • Unexpected console statement no-console - 不能使用console
  • ‘console’ is not defined no-undef - console變量未定義,不能使用未定義的變量

針對第1條提示,我們可以禁用no-console規則。將配置文件.eslintrc.js改為這樣:

module.exports = { extends: 'eslint:recommended', rules: { 'no-console': 'off', }, };

說明:配置規則寫在rules對象里面,key表示規則名稱,value表示規則的配置,具體說明見下文。

重新執行檢查還是提示no-undef

/example/merge.js 10:1 error 'console' is not defined no-undef  1 problem (1 error, 0 warnings)

這是因為JavaScript有很多種運行環境,比如常見的有瀏覽器和Node.js,另外還有很多軟件系統使用JavaScript作為其腳本引擎,比如PostgreSQL就支持使用JavaScript來編寫存儲引擎,而這些運行環境可能並不存在console這個對象。另外在瀏覽器環境下會有window對象,而Node.js下沒有;在Node.js下會有process對象,而瀏覽器環境下沒有。

所以在配置文件中我們還需要指定程序的目標環境:

module.exports = { extends: 'eslint:recommended', env: { node: true, }, rules: { 'no-console': 'off', }, };

再重新執行檢查時,已經沒有任何提示輸出了,說明merge.js已經完全通過了檢查。

配置文件

ESLint還可以在項目的package.json文件中指定配置,直接將上文中的module.exports的值寫到eslintConfig里面即可:

{ "name": "my-package", "version": "0.0.1", "eslintConfig": { "extends": "eslint:recommended", "env": { "node": true }, "rules": { "no-console": "off" } } }

另外還可以在執行eslint命令時通過命令行參數來指定,詳細文檔可以參考這里:Configuring ESLint - 配置

規則

每條規則有3個等級:offwarnerroroff表示禁用這條規則,warn表示僅給出警告,並不會導致檢查不通過,而error則會導致檢查不通過。

有些規則還帶有可選的參數,比如comma-dangle可以寫成[ "error", "always-multiline" ]no-multi-spaces可以寫成[ "error", { exceptions: { "ImportDeclaration": true }}]

規則的詳細說明文檔可以參考這里:Rules - 規則

使用共享的配置文件

上文我們以eslint:recommended為基礎配置,然后在此之上修改no-console這條規則。而在大多數時候,我們可能會根據自己個人或團隊的習慣,定制更多的規則,比如限定縮進是2個空格和使用單引號的字符串等。而如果每一個項目都要這樣寫到.eslintrc.js文件上,管理起來會比較麻煩。

我們可以將定義好規則的.eslintrc.js文件存儲到一個公共的位置,比如public-eslintrc.js

module.exports = { extends: 'eslint:recommended', env: { node: true, }, rules: { 'no-console': 'off', 'indent': [ 'error', 2 ], 'quotes': [ 'error', 'single' ], }, };

然后將原來的.eslintrc.js文件改成這樣:

module.exports = { extends: './public-eslintrc.js', };

為了驗證這樣的修改是否生效,將merge.js中的var ret = {};這一行前面多加一個空格,再執行ESLint檢查:

/example/merge.js 2:4 error Expected indentation of 2 space characters but found 3 indent  1 problem (1 error, 0 warnings)

這時候提示的是縮進只能為2個空格,而文件的第2行卻發現了3個空格,說明公共配置文件public-eslintrc.js已經生效了。

我們還可以使用已經發布到NPM上的ESLint配置,這些配置的模塊名一般以eslint-config-為前綴,比如我在學習ESLint時自己編寫的一個配置名為eslint-config-lei。要使用這個配置,先執行以下命令安裝它:

$ npm install -g eslint-config-lei

注意:用於我們的eslint命令是全局安裝的,所有用到的eslint-config-*模塊也必須全局安裝,否則將無法正確載入。這是一個已知的Bug,參考這里:Error: Cannot read config package for shareable config using global eslint #4822

然后將.eslintrc.js文件改成這樣:

module.exports = { extends: 'lei', };

再執行ESLint檢查,可以看到輸出如下的提示:

/example/merge.js 1:15 warning Unexpected space before function parentheses space-before-function-paren 2:3 error Unexpected var, use let or const instead no-var 3:8 error Unexpected var, use let or const instead no-var 4:5 error Unexpected var, use let or const instead no-var 5:10 error Unexpected var, use let or const instead no-var 10:19 warning A space is required after '{' object-curly-spacing 10:26 warning A space is required before '}' object-curly-spacing 10:29 warning A space is required after '{' object-curly-spacing 10:36 warning A space is required before '}' object-curly-spacing  9 problems (4 errors, 5 warnings)

ESLint配置文件中的extends還可以用來指定各種來源的配置引用,具體說明可以參考以下鏈接:

代碼格式化

ESLint規則列表頁面,我們發現有些規則的旁邊會帶有一個橙色扳手圖標,表示在執行eslint命令時指定--fix參數可以自動修復該問題。

接着上文使用eslint-config-lei配置的檢查,我們嘗試在執行檢查時添加--fix參數:

$ eslint merge.js --fix

執行完畢,沒有發現任何提示。再打開merge.js文件發現已經變成了這樣:

function merge() { const ret = {}; for (const i in arguments) { const m = arguments[i]; for (const j in m) ret[j] = m[j]; } return ret; } console.log(merge({ a: 123 }, { b: 456 }));

主要的變化有以下三部分:

  • 聲明函數時,函數名與參數列表的空格不見了:merge ()修改為merge()
  • var聲明的變量變成了const聲明:var ret = {}修改為const ret = {}
  • 對象的內容與花括號之間增加了空格:{a: 123}修改為{ a: 123 }

我們可以利用這個特性來自動格式化項目代碼,這樣就可以保證代碼書寫風格的統一。

發布自己的配置

前文關於「共享的配置文件」一小節已經提到,可以在extends中指定一個文件名,或者一個eslint-config-開頭的模塊名。為了便於共享,一般推薦將其發布成一個NPM模塊。

其原理就是在載入模塊時輸出原來.eslintrc.js的數據。比如我們可以創建一個模塊eslint-config-my用於測試。

新建文件eslint-config-my/index.js

module.exports = { extends: 'eslint:recommended', env: { node: true, es6: true, }, rules: { 'no-console': 'off', 'indent': [ 'error', 2 ], 'quotes': [ 'error', 'single' ], }, };

再新建文件eslint-config-my/package.json

{ "name": "eslint-config-my", "version": "0.0.1", "main": "index.js" }

為了能讓eslint正確載入這個模塊,我們需要執行npm link將這個模塊鏈接到本地全局位置:

$ npm link eslint-config-my

然后將文件.eslintrc.js改成這樣:

module.exports = { extends: 'my', };

說明:在extends中,eslint-config-my可簡寫為my

在執行eslint merge.js檢查,可看到沒有任何錯誤提示信息,說明eslint已經成功載入了eslint-config-my的配置。如果我們使用npm publish將其發布到NPM上,那么其他人通過npm install eslint-config-my即可使用我們共享的這個配置。

另外可以參考我自己寫的一個ESLint配置模塊:eslint-config-lei

關於共享ESLint配置的詳細文檔可參考:Shareable Configs - 可共享的配置

例外情況

盡管我們在編碼時懷着嚴格遵守規則的美好願景,而凡事總有例外。定立ESLint規則的初衷是為了避免自己犯錯,但是我們也要避免不顧實際情況而將其搞得太過於形式化,本末倒置。

ESLint提供了多種臨時禁用規則的方式,比如我們可以通過一條eslint-disable-next-line備注來使得下一行可以跳過檢查:

// eslint-disable-next-line var a = 123; var b = 456;

在上面的示例代碼中,var a = 123不會受到檢查,而var b = 456則右恢復檢查,在上文我們使用的eslint-config-lei的配置規則下,由於不允許使用var聲明變量,則var b這一行會報告一個error

我們還可以通過成對的eslint-enableeslint-disable備注來禁用對某一段代碼的檢查,但是稍不小心少寫了一個eslint-disable就可能會導致后面所有文件的檢查都被禁用,所以我並不推薦使用。

詳細使用方法可以參考文檔:Disabling Rules with Inline Comments - 使用行內注釋禁用規則

總結

剛開始接觸ESLint時覺得太難,是因為過太過於迷信權威。比如Airbnb公司的JavaScript風格,在GitHub上受到了很大的好評,其實我自己也非常認可這樣的編碼風格。但每個團隊都會根據自己的的實際情況來定制不同的東西,我們並不能隨便照搬過來。所以當使用eslint-config-airbnb這個配置進行ESLint檢查時,滿屏都是errorwarning,從而覺得這東西根本沒啥卵用。

另外我也犯了「大忌」:直接使用eslint-config-airbnb這種某個公司高度定制化的配置,而不是eslint:recommended這樣保守的。而且是直接用來檢查整個項目好幾十個JS文件,可想而知那是怎樣的畫面(本文最后版本的merge.js文件使用airbnb的配置,總共12行的代碼就提示了8個問題:✖ 8 problems (7 errors, 1 warning))。

本文的目的是讓讀者以一個比較低的姿態開始接觸ESLint,先學會簡單地配置規則,如果要更深入地定制自己的規則,建議閱讀「相關鏈接」中的ESLint文檔。

相關鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM