項(xiàng)目eslint從零到一
eslint在項(xiàng)目里并不太陌生,通常在使用腳手架時(shí),會(huì)默認(rèn)讓你安裝執(zhí)行的eslint,當(dāng)公司項(xiàng)目比較規(guī)范時(shí),常常會(huì)配置組內(nèi)統(tǒng)一的eslint規(guī)則,eslint幫助我們?cè)陂_發(fā)階段檢查代碼是否符合標(biāo)準(zhǔn)規(guī)范,統(tǒng)一了我們組內(nèi)不同項(xiàng)目代碼風(fēng)格,也可以幫助我們養(yǎng)成良好的代碼習(xí)慣,統(tǒng)一eslint對(duì)于項(xiàng)目的可維護(hù)性必不可少,今天我們一起學(xué)習(xí)一下如果改進(jìn)你項(xiàng)目的規(guī)范。
正文開始...
首先我們還是用之前搭建vue的一個(gè)項(xiàng)目從0到1開始配置eslint
安裝eslint
npm i eslint --save-dev
然后我們執(zhí)行初始化eslint命令
npm init @eslint/config
此時(shí)會(huì)讓我們選擇第三個(gè),并且選擇js modules, vue
當(dāng)你默認(rèn)選擇后就會(huì)生成一個(gè)文件.eslintrc.js,由于我添加了ts所以默認(rèn)也會(huì)添加@typescript-eslint,我們會(huì)發(fā)現(xiàn)package.json多了幾個(gè)插件@typescript-eslint/eslint-plugin、@typescript-eslint/parser,并且要安裝npm i typescript --save-dev
eslint規(guī)則是自己默認(rèn)選擇的配置
module.exports = {
env: {
browser: true,
es2021: true
},
extends: ['eslint:recommended', 'plugin:vue/essential', 'plugin:@typescript-eslint/recommended'],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['vue', '@typescript-eslint'],
rules: {
indent: ['error', 'tab'],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'never']
}
};
默認(rèn)生成的規(guī)則就是以上
我們運(yùn)行npx eslint ./src/index.js
執(zhí)行該命令就會(huì)檢測(cè)對(duì)于的文件是否符合eslint默認(rèn)配置的規(guī)則
添加eslint規(guī)則
在.eslintrc.js中,主要有以下5個(gè)部分
module.exports = {
env: {
browser: true,
es2021: true
},
extends: ['eslint:recommended', 'plugin:vue/essential', 'plugin:@typescript-eslint/recommended'],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['vue', '@typescript-eslint'],
rules: {
indent: ['error', 'tab'],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always']
}
};
env 支持的環(huán)境,根據(jù).browserslistrc瀏覽器預(yù)設(shè)的環(huán)境預(yù)設(shè)對(duì)應(yīng)的規(guī)則
module.exports = {
env: {
browser: true,
es2021: true,
es6: true
}
}
extends 繼承第三方的規(guī)則
module.exports = {
extends: ['eslint:recommended']
}
parserOptions 指定解析器選項(xiàng)
module.exports = {
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
}
}
plugins 插件
module.exports = {
plugins: ['vue', '@typescript-eslint'],
}
rules 具體對(duì)應(yīng)規(guī)則的設(shè)置
module.exports = {
rules: {
semi: 0 // 0 off,1 warn,2 error
},
}
參考一段之前業(yè)務(wù)有用到的統(tǒng)一eslint配置
// eslint配置
module.exports = {
root: true,
env: {
node: true,
},
parserOptions: {
parser: '@typescript-eslint/parser',
},
extends: [
'plugin:vue/essential',
'plugin:prettier/recommended',
'@vue/airbnb',
'@vue/typescript',
],
rules: {
'no-undef': 0, // 由于eslint無法識(shí)別.d.ts聲明文件中定義的變量,暫時(shí)關(guān)閉
'no-console': process.env.NODE_ENV === 'production' ? 2 : 0,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
indent: 0,
'linebreak-style': 0,
'no-trailing-spaces': 0,
'class-methods-use-this': 0,
'import/prefer-default-export': 0,
'no-restricted-syntax': 0,
'no-tabs': 0,
'import/no-unresolved': 0,
'no-underscore-dangle': 0,
'comma-dangle': 'off',
'max-len': 'off',
camelcase: 'off',
'object-curly-newline': 0,
'operator-linebreak': 0,
'guard-for-in': 0,
'import/no-webpack-loader-syntax': 0,
// 不安全項(xiàng)
'no-param-reassign': 0,
'no-dupe-class-members': 0,
'no-unused-vars': 0, // ts里面有校驗(yàn),可以把eslint 的校驗(yàn)關(guān)閉
// 提示警告
'no-return-await': 1,
'import/no-cycle': 1,
'no-nested-ternary': 1,
'no-new-func': 1,
'vue/no-side-effects-in-computed-properties': 1,
'vue/no-multiple-template-root': 'off', // vue3 模板可以有多個(gè)根結(jié)點(diǎn)
'vue/valid-template-root': 'off',
'vue/no-v-for-template-key': 'off', // vue3 v-for 中template 可以設(shè)置key
'vue/no-v-model-argument': 0,
'vue/no-use-v-if-with-v-for': 0,
'import/no-extraneous-dependencies': 1,
'no-continue': 1,
'operator-assignment': 1,
'no-bitwise': 1,
'prefer-destructuring': 2,
'array-callback-return': 2,
'func-names': 2,
'no-plusplus': 2,
'no-shadow': 2,
'no-mixed-operators': 2,
'no-fallthrough': 2,
'default-case': 2,
'no-useless-constructor': 2,
'no-unused-expressions': ["error", { "allowShortCircuit": true }],
// 關(guān)閉iview input組件,col組件個(gè)別標(biāo)簽報(bào)錯(cuò)
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
// 保證js、ts項(xiàng)目arrow風(fēng)格一致
'arrow-parens': [2, 'always', { requireForBlockBody: false }],
'implicit-arrow-linebreak': [0, 'beside'],
// ts 任意枚舉報(bào)錯(cuò)問題
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
},
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
env: {
jest: true,
},
},
],
};
選擇Airbnb風(fēng)格
在自定義自己的rules,也可以執(zhí)行npm init @eslint/config配置社區(qū)比較流行的自定義風(fēng)格,使用Airbnb
當(dāng)我們選擇airbnb風(fēng)格后,執(zhí)行npx eslint ./src/index.js
提示index.js有一個(gè)規(guī)則錯(cuò)誤
Expected 1 empty line after import statement not followed by another import import/newline-after-import我們將第三行換行就行
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');
我們看下生成的.eslintrc.js這個(gè)一般在你項(xiàng)目中多少有看到也可以是json類型
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:vue/essential',
'airbnb-base',
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: [
'vue',
],
rules: {
},
};
rules有很多的配置,可以參考官方[1]
運(yùn)行時(shí)檢測(cè)eslint
一般正常情況當(dāng)我們啟動(dòng)服務(wù)時(shí),如果我們代碼有寫得不規(guī)范,開發(fā)工具就終端就會(huì)給我們提示警告,此時(shí)我們需要eslint-loader[2],只需要這樣配置即可
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: [
'babel-loader', 'eslint-loader'
]
}
]
}
}
但是官方已經(jīng)不建議這么用了eslint-loader已經(jīng)停止了維護(hù),官方建議使用eslint-webpack-plugin
在webpack.config.js我們可以這么做
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
plugins: [
new ESLintPlugin()
]
}
當(dāng)我們運(yùn)行npm run server時(shí)就會(huì)檢查代碼錯(cuò)誤
提示在utils/index.js中不能使用console,很顯然,這條規(guī)則并不符合我們的初衷,我只需要在生產(chǎn)環(huán)境環(huán)境不打印console才行
當(dāng)我們修改.eslintrc.js時(shí),
module.exports = {
rules: {
'no-console': 0,
'import/extensions': ['error', 'always']
}
}
我們將rules規(guī)則的noconsole: 0允許使用console,當(dāng)我修改完時(shí),再次運(yùn)行,終端就不會(huì)報(bào)錯(cuò)了
我們?cè)偌觽€(gè)規(guī)則,max-params:2,函數(shù)形參不能到過三個(gè),如果超過三個(gè)就會(huì)報(bào)錯(cuò)
module.exports = {
rules: {
'no-console': 0,
'import/extensions': ['error', 'always'],
'max-params': 2
}
}
// utils/index.js
function test(a, b, c, d) {
console.log('hello', a, b, c, d);
}
test(1, 2, 3, 4);
因?yàn)槟J(rèn)max-params默認(rèn)最多就是3個(gè)參數(shù),所以在運(yùn)行時(shí)就提示報(bào)錯(cuò)了。于是你改成下面這樣就可以了
// utils/index.js
function test(a, ...rest) {
console.log('hello', ...rest);
}
test(1, 2, 3, 4);
vscode的eslint插件
除了eslint-webpack-plugin的插件幫我們?cè)诖a運(yùn)行時(shí)就可以檢測(cè)出代碼的一些不規(guī)范問題,我們通常可以結(jié)合vscode插件幫我更友好的提示,我們需要在寫代碼的時(shí)候,編輯器就已經(jīng)給我們提示錯(cuò)誤。
安裝完后,打開對(duì)應(yīng)文件,就會(huì)有對(duì)應(yīng)的提示
并且你可以通過提示跳轉(zhuǎn)到對(duì)應(yīng)的eslint
.prettierrc自動(dòng)格式化代碼
在vscode中裝上插件Prettier code formatter
然后在根目錄下創(chuàng)建.prettierrc.json文件
{
"singleQuote": true,
"printWidth": 150
}
設(shè)置編輯器的代碼長度printWidth是150,設(shè)置singleQuote單引號(hào)。
我們也需要設(shè)置一下vscode的settings.json,主要設(shè)置參照如下
然后添加一行自動(dòng)保存功能,這樣我們就可以保存時(shí),自動(dòng)格式化自己的代碼
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
}
因?yàn)閑slint既檢查了代碼又可以根據(jù).eslintrc.js美化代碼,但是prettierrc有時(shí)會(huì)與eslint的配置格式有沖突,所以此時(shí)vscode格式化的狀態(tài)就是混亂的,因此有時(shí)候很奇怪,所以你需要改settings.json默認(rèn)改成eslint,具體可以參考知乎這篇文章prettierrc[3]
網(wǎng)上關(guān)于prettierrc的配置有很多,具體上還是看組內(nèi)統(tǒng)一的規(guī)范,這里我貼一份之前項(xiàng)目格式化所用的,估計(jì)不同團(tuán)隊(duì)的配置絕大數(shù)是大同小異。
// .prettierrc.json
{
"eslintIntegration": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"proseWrap": "preserve",
"arrowParens": "avoid",
"bracketSpacing": true,
"disableLanguages": [
"vue"
],
"endOfLine": "auto",
"htmlWhitespaceSensitivity": "ignore",
"ignorePath": ".prettierignore",
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"requireConfig": false,
"trailingComma": "es5"
}
總結(jié)
eslint在項(xiàng)目中的配置,主要利用npm init @eslint/config快速初始化一份eslint配置,在試用前先進(jìn)行安裝npm i eslint --save-dev
開發(fā)環(huán)境使用eslint-loader,現(xiàn)在采用更多的是eslint-webpack-plugins
采用Airbnb風(fēng)格格式校驗(yàn)代碼
.prettierrc.json格式化代碼,不過注意與eslint格式?jīng)_突的問題。
本文示例code example[4]
參考資料
[1]
參考官方: https://eslint.org/docs/rules/
[2]
eslint-loader: https://www.npmjs.com/package/eslint-loader
[3]
prettierrc: https://zhuanlan.zhihu.com/p/347339865
[4]
code example: https://github.com/maicFir/lessonNote/tree/master/webpack/webpack-06-eslint
作者:Maic
歡迎關(guān)注微信公眾號(hào) :web技術(shù)學(xué)苑