ESLint里的规则教会我,无规矩 不编程

编程风格要统一

编程风格这个东西,说实在的对于刚加入团队的新成员来说还是很难让人完全适应的。因为每人的编程风格都不同,完全是各有千秋的既视感啊

到了新公司后团队中的每个人也都有各自一套的编程习惯,撸起代码来是挡也挡不住啊,什么都别问,老夫就是干,哈哈,每个coder的代码风格都大相径庭

不过话说回来,在团队开发中,所有的代码看起来风格一致是极其重要的,所以我们也需要一些代码检查工具,从JSLint,JSHint到今天的主角ESLint,都是非常好用的检查工具

那么闲言少叙,直接进入主题,投入ESLint的怀抱吧!!!

ESLint是伟大的作品

放眼望去,在前端,但凡有个项目,无论大小,都会看到它的身影。并非为了提高比格,让别人暗赞专业,而是它确确实实帮我们检测出不易察觉的错误,避免N多个线上bug

也通过其规则,让整个团队有了整齐划一的代码输出风格,也有业界的最佳实践写法,深入其中,受益匪浅…..好处优点不多说,用过都说好

既然都说好了,那还废话那么多,小二赶紧上菜啊

安装ESLint

现在前端项目开发中,都用到了webpack此等屌屌的构建工具(如果有对webpack不熟悉使用的可移步这里),那么就结合着一起来看下如何使用到项目里吧

  1. 首先安装一下
// 安装eslint
// 安装eslint-loader 在webpack中解析
// 安装babel-eslint  对Babel解析器的包装与ESLint兼容
// -D 安装在开发依赖环境 devDependencies 原--save-dev的简写
npm i eslint eslint-loader babel-eslint -D

友情提示:ESLint是基于Node的(当然webpack也是),所以在使用之前,请确保Node已经安装

2. 创建.eslintrc.js配置文件(默认是.eslintrc文件)

// .eslintrc.js
module.exports = {
    // 指定解析器
    'parse': '',
    // 指定解析器选项
    'parserOptions': {},
    // 指定脚本的运行环境
    'env': {},
    // 别人可以直接使用你配置好的ESLint
    'root': true,
    // 脚本在执行期间访问的额外的全局变量
    'globals': {},
    // 启用的规则及其各自的错误级别
    'rules': {}
};
  1. 将配置好的规则添加到webpack中对js文件检查
// webpack.config.js

module.exports = {
    entry: '',
    output: {},
    module: {
        rules: [
            {
                test: /\.js$/,
                use: ['babel-loader', 'eslint-loader']
            }
        ]
    },
    plugins: [],
    devServer: {}
};

按照上面的三步曲写完后,应该给自己撒花的,哈哈

ESLint里的规则教会我,无规矩 不编程

再放一张打包的index.js主文件的代码

ESLint里的规则教会我,无规矩 不编程

该配置的都配置完了,那就迫不及待的执行npm run dev吧,看看是不是完全OK的编译成功呢?

然并卵啊,高兴的太早了

ESLint里的规则教会我,无规矩 不编程

上图之所以会有error和warning的报错提示,是因为参照了下面我在公司中使用的ESLint配置规则导致的,那么就赶紧进入下一环节,看看这些规则吧

工作中用到的规则

正所谓人在江湖飘啊,哪有不挨刀的道理。谁敢说写的代码放到ESLint里完全不报错,报错纯属正常,没必要慌张

现在针对一些比较常见的规范来简单梳理一下,这么多规则,其实看的我也吃了一鲸

现在来看看编译过程中报错的规则吧

