eslint及stylelint规则详解(持续更新)

3,122 阅读10分钟

前言:

已经有很多文章讲解eslint等工具的配置流程,不再赘述,可参考文章很多。 最近在系统的将以前的项目统一lint规范,修改了一些lint告警和错误,整理下来,仅供参考。 使用过程中能感受到standard是一套并不非常严格的规则,还是相对友好的。

我的配置

eslint扩展只配置了vue+standard

module.exports = {
  root: true,
  parserOptions: {
    parser: "@babel/eslint-parser",
    sourceType: "module"
  },
  env: {
    es6: true,
    node: true,
    browser: true
  },
  extends: [
    "plugin:vue/recommended",
    "standard"
  ],
  plugins: [
    "vue"
  ],
  rules: {

  }
}

stylelint为scss扩展+recess-order+standard,后面会提加2条单独rules的原因。


module.exports = {
  extends: [
    "stylelint-config-standard",
    "stylelint-config-recess-order"
  ],
  plugins: [
    "stylelint-scss"
  ],
  ignoreFiles: [
    "**/*.{png,jpg,jpeg,gif,bmp,swf,svg,ico}",
    "**/*.{ttf,woff,eot}",
    "**/*.{yml,json}"
  ],
  rules: {
    "at-rule-no-unknown": null,
    "scss/at-rule-no-unknown": true
  }
}

