前言
在團隊協作開發中,為了統一代碼風格,避免一些低級錯誤,應該設有團隊成員統一遵守的編碼規范。很多語言都提供了Lint工具來實現這樣的功能,JavaScript也有類似的工具:ESLint。除了可以集成到構建工具中(如:Gulp)在構建過程中檢查代碼風格以外;還可以通過將ESLint和代碼編輯器相結合以提供代碼風格的實時校驗。這里將介紹如何在Visual Studio Code使用ESLint來提供代碼風格的實時校驗。
配置原則
- 能夠幫助發現代碼錯誤的規則,全部開啟
- 配置不應該依賴於某個具體項目,而應盡可能的合理
- 幫助保持團隊的代碼風格統一,而不是限制開發體驗
配置解讀
- 每一條配置都有注釋說明此配置的用途
- 對於理解困難的配置,都在注釋中有舉例
- 對於有爭議的配置,都在注釋中說明了為什么要這么配置的原因
- 對於關閉掉的配置,都在注釋中有對應的原因說明,以及 @off 的標識
- 對於能夠 autofix 的配置,都在注釋中有標注 @autofix
EsLint提供以下支持:
- ES6
- AngularJS
- JSX
- Style檢查
- 自定義錯誤和提示
EsLint提供以下幾種校驗:
1.語法錯誤校驗
2.不重要或丟失的標點符號,如分號
3.沒法運行到的代碼塊(使用過WebStorm的童鞋應該了解)
4.未被使用的參數提醒
5.漏掉的結束符,如}
6.確保樣式的統一規則,如sass或者less
7.檢查變量的命名
詳細的配置內容在這里:
/**
* AlloyTeam ESLint 規則 - React
*
* 包含所有 ESLint 規則,以及所有 eslint-plugin-react 規則
* 使用 babel-eslint 作為解析器
*
* @fixable 表示此配置支持 --fix
* @off 表示此配置被關閉了,並且后面說明了關閉的原因
*/
module.exports = {
extends: [
'./index.js',
],
plugins: [
'react'
],
rules: {
// 布爾值類型的 propTypes 的 name 必須為 is 或 has 開頭
// @off 不強制要求寫 propTypes
'react/boolean-prop-naming': 'off',
// 一個 defaultProps 必須有對應的 propTypes
// @off 不強制要求寫 propTypes
'react/default-props-match-prop-types': 'off',
// 組件必須有 displayName 屬性
// @off 不強制要求寫 displayName
'react/display-name': 'off',
// 禁止在自定義組件中使用一些指定的 props
// @off 沒必要限制
'react/forbid-component-props': 'off',
// 禁止使用一些指定的 elements
// @off 沒必要限制
'react/forbid-elements': 'off',
// 禁止使用一些指定的 propTypes
// @off 不強制要求寫 propTypes
'react/forbid-prop-types': 'off',
// 禁止直接使用別的組建的 propTypes
// @off 不強制要求寫 propTypes
'react/forbid-foreign-prop-types': 'off',
// 禁止使用數組的 index 作為 key
// @off 太嚴格了
'react/no-array-index-key': 'off',
// 禁止使用 children 做 props
'react/no-children-prop': 'error',
// 禁止使用 dangerouslySetInnerHTML
// @off 沒必要限制
'react/no-danger': 'off',
// 禁止在使用了 dangerouslySetInnerHTML 的組建內添加 children
'react/no-danger-with-children': 'error',
// 禁止使用已廢棄的 api
'react/no-deprecated': 'error',
// 禁止在 componentDidMount 里面使用 setState
// @off 同構應用需要在 didMount 里寫 setState
'react/no-did-mount-set-state': 'off',
// 禁止在 componentDidUpdate 里面使用 setState
'react/no-did-update-set-state': 'error',
// 禁止直接修改 this.state
'react/no-direct-mutation-state': 'error',
// 禁止使用 findDOMNode
'react/no-find-dom-node': 'error',
// 禁止使用 isMounted
'react/no-is-mounted': 'error',
// 禁止在一個文件創建兩個組件
// @off 有一個 bug https://github.com/yannickcr/eslint-plugin-react/issues/1181
'react/no-multi-comp': 'off',
// 禁止在 PureComponent 中使用 shouldComponentUpdate
'react/no-redundant-should-component-update': 'error',
// 禁止使用 ReactDOM.render 的返回值
'react/no-render-return-value': 'error',
// 禁止使用 setState
// @off setState 很常用
'react/no-set-state': 'off',
// 禁止拼寫錯誤
'react/no-typos': 'error',
// 禁止使用字符串 ref
'react/no-string-refs': 'error',
// 禁止在組件的內部存在未轉義的 >, ", ' 或 }
'react/no-unescaped-entities': 'error',
// @fixable 禁止出現 HTML 中的屬性,如 class
'react/no-unknown-property': 'error',
// 禁止出現未使用的 propTypes
// @off 不強制要求寫 propTypes
'react/no-unused-prop-types': 'off',
// 定義過的 state 必須使用
// @off 沒有官方文檔,並且存在很多 bug: https://github.com/yannickcr/eslint-plugin-react/search?q=no-unused-state&type=Issues&utf8=%E2%9C%93
'react/no-unused-state': 'off',
// 禁止在 componentWillUpdate 中使用 setState
'react/no-will-update-set-state': 'error',
// 必須使用 Class 的形式創建組件
'react/prefer-es6-class': [
'error',
'always'
],
// 必須使用 pure function
// @off 沒必要限制
'react/prefer-stateless-function': 'off',
// 組件必須寫 propTypes
// @off 不強制要求寫 propTypes
'react/prop-types': 'off',
// 出現 jsx 的地方必須 import React
// @off 已經在 no-undef 中限制了
'react/react-in-jsx-scope': 'off',
// 非 required 的 prop 必須有 defaultProps
// @off 不強制要求寫 propTypes
'react/require-default-props': 'off',
// 組件必須有 shouldComponentUpdate
// @off 沒必要限制
'react/require-optimization': 'off',
// render 方法中必須有返回值
'react/require-render-return': 'error',
// @fixable 組件內沒有 children 時,必須使用自閉和寫法
// @off 沒必要限制
'react/self-closing-comp': 'off',
// @fixable 組件內方法必須按照一定規則排序
'react/sort-comp': 'error',
// propTypes 的熟悉必須按照字母排序
// @off 沒必要限制
'react/sort-prop-types': 'off',
// style 屬性的取值必須是 object
'react/style-prop-object': 'error',
// HTML 中的自閉和標簽禁止有 children
'react/void-dom-elements-no-children': 'error',
// @fixable 布爾值的屬性必須顯式的寫 someprop={true}
// @off 沒必要限制
'react/jsx-boolean-value': 'off',
// @fixable 自閉和標簽的反尖括號必須與尖括號的那一行對齊
'react/jsx-closing-bracket-location': [
'error',
{
nonEmpty: false,
selfClosing: 'line-aligned'
}
],
// @fixable 結束標簽必須與開始標簽的那一行對齊
// @off 已經在 jsx-indent 中限制了
'react/jsx-closing-tag-location': 'off',
// @fixable 大括號內前后禁止有空格
'react/jsx-curly-spacing': [
'error',
{
when: 'never',
attributes: {
allowMultiline: true
},
children: true,
spacing: {
objectLiterals: 'never'
}
}
],
// @fixable props 與 value 之間的等號前后禁止有空格
'react/jsx-equals-spacing': [
'error',
'never'
],
// 限制文件后綴
// @off 沒必要限制
'react/jsx-filename-extension': 'off',
// @fixable 第一個 prop 必須得換行
// @off 沒必要限制
'react/jsx-first-prop-new-line': 'off',
// handler 的名稱必須是 onXXX 或 handleXXX
// @off 沒必要限制
'react/jsx-handler-names': 'off',
// @fixable jsx 的 children 縮進必須為四個空格
'react/jsx-indent': [
'error',
4
],
// @fixable jsx 的 props 縮進必須為四個空格
'react/jsx-indent-props': [
'error',
4
],
// 數組中的 jsx 必須有 key
'react/jsx-key': 'error',
// @fixable 限制每行的 props 數量
// @off 沒必要限制
'react/jsx-max-props-per-line': 'off',
// jsx 中禁止使用 bind
// @off 太嚴格了
'react/jsx-no-bind': 'off',
// 禁止在 jsx 中使用像注釋的字符串
'react/jsx-no-comment-textnodes': 'error',
// 禁止出現重復的 props
'react/jsx-no-duplicate-props': 'error',
// 禁止在 jsx 中出現字符串
// @off 沒必要限制
'react/jsx-no-literals': 'off',
// 禁止使用 target="_blank"
// @off 沒必要限制
'react/jsx-no-target-blank': 'off',
// 禁止使用未定義的 jsx elemet
'react/jsx-no-undef': 'error',
// 禁止使用 pascal 寫法的 jsx,比如 <TEST_COMPONENT>
'react/jsx-pascal-case': 'error',
// @fixable props 必須排好序
// @off 沒必要限制
'react/jsx-sort-props': 'off',
// @fixable jsx 的開始和閉合處禁止有空格
'react/jsx-tag-spacing': [
'error',
{
closingSlash: 'never',
beforeSelfClosing: 'always',
afterOpening: 'never'
}
],
// jsx 文件必須 import React
'react/jsx-uses-react': 'error',
// 定義了的 jsx element 必須使用
'react/jsx-uses-vars': 'error',
// @fixable 多行的 jsx 必須有括號包起來
// @off 沒必要限制
'react/jsx-wrap-multilines': 'off'
}
};
使用方法
標准規則
安裝
npm install --save-dev eslint-config-alloy babel-eslint
配置 .eslintrc.js
在你的項目根目錄下創建 .eslintrc.js,並將以下內容復制到文件中:
module.exports = {
extends: [
'eslint-config-alloy',
],
globals: {
// 這里填入你的項目需要的全局變量
// 這里值為 false 表示這個全局變量不允許被重新賦值,比如:
//
// jQuery: false,
// $: false
},
rules: {
// 這里填入你的項目需要的個性化配置,比如:
//
// // @fixable 一個縮進必須用兩個空格替代
// 'indent': [
// 'error',
// 2,
// {
// SwitchCase: 1,
// flatTernaryExpressions: true
// }
// ]
}
};
React 版
安裝
npm install --save-dev eslint-config-alloy eslint-plugin-react babel-eslint
配置 .eslintrc.js
在你的項目根目錄下創建 .eslintrc.js,並將以下內容復制到文件中:
安裝完成后我們可以看到除了ESLint命令行工具為我們生成的ESLint依賴包,還有一個特殊的.eslintrc.json文件,該文件是ESLint的配置文件,如下所示:
{
"extends": "standard",
"installedESLint": true,
"plugins": [
"standard"
]
}
配置文件中除了聲明我們所使用的代碼風格以外,我們還可以定制自己的規則,比如:聲明全局變量或者規定字符串引號的風格,以及其他任何ESLint支持的規則都是可以配置的,下面是一個簡單的示例:
{
"extends": "standard",
"installedESLint": true,
"plugins": [
"standard"
],
"rules": {
//關閉額外的分號檢查
//0:關閉,1:警告,2:異常
"semi": 0,
//字符串必須使用單引號
"quotes": [
"error",
"single"
]
}
}
配置文件中除了聲明我們所使用的代碼風格以外,我們還可以定制自己的規則,比如:聲明全局變量或者規定字符串引號的風格,以及其他任何ESLint支持的規則都是可以配置的,下面是一個簡單的示例:
module.exports = {
extends: [
'eslint-config-alloy/react',
],
globals: {
// 這里填入你的項目需要的全局變量
// 這里值為 false 表示這個全局變量不允許被重新賦值,比如:
//
// React: false,
// ReactDOM: false
},
rules: {
// 這里填入你的項目需要的個性化配置,比如:
//
// // @fixable 一個縮進必須用兩個空格替代
// 'indent': [
// 'error',
// 2,
// {
// SwitchCase: 1,
// flatTernaryExpressions: true
// }
// ],
// // @fixable jsx 的 children 縮進必須為兩個空格
// 'react/jsx-indent': [
// 'error',
// 2
// ],
// // @fixable jsx 的 props 縮進必須為兩個空格
// 'react/jsx-indent-props': [
// 'error',
// 2
// ]
}
};
代碼改造經驗
如果是一個新項目,應用一個比較嚴格的 ESLint 規則並不是一件難事。
但是如果是一個已經維護多年的老項目,那么突然引入 ESLint 就會有成千上萬個錯誤。這個時候該如何改造呢?
1. 將所有報錯的配置都關閉
運行 ESLint 之后,會有很多錯誤,這時候我們可以把他們先暫時關閉掉。
由於項目還在不停地迭代,這樣可以保證其他不會報錯的規則能夠應用到新增的文件上。
這時你的 .eslintrc.js 應該類似與下面的樣子:
module.exports = {
extends: [
'@alloyteam/eslint-config-standard',
],
globals: {
React: false,
jQuery: false,
$: false
},
rules: {
'no-dupe-keys': 'off',
'no-var': 'off',
'complexity': 'off',
'indent': 'off'
}
};
小技巧:如果報錯的規則太多了,可以在運行 ESLint 的時候,加上參數 -f json,這樣的話會以 json 格式輸出,然后稍作處理就可以直接得到所有報錯的規則了。
注意:一開始不要開啟 --fix,因為修復的太多了,就難以 review 代碼了。