非友情提示:每个规则对应的0,1,2分别表示off, warning, error三个错误级别

  • no-unused-vars
    • 定义了变量却没有在代码中使用,这是防止产生多余没用的变量
  • semi
    • 缺少分号,行尾必须使用分号,这是为了在压缩代码的时候出现意外情况
  • no-console
    • 禁止使用 console,提醒开发者,上线时要去掉,因为是warning不会导致编译的js出问题
  • consistent-this
    • this的别名规则,只允许self和that,防止有些人写成_this或者me等等,哈哈
  • curly
    • if 后必须包含 { ,单行 if 除外,也是为了方便阅读代码
// 错误写法
function fn (key) {
   if (key === 'a') 
       return 1;
}
fn('a');
// 正确写法
function fn (key) {
   if (key === 'a') {
       return 1;
   }
   if (key === 'b') return 2;
}
fn('a');
  • default-case
    • switch 语句必须包含 default
// 错误写法
function fn (key) {
    let str = '';

    switch (key) {
        case 'a':
            str = 'a';
            break;
        case 'b':
            str = 'b';
            break;
    }
    return str;
}
// 正确写法
function fn (key) {
    let str = '';

    switch(key) {
        case 'a':
            str = 'a';
            break;
        case 'b':
            str = 'b';
            break;
        default:
            str = 'c';
            break;
    }
    return str;
}
  • eqeqeq
    • 必须使用全等===进行比较,防止隐式转换带来的意外问题
  • guard-for-in
    • for in时需检测hasOwnProperty,避免遍历到继承来的属性方法
  • max-depth
    • 最大块嵌套不能超过5层
// 正确写法
if () {
    if () {
        if () {
            if () {
                if () {
                    
                }
            }
        }
    }
}
  • max-params
    • 函数的形参不能多于8个,如果形参过多,我们现在可以用扩展运算符…来代替后面多余的形参
  • new-cap
    • new关键字后类名应首字母大写,区分类和函数
  • no-array-constructor
    • 禁止使用Array构造函数,定义数组直接用最快捷的方式[1, 2, 3]
  • no-await-in-loop
    • 禁止将await写在循环里,循环属于同步操作,不该将await异步操作写在内部
  • no-caller
    • 禁止使用arguments.caller和arguments.callee,ES6中废弃了
  • no-const-assign
    • 禁止对const定义重新赋值
  • no-delete-var
    • 禁止对变量使用delete关键字,delete只适用于对象的属性,提醒使用的范围
  • no-dupe-args
    • 函数参数禁止重名
  • no-empty-function
    • 禁止空的function,保证写的每一个function都有用
  • no-eval
    • 禁止使用eval,eval是“魔鬼”,所以在开发中避免
  • no-extra-semi
    • 禁止额外的分号,有些地方没必要加分号比如if () {};这样就是错误的
  • no-global-assign
    • 禁止对全局变量赋值

好了还有一些就需要大家在使用中来去体会了,下面我将我司工作当中用到的ESLint规则贴出来,方便大家去做代码检查了

// .eslintrc.js
module.exports = {
    // 解析ES6
    'parser': 'babel-eslint',
    'parserOptions': {
        // 启用ES8语法支持
        'ecmaVersion': 2017,    
        // module表示ECMAScript模块
        'sourceType': 'module',
        // 使用额外的语言特性
        'ecmaFeatures': {
            'experimentalObjectRestSpread': true,
            'jsx': true,
            'modules': true,
        }
    },
    // 这些环境并不是互斥的,所以你可以同时定义多个
    'env': {
        'browser': true,
        'jquery': true,
        'node': true,
        'commonjs': true,
        'es6': true,
    },
    'root': true,
    // 当访问当前源文件内未定义的变量时,no-undef 规则将发出警告
    // 所以需要定义这些额外的全局变量
    'globals': {
        'OnlySVG': true,
        'monitor': true,
        'CanvasRender': true,
        'Vue': true,
        'VueRouter': true
    },
    'rules': {
        // 设置了 setter ,必须相应设置 getter ,反之不必须
        'accessor-pairs': 2,

        // 数组方括号前后的换行符使用规则
        // @off 不关心
        'array-bracket-newline': 0,

        // 数组方括号前后的空格使用规则
        // @off 不关心
        'array-bracket-spacing': 0,

        // 数组的 map、filter、sort 等方法,回调函数必须有返回值
        'array-callback-return': 2,

        // 每个数组项是否独占一行
        // @off 不关心
        'array-element-newline': 0,

        // 箭头函数的书写规则
        // @off 不限制
        'arrow-body-style': 0,

        // 箭头函数的圆括号使用规则
        // @off 不限制
        'arrow-parens': 0,

        // 箭头函数的空格使用规则
        // @off 不限制
        'arrow-spacing': 0,

        // 不能在块外使用块作用域内 var 定义的变量
        'block-scoped-var': 2,

        // 代码块花括号前后的空格规则
        // @off 不关心
        'block-spacing': 0,

        // if else 的花括号换行规则
        // @off 不关心
        'brace-style': 0,

        // callback 之后必须立即 return
        // @off 没必要
        'callback-return': 0,

        // 变量名必须使用驼峰式
        // @off 暂不限制
        'camelcase': 0,

        // 注释的首字母应该大写
        // @off 没必要
        'capitalized-comments': 0,

        // class 的非静态方法必须包含 this 关键字
        'class-methods-use-this': 2,

        // 对象的最后一项后面是否写逗号
        // @off 此项目不关心
        // @fixable 对于 PC 项目考虑兼容性时需要设置
        'comma-dangle': 0,

        // 逗号前后是否有空格
        // @off 不关心
        'comma-spacing': 0,

        // 逗号写在行首还是行尾
        // @off 不关心
        'comma-style': 0,

        // 禁止函数 if ... else if ... else 的复杂度超过 20
        'complexity': 2,

        // 使用方括号访问对象属性时,方括号前后的空格规则
        // @off 不关心
        'computed-property-spacing': 0,

        // 禁止函数在不同条件下返回不同类型的值
        // @off 有时候会希望通过参数获取不同类型的返回值
        'consistent-return': 0,

        // this 的别名规则,只允许 self 或 that
        'consistent-this': [2, 'self', 'that'],

        // 构造函数中必须调用 super
        // @off 没必要
        'constructor-super': 0,

        // if 后必须包含 { ,单行 if 除外
        'curly': [2, 'multi-line', 'consistent'],

        // switch 语句必须包含 default
        'default-case': 2,

        // 链式操作时,点的位置,是在上一行结尾还是下一行开头
        // @off 不关心
        'dot-location': 0,

        // 文件最后必须有空行
        // @off 不限制
        'eol-last': 0,

        // 必须使用 === 和 !== ,和 null 对比时除外
        'eqeqeq': [2, 'always', { 'null': 'ignore' }],

        // for 循环不得因方向错误造成死循环
        'for-direction': 2,

        // 执行函数的圆括号前后的空格规则
        // @off 不关心
        'func-call-spacing': 0,

        // 把函数赋给变量或对象属性时,函数名和变量名或对象属性名必须一致
        // @off 不限制
        'func-name-matching': 0,

        // 不允许匿名函数
        // @off 不限制
        'func-names': 0,

        // 必须只使用函数申明或只使用函数表达式
        // @off 不限制
        'func-style': 0,

        // generator 的 * 前后空格使用规则
        // @off 不限制
        'generator-star-spacing': 0,

        // getter 必须有返回值,允许返回 undefined