注释

  1. eslintrc文件中可配置规则on/off/0/1/2/error/warning等

  2. eslintignore中可配置想忽略的文件夹/文件

  3. 如果1,2不满足需求,想在具体代码中配置忽略,如下:

    • 忽略整个文件:文件首行增加 /* eslint-disable */
    • 忽略代码块:首尾增加disable和enable
            /* eslint-disable */
                var a =1,b=2
                alert('foo');
            /* eslint-enable */
    
    • 忽略某行代码,以下4种均可
    alert('foo'); // eslint-disable-line
    
    // eslint-disable-next-line
    alert('foo');
    
    /* eslint-disable-next-line */
    alert('foo');
    
    alert('foo'); /* eslint-disable-line */
    
    
    • 忽略某行代码的单个/多个规则,在上述基础上增加规则即可:
    alert('foo'); // eslint-disable-line no-alert, quotes, semi
    
    // eslint-disable-next-line no-alert, quotes, semi
    alert('foo');
    
    alert('foo'); /* eslint-disable-line no-alert, quotes, semi */
    
    /* eslint-disable-next-line no-alert, quotes, semi */
    alert('foo');
    
    • 在vue的节点中忽略某行dom中的es校验 (场景举例:忽略对v-html的校验;需安装eslint-plugin-vue

    <!-- eslint-disable-next-line -->

    <!-- eslint-disable-next-line vue/no-v-html -->

stylelint忽略与eslint类似,不再赘述。

关于VUE扩展的强调

1.mixin文件需要在export default前增加一句// @vue/component使检测器将其视为组件。否则扩展对mixin文件不生效。详情查看eslintgithub

2.在standrard的eslintrc和项目本地的eslintrc中,rules没有配置任何关于vue扩展的规则,使用的就是vue-recommend的默认规则配置.下图每个文件中的规则可访问官方文档

3.项目未配置任何关于vue3的lint扩展。参考链接

image.png

ESlint规则记录

eslint-base

中文规则网站竟然不全,wolegedacao,贴个英文规则

基于eslint规则官网的顺序逐一列出standard采用的规则。

Possible Errors

1.no-async-promise-executor禁止使用异步函数作为 Promise executor

new Promise 构造函数接收一个 executor 函数作为参数,该函数具有 resolve 和 reject 两个参数 禁止该参数为异步函数,不能带async。

2.no-compare-neg-zero禁止与 -0 进行比较 (听说过,没见过,没事不要写什么负0)

3.no-cond-assign 该规则禁止在 ifforwhile 和 do...while 语句中出现模棱两可的赋值操作符。(ex:如果在if判断中手滑少写了个等号,会报错)

4. no-constant-condition禁止在以下语句的条件中出现常量表达式:

  • ifforwhile 或 do...while 语句
  • ?: 三元表达式 即if(0) if(true) 此类代码会报错

如下代码会同时触发3和4报错,首先使用了赋值单等号,赋值后成了常量。另:standard允许使用console,不允许有debugger

if (this.data = '') {
  console.log('111')
}

5.no-control-regex 禁止在正则表达式中使用控制字符

在 ASCII 中,0-31 范围内的控制字符是特殊的、不可见的字符。这些字符很少被用在 JavaScript 字符串中,所以一个正则表达式如果包含这些字符的,很有可能一个错误。

const regx = /\x0D/    //回车符
console.log(regx)
//error  Unexpected control character(s) in regular expression: \x0d  no-control-regex

6. no-debugger 禁用debugger

7.no-dupe-args 禁止 function 定义中出现重名参数。 (ex: fucntion test(a,b,a) //error)

8. no-dupe-keys禁止对象字面量中出现重复的 key, 即禁止对象同名属性

(ex: var myObj = {name:'Jon',name:'Lucy'} //error

9.no-duplicate-case switch case结构中,不允许有重复的case值

10.no-empty 禁止空代码块,会忽略包含一句注释的代码块。

standard配置为"no-empty": ["error", { "allowEmptyCatch": true }], 额外允许空的catch块

11. no-empty-character-class 禁止正则表达式中出现空字符集 (ex:/^abc[]/ 匹配个寂寞)

12.no-ex-assign禁止对catch的error重新赋值

13.no-extra-boolean-cast禁止不必要的布尔转换(auto fix

if(!!this.passWord)会被自动修复为if(this.passWord) if语句会自动做布尔转换,手动多余

14.no-extra-parens禁止多余的圆括号(auto fix) standard配置为"no-extra-parens": ["error", "functions"] 即允许函数周围多余的括号。

ex: typeof (a) 会被自动修复为typeof a

15.no-func-assign禁止对函数声明重新赋值

16. no-import-assign禁止对import的变量重新赋值

import mod, { named } from "./mod.mjs"
import * as mod_ns from "./mod.mjs"

//错误举例
mod = 1          // ERROR: 'mod' is readonly.
named = 2        // ERROR: 'named' is readonly.
mod_ns.named = 3 // ERROR: the members of 'mod_ns' is readonly.
mod_ns = {}      // ERROR: 'mod_ns' is readonly.


//推荐举例
mod.prop = 1
named.prop = 2
mod_ns.named.prop = 3

// Known Limitation
function test(obj) {
    obj.named = 4 // Not errored because 'obj' is not namespace objects.
}
test(mod_ns) // Not errored because it doesn't know that 'test' updates the member of the argument.

17.no-invalid-regexp禁止在 RegExp 构造函数中出现无效的正则表达式。 这条报错很难单独出现。因为经常和另一条规则 prefer-regex-literals 一起出现。 正则创建形式:

/ab+c/i; //字面量形式
new RegExp('ab+c', 'i'); // 首个参数为字符串模式的构造函数
new RegExp(/ab+c/, 'i'); // 首个参数为常规字面量的构造函数

单独触发no-invalid-regexp:

const flag = 'i'
const a = new RegExp('[', flag)
console.log(a)

如果使用let b = /[/ let c=/'['/这种字面量形式,解析器会直接报语法错误,但在RegExp构造函数中不会报错,所以增加了这条规则。但是很少有人这样单独定义flag和pattern来写正则,完全没有必要。如果不单独声明flag和pattern,会触发另一条规则 prefer-regex-literals standard的配置是 ["error", { "disallowRedundantWrapping": true }], 禁止构造函数中只使用字面量,多余的//包裹也会报错。

综上:除非使用动态的正则,优先采用字面量形式创建。

18.no-irregular-whitespace禁止不规则的空白。由于在Stylistic Issues风格指南中规定了各种空格规则且会自动fix,之后不会触发此规则。

19.no-loss-of-precision禁止丢失精度的数字 基础知识补一补

20. no-misleading-character-class 禁止在字符类语法中出现由多个代码点组成的字符 基础知识再补一补

21.no-obj-calls禁止把全局对象作为函数调用

错误 代码示例:

/*eslint no-obj-calls: "error"*/

var math = Math();
var json = JSON();
var reflect = Reflect();

正确 代码示例:

/*eslint no-obj-calls: "error"*/

function area(r) {
    return Math.PI * r * r;
}
var object = JSON.parse("{}");
var value = Reflect.get({ x: 1, y: 2 }, "x");

22. no-prototype-builtins禁止直接调用 Object.prototypes 的内置属性。

错误 代码示例:

/*eslint no-prototype-builtins: "error"*/

var hasBarProperty = foo.hasOwnProperty("bar");

var isPrototypeOfBar = foo.isPrototypeOf(bar);

var barIsEnumerable = foo.propertyIsEnumerable("bar");

正确 代码示例:

/*eslint no-prototype-builtins: "error"*/

var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");

var isPrototypeOfBar = Object.prototype.isPrototypeOf.call(foo, bar);

var barIsEnumerable = {}.propertyIsEnumerable.call(foo, "bar");

23.no-regex-spaces禁止正则表达式字面量中出现多个空格(auto fix

24. no-sparse-arrays 禁用稀疏数组 错误示例: var items = [,]; var colors = [ "red",, "blue" ];

25. no-template-curly-in-string 禁止在常规字符串中出现模板字面量占位符语法,

即不允许var msg = "Hello ${name}!"之类的写法

  1. no-unexpected-multiline禁止出现令人困惑的多行表达式。看举例更方便明白意图。

错误 代码示例:

/*eslint no-unexpected-multiline: "error"*/

var foo = bar
(1 || 2).baz();

var hello = 'world'
[1, 2, 3].forEach(addNumber);

let x = function() {}
`hello`

let x = function() {}
x
`hello`

let x = foo
/regex/g.test(bar)

正确 代码示例:

/*eslint no-unexpected-multiline: "error"*/

var foo = bar;
(1 || 2).baz();

var foo = bar
;(1 || 2).baz()

var hello = 'world';
[1, 2, 3].forEach(addNumber);

var hello = 'world'
void [1, 2, 3].forEach(addNumber);

let x = function() {};
`hello`

let tag = function() {}
tag `hello`

27. no-unreachable禁止在 returnthrowcontinue 和 break 语句之后出现不可达代码

28. no-unreachable-loop 禁止最多可进行一次迭代的循环。如在循环中写一定执行的break,如下错误示例

for (foo of bar) {
    if (foo.id === id) {
        doSomething(foo);
    }
    break;
}

29.no-unsafe-finally禁止在 finally 语句块中出现控制流语句。JavaScript 暂停 try 和 catch 语句块中的控制流语句,直到 finally 语句块执行完毕。所以,当 returnthrowbreak 和 continue 出现在 finally 中时, try 和 catch 语句块中的控制流语句将被覆盖,这被认为是意外的行为。

30.no-unsafe-negation 禁止对关系运算符的左操作数使用否定操作符 (auto fix) 该规则禁止对关系运算符的左操作数使用否定操作符。

关系运算符有:

  • in 运算符.
  • instanceof 运算符.

错误 代码示例:

/*eslint no-unsafe-negation: "error"*/

if (!key in object) {
    // operator precedence makes it equivalent to (!key) in object
    // and type conversion makes it equivalent to (key ? "false" : "true") in object
}

if (!obj instanceof Ctor) {
    // operator precedence makes it equivalent to (!obj) instanceof Ctor
    // and it equivalent to always false since boolean values are not objects.
}

正确 代码示例:

/*eslint no-unsafe-negation: "error"*/

if (!(key in object)) {
    // key is not in object
}

if (!(obj instanceof Ctor)) {
    // obj is not an instance of Ctor
}

if(("" + !key) in object) {
    // make operator precedence and type conversion explicit
    // in a rare situation when that is the intended meaning
}

31.no-useless-backreference 禁止在正则表达式中使用无用的反向引用,没仔细研究

32. use-isnan ["error", {"enforceForSwitchCase": true, "enforceForIndexOf": true}], 禁止在各种判断的地方直接使用NaN,必须使用isNaN来判断,包括switch case分支和indexOf

33. valid-typeof ["error", { "requireStringLiterals": true }] typeof只能与typeof预期的几个字符串结果比较

Best Practices

1. accessor-pairs ["error", { "setWithoutGet": true, "enforceForClassMembers": true }],强制 getter 和 setter 在对象中成对出现 ,但只有setter不报错,报warning。enforceForClassMembers对class中的get,set也生效.

eslint-recommand

1. no-case-declarations no-case-declarations

2.no-throw-literal

eslint-plugin-vue

1. vue/require-default-prop warning Prop 'clientType' requires default value to be set

2. vue/require-prop-type-constructor error The "clientType" property should be a constructor

props: {
    clientType: ''
  },

针对props,要求有默认值且type为构造函数,修改如下

props: {
    clientType: {
      default: '',
      type: String
    }
  },

如果为对象,如下写法将触发报错3

props: {
    clientType: {
      default: '',
      type: Object
    }
  },

3. vue/require-valid-default-prop error Type of the default value for 'clientType' prop must be a function
此规则检查对于给定的type,default是否有效。Arrary和Object的default必须是函数。修改如下:

props: {
      clientType: {
          type: Object,
          default() {
            return { message: 'hello' }
          }
      }
    },

4. vue/no-use-v-if-with-v-for vue对v-for的处理优先级高,导致每次循环都会执行一遍v-if,目前项目中有大量的v-if和v-for放在一个节点上,实实在在的影响了执行效率.推荐外层增加一层template使用v-if

<ul v-if="complete"> 
    <TodoItem v-for="todo in todos" :todo="todo" />
</ul>

eslint-plugin-node

standrd配置了如下7条

1."node/handle-callback-err": ["error", "^(err|error)$" ] 一定要处理回调函数的error或err参数

以^开头表示正则匹配

  • 如果选项是"^(err|error|anySpecificError)$",则规则报告参数名称可以是err,error或 的未处理错误anySpecificError
  • 如果选项为"^.+Error$",则规则报告参数名称以Error(例如,connectionErrorvalidationError将匹配)结尾的未处理错误。
  • 如果选择是"^.*(e|E)rr",该规则报告,其中的参数名称中包含任何字符串匹配未处理的错误errErr(例如errerroranyErrorsome_err将匹配)。
//如下回调函数传参为data,不报错。reject特意传参new Error()是因为有另一条规则
//Expected the Promise rejection reason to be an Error  prefer-promise-reject-errors
function (data) {
    return Promise.reject(new Error())
  })
  
//把data改为err或error,同样不使用,会报错  Expected error to be handled  node/handle-callback-err 
function (error) {
    return Promise.reject(new Error())
  })

2."node/no-callback-literal": "error" 调用名为cb或callback的函数时,第一个参数不能为字面量

/*如下代码会触发报错触发报错 
error  Unexpected literal in error position of callback  node/no-callback-literal*/

mounted () {
    function callback (a) {
      console.log(a)
    }
    callback('a')
  },
 //注:在methods里定义callback(){}, this.callback('字面量')不会报错
 
 
 /*不报错的调用*/
cb(undefined);
cb(null, 5);
callback(new Error('some error'));
callback(someVariable);

3."node/no-deprecated-api": "error" 禁用废弃的node api

  //例如 url.prase废弃,推荐改为如下写法
  let params = (new URL(window.location)).searchParams;
  let lang = params.get('lang')

4."node/no-exports-assign": "error" 应该是不许给export直接赋值

👍 Examples of **correct** code for this rule:

/*eslint node/no-exports-assign: error */

module.exports.foo = 1
exports.bar = 2

module.exports = {}

// allows `exports = {}` if along with `module.exports =`
module.exports = exports = {}
exports = module.exports = {}


👎 Examples of **incorrect** code for this rule:


/*eslint node/no-exports-assign: error */

exports = {}

5."node/no-new-require": "error" 禁止new关键字和require一起使用

//不推荐
var appHeader = new require('app-header');
//推荐
var AppHeader = require('app-header');
var appHeader = new AppHeader();

6."node/no-path-concat": "error" 禁止带正反斜杠的字符串连接__dirname__filename(不完全确定)

开发人员可能会尝试使用这些变量来创建其他文件的路径,例如:

var  fullPath  =  __dirname  +  "/index.js" ;

这种方式,需要配合运行的操作系统判断的代码,官方不推荐,应使用path模块的join或resolve

var fullPath1 = path.resolve(__dirname, "index.js");

var fullPath2 = path.join(__dirname, "index.js");

报错写法举例

/*eslint node/no-path-concat: "error"*/

const fullPath1 = __dirname + "/foo.js";
const fullPath2 = __filename + "/foo.js";
const fullPath3 = `${__dirname}/foo.js`;
const fullPath4 = `${__filename}/foo.js`;

正确写法举例

const fullPath1 = path.join(__dirname, "foo.js");
const fullPath2 = path.join(__filename, "foo.js");
const fullPath3 = __dirname + ".js";
const fullPath4 = __filename + ".map";
const fullPath5 = `${__dirname}_foo.js`;
const fullPath6 = `${__filename}.test.js`;

7."node/process-exit-as-throw": "error"

执行--fix能自动修复的规则列表

no-extra-boolean-cast禁止不必要的布尔转换

no-regex-spaces禁止正则表达式字面量中出现多个空格

no-extra-parens禁止多余的圆括号(auto fix)standard配置为"no-extra-parens": ["error", "functions"] 即允许函数周围多余的括号。

no-unsafe-negation 禁止对关系运算符的左操作数使用否定操作符

standard未采用的eslint规则

for-direction

getter-return

no-await-in-loop

no-console

no-extra-semi standard配置了无分号,覆盖此规则

no-inner-declarations禁止在嵌套的语句块中声明函数

no-dupe-else-if 禁止else if分支条件重复

no-promise-executor-return

no-setter-return

no-unsafe-optional-chaining

中文站缺失的规则

no-dupe-else-if 禁止else if分支条件重复

no-import-assign

no-loss-of-precision

no-promise-executor-return

此规则的错误代码示例:

/*eslint no-promise-executor-return: "error"*/

new Promise((resolve, reject) => {
    if (someCondition) {
        return defaultResult;
    }
    getSomething((err, result) => {
        if (err) {
            reject(err);
        } else {
            resolve(result);
        }
    });
});

new Promise((resolve, reject) => getSomething((err, data) => {
    if (err) {
        reject(err);
    } else {
        resolve(data);
    }
}));

new Promise(() => {
    return 1;
});

此规则的正确代码示例:

/*eslint no-promise-executor-return: "error"*/

new Promise((resolve, reject) => {
    if (someCondition) {
        resolve(defaultResult);
        return;
    }
    getSomething((err, result) => {
        if (err) {
            reject(err);
        } else {
            resolve(result);
        }
    });
});

new Promise((resolve, reject) => {
    getSomething((err, data) => {
        if (err) {
            reject(err);
        } else {
            resolve(data);
        }
    });
});

Promise.resolve(1);

no-setter-return

no-unreachable-loop

no-unsafe-optional-chaining 禁止在不允许使用undefined值的上下文中使用可选链. 可选的链接 ( ?.) 表达式可以短路,返回值为undefined。因此,将评估的可选链接表达式视为函数、对象、数字等,可能会导致 TypeError 或意外结果。例如:

var obj = undefined;

1 in obj?.foo;  // TypeError
with (obj?.foo);  // TypeError
for (bar of obj?.foo);  // TypeError
bar instanceof obj?.foo;  // TypeError
const { bar } = obj?.foo;  // TypeError

此外,括号限制了链中短路的范围。例如:

var obj = undefined;

(obj?.foo)(); // TypeError
(obj?.foo).bar; // TypeError

no-useless-backreference