項目eslint從零到一

eslint在項目里并不太陌生,通常在使用腳手架時,會默認讓你安裝執(zhí)行的eslint,當公司項目比較規(guī)范時,常常會配置組內(nèi)統(tǒng)一的eslint規(guī)則,eslint幫助我們在開發(fā)階段檢查代碼是否符合標準規(guī)范,統(tǒng)一了我們組內(nèi)不同項目代碼風格,也可以幫助我們養(yǎng)成良好的代碼習慣,統(tǒng)一eslint對于項目的可維護性必不可少,今天我們一起學(xué)習一下如果改進你項目的規(guī)范。

正文開始...

首先我們還是用之前搭建vue的一個項目從0到1開始配置eslint

安裝eslint
npm i eslint --save-dev
然后我們執(zhí)行初始化eslint命令

npm init @eslint/config
此時會讓我們選擇第三個,并且選擇js modules, vue



當你默認選擇后就會生成一個文件.eslintrc.js,由于我添加了ts所以默認也會添加@typescript-eslint,我們會發(fā)現(xiàn)package.json多了幾個插件@typescript-eslint/eslint-plugin、@typescript-eslint/parser,并且要安裝npm i typescript --save-dev
eslint規(guī)則是自己默認選擇的配置

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']
  }
};

默認生成的規(guī)則就是以上

我們運行npx eslint ./src/index.js


執(zhí)行該命令就會檢測對于的文件是否符合eslint默認配置的規(guī)則
添加eslint規(guī)則
在.eslintrc.js中,主要有以下5個部分

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è)對應(yīng)的規(guī)則

module.exports = {
  env: {
      browser: true,
      es2021: true,
      es6: true
  }
}
extends 繼承第三方的規(guī)則

module.exports = {
  extends: ['eslint:recommended']
}
parserOptions 指定解析器選項

module.exports = {
    parserOptions: {
      ecmaVersion: 'latest',
      parser: '@typescript-eslint/parser',
      sourceType: 'module'
    }
}
plugins 插件

module.exports = {
  plugins: ['vue', '@typescript-eslint'],
}
rules 具體對應(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無法識別.d.ts聲明文件中定義的變量,暫時關(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,
        // 不安全項
        'no-param-reassign': 0,
        'no-dupe-class-members': 0,
        'no-unused-vars': 0, // ts里面有校驗,可以把eslint 的校驗關(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 模板可以有多個根結(jié)點
        '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組件個別標簽報錯
        'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
        // 保證js、ts項目arrow風格一致
        'arrow-parens': [2, 'always', { requireForBlockBody: false }],
        'implicit-arrow-linebreak': [0, 'beside'],
        // ts 任意枚舉報錯問題
        '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風格
在自定義自己的rules,也可以執(zhí)行npm init @eslint/config配置社區(qū)比較流行的自定義風格,使用Airbnb







當我們選擇airbnb風格后,執(zhí)行npx eslint ./src/index.js

提示index.js有一個規(guī)則錯誤
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這個一般在你項目中多少有看到也可以是json類型






module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'plugin:vue/essential',
    'airbnb-base',
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: [
    'vue',
  ],
  rules: {
  },
};

rules有很多的配置,可以參考官方[1]

運行時檢測eslint
一般正常情況當我們啟動服務(wù)時,如果我們代碼有寫得不規(guī)范,開發(fā)工具就終端就會給我們提示警告,此時我們需要eslint-loader[2],只需要這樣配置即可

module.exports = {
  module: {
    rules: [
        {
        test: /\.(js|jsx)$/,
        use: [
          'babel-loader', 'eslint-loader'
        ]
      }
    ]
  }
}
但是官方已經(jīng)不建議這么用了eslint-loader已經(jīng)停止了維護,官方建議使用eslint-webpack-plugin

在webpack.config.js我們可以這么做

const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
  plugins: [
    new ESLintPlugin()
  ]
}
當我們運行npm run server時就會檢查代碼錯誤


提示在utils/index.js中不能使用console,很顯然,這條規(guī)則并不符合我們的初衷,我只需要在生產(chǎn)環(huán)境環(huán)境不打印console才行
當我們修改.eslintrc.js時,

module.exports = {
   rules: {
    'no-console': 0,
    'import/extensions': ['error', 'always']
  }
}
我們將rules規(guī)則的noconsole: 0允許使用console,當我修改完時,再次運行,終端就不會報錯了

我們再加個規(guī)則,max-params:2,函數(shù)形參不能到過三個,如果超過三個就會報錯

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);



因為默認max-params默認最多就是3個參數(shù),所以在運行時就提示報錯了。于是你改成下面這樣就可以了
// utils/index.js
function test(a, ...rest) {
  console.log('hello', ...rest);
}
test(1, 2, 3, 4);
vscode的eslint插件
除了eslint-webpack-plugin的插件幫我們在代碼運行時就可以檢測出代碼的一些不規(guī)范問題,我們通??梢越Y(jié)合vscode插件幫我更友好的提示,我們需要在寫代碼的時候,編輯器就已經(jīng)給我們提示錯誤。


安裝完后,打開對應(yīng)文件,就會有對應(yīng)的提示


并且你可以通過提示跳轉(zhuǎn)到對應(yīng)的eslint
.prettierrc自動格式化代碼

在vscode中裝上插件Prettier code formatter


然后在根目錄下創(chuàng)建.prettierrc.json文件

{
  "singleQuote": true,
  "printWidth": 150
}
設(shè)置編輯器的代碼長度printWidth是150,設(shè)置singleQuote單引號。

我們也需要設(shè)置一下vscode的settings.json,主要設(shè)置參照如下


然后添加一行自動保存功能,這樣我們就可以保存時,自動格式化自己的代碼
{
   "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
}
因為eslint既檢查了代碼又可以根據(jù).eslintrc.js美化代碼,但是prettierrc有時會與eslint的配置格式有沖突,所以此時vscode格式化的狀態(tài)就是混亂的,因此有時候很奇怪,所以你需要改settings.json默認改成eslint,具體可以參考知乎這篇文章prettierrc[3]

網(wǎng)上關(guān)于prettierrc的配置有很多,具體上還是看組內(nèi)統(tǒng)一的規(guī)范,這里我貼一份之前項目格式化所用的,估計不同團隊的配置絕大數(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在項目中的配置,主要利用npm init @eslint/config快速初始化一份eslint配置,在試用前先進行安裝npm i eslint --save-dev

開發(fā)環(huán)境使用eslint-loader,現(xiàn)在采用更多的是eslint-webpack-plugins

采用Airbnb風格格式校驗代碼

.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)注微信公眾號 :web技術(shù)學(xué)苑