項目 eslint 從零到一

eslint在項目裏並不太陌生,通常在使用腳手架時,會默認讓你安裝執行的eslint, 當公司項目比較規範時,常常會配置組內統一的eslint規則,eslint幫助我們在開發階段檢查代碼是否符合標準規範,統一了我們組內不同項目代碼風格,也可以幫助我們養成良好的代碼習慣,統一eslint對於項目的可維護性必不可少,今天我們一起學習一下如果改進你項目的規範。

正文開始...

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

安裝 eslint

npm i eslint --save-dev

然後我們執行初始化eslint命令

npm init @eslint/config

此時會讓我們選擇第三個, 並且選擇js modules, vue

當你默認選擇後就會生成一個文件.eslintrc.js, 由於我添加了ts所以默認也會添加@typescript-eslint,我們會發現package.json多了幾個插件@typescript-eslint/eslint-plugin@typescript-eslint/parser,並且要安裝npm i typescript --save-dev

eslint規則是自己默認選擇的配置

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

默認生成的規則就是以上

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

執行該命令就會檢測對於的文件是否符合eslint默認配置的規則

添加 eslint 規則

.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']
  }
};
module.exports = {
  env: {
      browser: true,
      es2021: true,
      es6: true
  }
}
module.exports = {
  extends: ['eslint:recommended']
}
module.exports = {
    parserOptions: {
      ecmaVersion: 'latest',
      parser: '@typescript-eslint/parser',
      sourceType: 'module'
    }
}
module.exports = {
  plugins: ['vue', '@typescript-eslint'],
}
module.exports = {
  rules: {
      semi: 0 // 0 off,1 warn,2 error
  },
}

參考一段之前業務有用到的統一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聲明文件中定義的變量,暫時關閉
        '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 的校驗關閉
        // 提示警告
        '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 模板可以有多個根結點
        'vue/valid-template-root': 'off',
        'vue/no-v-for-template-key': 'off', // vue3  v-for 中template 可以設置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 }],
        // 關閉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, 也可以執行npm init @eslint/config配置社區比較流行的自定義風格,使用Airbnb

當我們選擇airbnb風格後,執行npx eslint ./src/index.js

提示index.js有一個規則錯誤

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

一般正常情況當我們啓動服務時,如果我們代碼有寫得不規範,開發工具就終端就會給我們提示警告,此時我們需要 eslint-loader[2],只需要這樣配置即可

module.exports = {
  module: {
    rules: [
        {
        test: /\.(js|jsx)$/,
        use: [
          'babel-loader', 'eslint-loader'
        ]
      }
    ]
  }
}

但是官方已經不建議這麼用了eslint-loader已經停止了維護,官方建議使用eslint-webpack-plugin

webpack.config.js我們可以這麼做

const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
  plugins: [
    new ESLintPlugin()
  ]
}

當我們運行npm run server時就會檢查代碼錯誤

提示在utils/index.js中不能使用console, 很顯然,這條規則並不符合我們的初衷,我只需要在生產環境環境不打印console纔行

當我們修改.eslintrc.js時,

module.exports = {
   rules: {
    'no-console': 0,
    'import/extensions': ['error', 'always']
  }
}

我們將rules規則的noconsole: 0允許使用console, 當我修改完時,再次運行,終端就不會報錯了

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

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 個參數,所以在運行時就提示報錯了。於是你改成下面這樣就可以了

// utils/index.js
function test(a, ...rest) {
  console.log('hello', ...rest);
}
test(1, 2, 3, 4);

vscode 的 eslint 插件

除了eslint-webpack-plugin的插件幫我們在代碼運行時就可以檢測出代碼的一些不規範問題,我們通常可以結合vscode插件幫我更友好的提示, 我們需要在寫代碼的時候,編輯器就已經給我們提示錯誤。

安裝完後,打開對應文件, 就會有對應的提示

並且你可以通過提示跳轉到對應的eslint

.prettierrc 自動格式化代碼

vscode中裝上插件Prettier code formatter

然後在根目錄下創建.prettierrc.json文件

{
  "singleQuote": true,
  "printWidth": 150
}

設置編輯器的代碼長度 printWidth 是 150, 設置 singleQuote 單引號。

我們也需要設置一下vscodesettings.json, 主要設置參照如下

然後添加一行自動保存功能, 這樣我們就可以保存時,自動格式化自己的代碼

{
   "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
}

因爲eslint既檢查了代碼又可以根據.eslintrc.js美化代碼,但是prettierrc有時會與eslint的配置格式有衝突,所以此時 vscode 格式化的狀態就是混亂的,因此有時候很奇怪,所以你需要改settings.json默認改成eslint, 具體可以參考知乎這篇文章 prettierrc[3]

網上關於prettierrc的配置有很多,具體上還是看組內統一的規範,這裏我貼一份之前項目格式化所用的,估計不同團隊的配置絕大數是大同小異。

// .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"
}

總結

參考資料

[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

Web 技術學苑 專注前端 web 技術、分享 web 技術

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/N-F7WaBeao0DdsaYN2kX0A