规范化介绍

这里我们来看一下关于规范化内容的介绍,规范化使我们践行前端工程化过程中重要的组成部分,在这里我们会通过一下几个方面来进行介绍和说明。

首先我们会去说明为什么需要有规范化的标准,然后我们会去介绍在开发过程中哪些地方是需要使用这些规范化标准操作的。最后当我们有了这些规范化标准之后肯定要应用于自己的项目当中。在这个时候呢就应该有具体的实施方法。所以我们也会去介绍一些具体的实施规范化标准操作的方法。

那么在这里我们就先看一下为什么我们要有这些规范化的标准。

俗话说没有规矩不成方圆,做人做事都是一样,尤其是在开发行业中,更是需要有严谨的工作态度。我们都知道大多数情况下,软件开发都不是一个人的工作,都是需要多人协同的,而不同的开发者有不同的编码习惯和喜好。

这些个人的喜好并没有什么不好的地方,只是说如果同一个项目中,每个人的喜好都不相同。那就会导致项目的维护成本大大提高。

所以说我们就需要为每个项目或者说团队明确一个统一的标准,让这个项目或者说团队中的成员,都按照统一的标准去完成工作,从而避免各种不统一而带来的一些麻烦。

那么知道了为什么需要规范化标准之后,我们再来看一下,在开发过程中那些地方时具体需要用到这些规范化标准操作的。其实也很简单,我们在开发过程中所编写的代码,文档,甚至是提交的日志都需要去进行标准化的操作。

总之凡是开发过程中人为编写而产生的一些内容,都应该去被规范化操作,其中代码的标准化规范呢最为重要。因为代码的规范很大程度上决定了我们项目的质量也决定了项目的可维护性质。

为了便于后期维护和团队其他成员的阅读,一般情况下我们都会对代码的编码风格去做一个统一的要求,在这其中一般都会包括,统一关键词和操作符左右的空格;统一代码的缩进方式;统一是否使用分号结尾;统一变量或者是函数的命名规范;等等还有一些其他的我们在后面去进行使用。

知道了这些需要规范化操作的地方,以及一些规范化标准之后,我们就来看一下具体有哪一些方法可以去完成这些规范化的操作。

最初我们在落实规范化的操作时,其实也非常的简单,只需要我们提前约定好一个可以执行的标准,然后按照这个标准进行各自的开发工作。

最终我们在code review的环节就会按照之前约定的标准去进行检查相应的代码,但是如果单靠人为约束的方式落实规范化会有很多的问题。一来人为约束不可靠,二来开发者也很难记住每个规则,所以我们就需要有专门的工具加以保障。

相比于人为检察,工具的检查更为严谨,更为可靠。同时还可以配合自动化的工具实现自动化检查,这样的规范化就更加容易得到质量上的保证。

一般我们把通过工具去找到项目中不合规范的地方这样的一个过程称之为Lint,之所以称之为Lint的原因是因为刚有C语言的时候,有一些常见的代码问题是不能被编译器捕获到的,所以有人就开发了一个叫做Lint的工具,用于在编译之前检查出这些问题,避免编译之后带来一些不必要的问题。

所以后续这种类似功能的工具都被称之为叫做Lint,或者说linter,例如现在前端最常见的es-lint,style-lint等等。

说完了这些之后呢我们就一起具体的去摸索着看一下,如何使用这些工具去落实我们前端项目的规范化操作。

在这个过程中我们会去看到以下这样一些内容。比如我们会去演示一下ESLint工具的使用,同时也会去定制一些ESLint的校验规则。知道这个操作以后我们会去看一下ES Lint对TypeScript的一些语法支持。之后我们还会去演示一下ESLint结合智能化的工具或者说Webpack这样的一些打包工具进行我们项目的自动化的一个校验操作。最后我们还会介绍一些基于ESLint的衍生工具。然后我们还会去说一下Stylelint是如何对我们当前css进行校验操作的。最后我们还要去演示一下我们当前的githook去配合我们这样一些ESLint工具如何去在我们代码提交之前进行一些智能化的校验,从而让我们的代码在进行集成之前就可以通过我们的校验。

那这些就是关于我们规范化操作的一些内容介绍。

ESLint介绍

这里我们来看一下关于ESLint的基本介绍,之前我们已经知道了,当下采用工具去完成项目代码的校验工作是更加高效和合理的,在这里我们使用的就是ESLint。

他是我们目前最为主流的javascrit lint工具,专门用于监测javascript代码的质量。通过ESLint就可以很容易的去统一不同开发者的编码风格。例如我们的缩进,换行,分号以及空格之类的使用。

不仅如此,ESLint还可以帮助我们找出代码当中一些不合理的地方,例如我们定义了一个从未使用的变量,或者说我们在一个变量使用之后才去他进行声明。再或者说我们在进行比较的时候往往总是选择==的符号。等等还有很多其他的一些操作。

而这些不合理的操作一般都是我们代码当中所存在的一些潜在问题,通过ESLint就能够有效的避免这些问题,从而去提高我们代码的质量。

另一方面我个人认为ESLint也可以去帮助开发者提升编码能力,为什么这么说呢,设想一下如果编写的代码每次在执行lint操作的时候都能够找出一堆的问题,而这些问题大都是以往编码时候的坏习惯。

慢慢的就应该记住了这些问题,正常来说当下次遇到的时候自己就会主动避免他们,那么久而久之你的编码能力自然而然的就得到了一个提升。

其实总结一下就是想要去表达,无论出于提高项目编码质量的原因还是提升自身编码水平的原因,ESLint都有很大的价值。

那么接下来我们就通过一些尝试具体的来体会ESLint的这些优势,顺便我们去掌握这一类lint工具的使用规律。以便后面我们接触到其他lint工具的时候可以做到触类旁通。最后去做到以不变应万变。

ESLint安装

首先我们使用ESLint就是为了校验项目的代码,因此我们需要先有一个项目,而在这个项目中该如何使用ESLint呢?

他其实就是一款基于NodeJs开发的npm模块,所以我们想要使用ESLint也就需要先通过npm或者yarn安装这个模块。

最后完成安装之后我们就可以通过简单的命令来校验安装操作是否成功,明确了这些操作步骤之后我们就可以回到开发工具当中进行具体的实操。

在这里我们打开一个空的示例项目,我们先使用npm init --yes初始化项目的package.json文件用于管理项目的npm依赖。

npm init

有了package.json我们就可以安装ESLint模块了, 我们这里采用项目内安装依赖。

npm install eslint --save-dev

这里说个题外话,我们已经很少全局去安装某个npm 模块了,因为大多数情况下都是具体项目依赖某个模块,把模块安装到项目本地,让他跟着项目一起管理会更加合理。

而且别人在拿到你的项目过后不需要单独的去关心你这个项目依赖了哪些全局模块,直接通过npm install就可以安装必要的工具模块,这也就从侧面提高了项目的可维护性。

由于eslint提供了一个cli程序,所以安装完成eslint模块之后在node_modules的bin目录里就会多出一个eslint的可执行文件。

后续我们就可以直接通过这个cli程序去监测代码当中的问题。回到命令行,找到这个eslint的可执行文件。输入--version我们就看到了这个eslint所使用的版本。

cd node_modules
cd .bin
eslint --version

通过之前的学习我们已经知道,node_modules的bin目录下的工具可以直接使用npx或者yarn快速找到,不需要进入到具体的目录。

如果你使用的yarn可以直接执行yarn eslint,因为yarn会自动找到bin目录下的命令,npx是npm新集成的工具,你只要是安装了对应的npm版本就会拥有npx命令。

npx eslint --version // yarn eslint --version

最后我们补充一点题外话,不要纠结是使用npm还是使用yarn,他们两者之间其实没有绝对的好坏之分,各有各的优势,你就按照你所在团队或者项目的具体要求使用其中任何一款即可。

以上就是关于eslint安装的简单介绍。下面我们会去具体的看一下关于eslint的一些功能表现。

ESLint 快速上手

这里我们来看一下ESLint快速上手的相关内容,当我们可以执行eslint模块的安装操作之后,我们就来通过一个案例具体的来看一下eslint在项目代码检查方面的具体表现,首先我们还是快速的看一下后面操作的步骤说明。

在最开始的时候我们需要去新建一个项目, 并且完成相应的npm初始化操作,同时安装好对应的ESLint模块。

npm install eslint --save-dev

然后我们在这个项目当中去新建一个JS文件,同时在这个JS文件中编写一些问题代码,在这之后就可以去执行ESLint相应的命令来进行代码的检查。

但是在第一个使用ESLint操作之前必须要完成相应的配置,然后才能去进行正常的使用,这些配置我们接下来会进行具体的讲解。

首先完成npm初始化工作,这个步骤大家基本都了解,这里就不详细讲述了,项目中安装eslint模块。

在项目中新建一个js文件。我们这里叫main.js.

我们这里故意调用fn的时候少些一个) 然后再去调用一个不存在的函数syy

const foo=123;

function fn() {
    console.log("hello");
        console.log("eslint);
}

fn(
syy();

在这个main.js中无论是代码还是格式还是合理性都存在一些问题, 接下来我们就尝试用ESLint去找到这些细节性的问题。

我们通过运行eslint,来查找这些问题,第一次运行时我们需要对eslint进行初始化。

npx eslint --init

运行之后会打印一些交互性的问题,第一个问题一般会问我们如何使用,这里会给出来三个选项,第一个是只检查语法错误,第二个是检查语法错误并且发现问题代码,第三个是检查错误语法,发现错误并且校验代码风格。很明显第三个是最全面的。

这里我们来具体说一下这三个功能的具体含义。

第一个语法错误很好理解,在我们的实例中fn函数的调用就是一个语法错误,因为我们少了一个小括号。

而问题代码就是指代码中不合理的地方,例如这里我们定义了一个未被使用的变量,或者说调用了一个不存在的函数。这些都属于问题代码。

第三个代码风格是最容易理解的。在最初的时候我们对ESLint的期望值就是,希望他能去找出代码当中子编码风格上存在的一些问题。例如我们代码当中的缩进是不统一的。

我相信你看到这里就应该能感受到ESLint带来的具体价值了。

知道了这些之后我们在这里就能去做出一些选择了,我们这里选择第三个。在实际的代码开发中我们也建议选择第三个,因为这样可以让你的代码质量得到最大的保障。

选择完成之后命令行的终端就出现了第二个问题,项目代码中的模块化采用的是哪一种,同样这里也是三个选项。

第一个是ES Modules,第二个是CommonJS也就是require函数的方式,第三个是没有用到任何的模块化。简单来说这里的效果就是决定你的代码当中是否允许去出现指定的语法或者说调用。

例如如果你选择的是CommonJS那就允许使用全局的require函数和exports对象,如果你选择是ES Modules那就可以去使用export和import语法,我们这里没有用到模块化,所以我们就选择没有任何模块化。

选择之后紧接着就出现了第三个问题,选择哪一款框架,这里有react和vue我们这里暂时先选择node,什么都没用。

然后是第四个问题,有么有使用typescript,如果用到了就输入y,默认是N。

第五个问题询问我们代码最终运行在哪个环境,默认给出的是浏览器,这里可以选择浏览器额node。这里是根据你的运行环境判断是否允许使用相应环境下的API,在这里我们默认选中了浏览器。

第六个问题是询问如何定义代码风格,这里有三个选择,第一个是使用市面上的主流风格,第二个是通过一些问题去形成一个风格,第三个是根据代码文件推断出风格。一般情况下我们都是采用市面上的主流代码风格。这样的话如果我们的项目有新的成员加入他也可以更好更快的适应我们的风格。

选择之后这里又会给出三个主流风格,分别是airbnb,standard和google,其中airbnb和google分别是这两家公司的具体编码规范,而standard是开源社区的一套规范。我个人平时采用的就是这个规范。他最大的特点就是不用在语句的末尾添加分号。这里没有标准,只需要按照团队的标准执行就好了。我们这里选择standard。

最后一个问题询问我们配置文件需要以什么样的格式存放,我习惯使用js方式,因为可以添加代码,书写更多的逻辑。

回车之后会提醒我们需要再安装几个插件,这是因为我们刚刚选择的风格需要依赖一些插件。

安装过后项目的根目录下面就会多出一个eslint的配置文件,.eslintrc.js, 有了这个配置文件过后我们执行下面的命令去校验我们这里的main.js。

执行这里需要跟上文件路径,这里也可以使用路径的通配符进行批量的检查, 运行之后eslint就能够对文件自动的进行代码检查了。

npx eslint ./main.js

执行结果表示检查出一个error语法错误,也就是函数少个小括号的错误,我们回到代码中修正这个错误,再次执行eslint。

const foo=123;

function fn() {
    console.log("hello");
        console.log("eslint);
}

fn()
syy();

这一次我们就看到了更多的错误,可能你会好奇为什么刚刚我们没有找出这些错误呢,其实原因非常简单,当我们代码中存在着语法错误时,eslint是没有办法去检查问题代码和代码风格的,那么这个时候你就可以自己根据提示找到具体的问题代码。然后依次的进行解决。

也可以根据--fix参数,来自动修正绝大多数代码风格上的问题。这里我们使用--fix完成一次修正。

npx eslint ./main.js --fix

这一次问题的数量一下就少了很多,那些风格上的代码就已经被自动修正了,非常的方便。不过如果你自己还没有养成良好的编码习惯建议在开始的使用还是手动的去修改每一个不好的地方,因为这样就可以加深我们的印象。

作为一个优秀的开发人员,我们写出来的代码他本身就应该是格式良好的,而不是后来去依赖这些工具,进行格式化。这些工具他只是在最后用于确保你代码的质量。

const foo = 123;

function fn () {
    console.log("hello");

    console.log("eslint);
}

fn()

syy()

在最后我们会看到还有两个没有被自动fix的问题,我们需要回到代码当中自己手动的进行处理。这里foo变量是无意义的,syy函数没有定义,删除这两个内容。再次运行eslint检查,代码本身的问题就全部解决了。

以上这些就是eslint的基本作用。简单总结就是两点,第一eslint可以去找出代码当中的问题,而这个问题包括语法错误,代码不合理还有风格不统一。第二eslint可以自动去修复代码风格上的绝大多数问题。

ESLint 配置文件解析

这里我们来深入的去了解一下eslint的配置文件,之前我们通过eslint --int在项目根目录下创建了一个叫做.eslintrc.js的配置文件。

在这个文件中写入的配置就会影响到当前这个目录以及所有子目录的文件,正常情况下我们是不会手动去修改这个配置,但是如果我们需要去开启或者说关闭某些校验规则的时候那这个配置文件就会非常重要。

下面我们就来具体的看一下它里面的配置内容。我们打开配置文件看一下。因为这个文件最终也是运行在nodejs当中,所以可以看到这里是以CommonJS的方式导出了一个对象。

module.exports = {
    env: {
        browser: true,
        es2020: true
    },
    extends: [
        'standard'
    ],
    parserOptions: {
        ecamVersion: 11
    },
    rules: {

    }
}

在这个配置对象当中目前是有4个配置选项,其中第一个配置项叫做env, 我们都知道JavaScript在不同的运行环境中是有不同的API可以被调用,而这些API很多时候都是以全局的成员方式去提供出来,例如在浏览器环境我们可以直接去使用window和document对象,而在nodejs环境当中确不存在这些对象。

这个env的选项作用就是标记代码最终的运行环境,eslint会根据环境信息来判断某一个全局成员是否是可用的,从而去避免你的代码当中去使用到那些不存在的成员。例如这里的browser为true就表示代码会运行在浏览器环境中。这也就意味着我们可以直接在代码中去使用document或者window这样的全局对象。

换个角度来说这里想表达的就是这里的每一种环境他对应着一组预定义的全局变量,一旦开启了某个环境那么这个环境当中所有的全局成员就都能足够直接被允许去进行使用。

我们这里来做一个尝试,我们加入删掉browser或者把browser设置为false,我们运行下面的main.js代码。

document.getElementById('#abc');

但是运行的时候我们发现并没有报出document未定义的错误,简单来说这其实是因为我们再生成eslint配置的时候我们选择的是standard风格,最终我们这里的配置也就继承了standard配置,在standard这个风格中做了一些具体的配置,document和window在任何的环境当中是都可以去使用的。

我们也可以去找到standard当中对应的配置来进行一个求证,我们可以找到node_modules中eslint-config-standard目录,里面有个eslintrc.json文件,这个文件中奖document,navigator,window设置为了全局只读的一个成员。

"globals": {
    "document": "readonly",
    "navigator": "readonly",
    "window": "readonly"
}

这个配置被我们的配置文件所继承,所以我们的env也就不会影响document的使用。我们可以尝试调用一下alert,这个成员是没有被声明的。

alert(11);

这个时候就会报出一个alert未定义的错误,这也就证明env选项确实是根据环境来判断全局成员是否可用。

那env具体可以设置哪些运行环境呢?

browser: 浏览器环境中的全局变量

node: nodejs全局变量和作用域

commonjs: CommonJs全局变量和CommonJs作用域(用于 Browserify/webpack打包的只有浏览器中运行的代码)

shared-node-browser: NodeJs和Browser通用全局变量。

es6: 启用除了modules以外的所有ECMAScript6特性,该选项会自动设置ecmaVersion解析器选项为6。

worker: Web Workers 全局变量。

amd: 将require和define定义为像amd一样的全局变量。

mocha: 添加所有 Mocha 测试全局变量。

jasmine: 添加所有jasmine版本1.3和2.0的测试全局变量。

jest: Jest 全局变量

phantomjs: PhantomJs全局变啦

protractor: protractor全局变量

qunit: QUnit 全局变量

jquery: JQuery 全局变量

prototypejs: Prototype-js 全局变量

shelljs: shelljs 全局变量

meteor: Meteor 全局变量

mongo: MongoDB 全局变量

applescript: AppleScript 全局变量

nashorn: Java 8 Nashorn 全局变量

serviceworker: Service Worker全局变量

atomtest: Atom 测试全局变量

embertest: Ember全局变量

webextensions: WebExtensions全局变量

greasemonkey: Greasemonkey全局变量

需要注意的是这些环境并不是互斥的,也就是说你可以同时开启多个不同的环境,例如我们可以将browser和node都设置为true,这样这两个环境中的所有全局成员就都可以去同时使用。

module.exports = {
    env: {
        browser: true,
        node: true,
        es2020: true
    },
    extends: [
        'standard'
    ],
    parserOptions: {
        ecamVersion: 11
    },
    rules: {

    }
}

我们再来看下extends,这个选项是用来继承一些共享的配置,如果你需要在多个项目当中共享一个eslint配置就可以定义一个公共的配置文件或者说配置模块然后在这里去继承就可以了。这个属性值是一个数组,数组中的每一项都是一个配置。

parserOptions这个的作用是设置语法解析器的,我们知道ECMAScript近几年发布了很多版本,这个配置的作用就是是否允许使用某一个ES版本的语法。这里设置的是11,也就是ECMAScript2020。

如果我们将ecamVersion设置为5,这个时候就没办法使用ES6的新特性了,不够这里需要注意,在ecamVersion低于6的情况下sourceType不能是module,因为ES Modules是ES6的新特性,这里我们就需要将standard配置中的sourceType修改为script。

这里需要注意的是parserOptions影响的只是语法检测,不代表某个成员是否可用,如果是ES2015提供的全局成员,比如Promise还是需要env中的ES6选项进行控制,我们这里可以尝试着把ecamVersion改为2015,然后把env中的es2015设置为false。

module.exports = {
    env: {
        browser: true,
        es2015: false
    },
    extends: [
        'standard'
    ],
    parserOptions: {
        ecamVersion: 2015
    },
    rules: {

    }
}

这里就是关于我们刚刚所提到的parserOptions只是检测语法,而不是代表某个成员是否可用,具体的成员我们还是要根据环境来定义。

说完这个以后我们再来看一下rules配置,这个属性就是配置ESLint中每个规则的开启或者是关闭,例如我们这里可以尝试开启no-alert规则。具体的开启方法就是在rules里面添加一个属性,属性名是内置的规则名称,属性值可以有三种,分别是off,warning和error。如果是off代表规则关闭,如果是warning就是发出警告,如果是error就表示当前报出错误。这里我们选择error。

module.exports = {
    env: {
        browser: true,
        es2015: false
    },
    extends: [
        'standard'
    ],
    parserOptions: {
        ecamVersion: 2015
    },
    rules: {
        'no-alert': "error"
    }
}

设置完成以后,代码中使用了alert就会报出error错误。这就是rules的属性的作用,那具体来说会有哪些规则可以来使用呢?在eslint的官网上就给出了所有内置的可用的校验规则列表,我们可以在使用的时候进行查看。而我们目前所使用的这个standard风格当中已经是开启了很多规则。基本上也满足了所有的需求,如果有需要的情况下可以根据自己的需求来进行具体的配置。

最后还有一个globals选项,在最新版本的默认配置当中已经没有体现了。我们这里简单的介绍一下,这个选项其实很好理解,就是去额外的声明我们在代码中可以使用的全局成员。

例如在使用了JQuery的项目当中,一般都会使用一个全局的JQuery对象,那么我们就可以直接去添加一个JQuery,将他的值设置为readonly,这样代码当中就能直接去使用JQuery,而且也不会报错。这就是globals选项的一个作用,我们可以依据具体的需求来完成我们相应的配置就可以了。

module.exports = {
    env: {
        browser: true,
        es2015: false
    },
    extends: [
        'standard'
    ],
    parserOptions: {
        ecamVersion: 2015
    },
    rules: {
        'no-alert': "error"
    },
    globals: {
        jQuery: 'readonly'
    }
}

以上的这些就是关于我们ESLint配置文件当中具体的配置项目他给出的一个具体的解释。

ESLint 配置注释

这里我们来看一下ESLint配置注释的相关内容,简单来说配置注释就可以理解为将配置直接通过注释的方式写在我们的脚本文件当中,然后再去执行代码的校验。

为什么要这样做呢?原因也很简单,比如我们在实际的开发过程中如果使用ESLint难免遇到一两处需要违反配置规则的地方,这种情况下我们肯定不能因为这一两个点就去推翻校验规则的配置,所以在这个时候我们就可以去使用ESLint的校验规则的配置注释去解决这样的一些问题。

比如我们这里定义一个普通的字符串,并且在这个字符串当中因为一些业务的需求需要使用${}这样字面量的占位符,但是我们使用的standard风格是不允许我们这样去使用它的。

要解决这样的问题我们就可以通过注释的方式,临时去禁用一下这样的规则,这种注释的语法有很多种,具体可以参考ESLint官方给出的一个文档,我们这里直接使用eslint-disable-line, 这样我们eslint在工作的时候就会选择忽略这一行代码。

const str = '${name} is a coder'; // eslint-disable-line
console.log(str);

这样去使用虽然可以解决我们所面临的问题,但是同样也会带来一个新的问题,一行当中如果有多个问题存在的时候我们这样操作所有问题就都不会被检测了。因此更好的做法应该是在eslint-disable-line后面跟上一个具体要禁用的规则名称。比如这里的是no-template-curly-in-string。

const str = '${name} is a coder'; // eslint-disable-line no-template-curly-in-string
console.log(str);

这样就会只忽略指定的问题规则,其他的问题仍旧可以被发现。

当然注释的方式不仅可以去禁用某个规则,还能去声明全局变量,修改某个规则的配置,临时开启某个环境等等,这些功能如果你有需要的话可以去通过下面的链接找到对应的文档去查询对应的使用。

http://eslint.cn/docs/user-guide/configuring#configuring-rules

这里就是ESLint配置注释相关介绍。

ESLint 结合自动化工具

这里我们来看一下ESLint结合自动化工具的使用,我们都知道ESLint本身是一个独立的工具,但是如果现在是一个有自动化构建的工作流的项目当中,还是建议把ESLint集成到自动化构建的工作流当中,这样去做有两个优点。

因为肯定是需要执行项目构建的,而我们把ESLint集成到项目构建当中就可以去确保他一定会去工作,其次整合到一起去管理也会更加的方便一点,与项目相关的命令也会更加的统一,不用一会去指定gulp,也不用一会去执行ESLint,下面我们就来看一下如何去实现ESLint与gulp的集成。

这里有一些前置工作我们先去说一下, 首先我们需要安装eslint模块和gulp-eslint模块,我们这里就不演示了,直接进入。别忘记创建eslint的配置文件,通过eslint --init

我们找到gulp的配置文件,guipfile.js在处理js的任务中做修改。

const script = () => {
    return src('src/main.js', {base: 'src'})
    .pipe(plugins.babel({presets: ['@babel/preset-env']}))
    .pipe(dest('temp'))
    .pipe(bs.reload({stream: true}))
}

我们现在要做的就是将eslint操作集成进去,因为gulp是属于一种管道的工作机制,所以如果我们是想把eslint集成到这个工作当中,我们就应该在babel处理之前先去进行eslint操作,否则经过babel之后我们的代码就不是真正的源代码了。

我们可以直接使用plugins.eslint找到这个插件然后进行使用.pipe(plugins.eslint())这样之后就会先去执行eslint,然后再去执行babel编译。

默认情况下eslint只会去检查代码中存在的问题,并不会根据检查的结果做出任何的反馈,正确的做法是在eslint插件处理完之后,先去使用eslint的format方法在控制台打印出具体的错误信息,之后再去使用eslint中的failAfterError方法让eslint再检查出错误之后终止任务管道。

const script = () => {
    return src('src/main.js', {base: 'src'})
    .pipe(plugins.eslint())
    .pipe(plugins.eslint.format())
    .pipe(plugins.eslint.failAfterError())
    .pipe(plugins.babel({presets: ['@babel/preset-env']}))
    .pipe(dest('temp'))
    .pipe(bs.reload({stream: true}))
}

这个时候我们就可以使用eslint去检测我们代码了。而且eslint也集成到编译js的任务中了,也就是融入到了我们所涉及的工作流当中。

ESLint 结合 Webpack

这里我们来看一下ESLint结合webpack使用的相关内容,如果你目前正在开发一个webpack打包的项目,ESLint同样也可以集成进去,只不过webpack集成ESLint并不是以插件的方式来完成的,而是通过loader机制,我们都知道webpack在打包模块之前会将遇到的模块都交给对应的loader进行处理。

所以eslint就可以通过一个loader的形式去集成到webpack当中,这样就可以实现在打包javascript之前,先通过eslint来校验javascript代码。

我们首先需要安装对应的eslint模块和eslint-loader模块。并且初始化.eslintrc.js配置文件。

npm install eslint eslint-loader -D

完成之后我们就可以回到项目中去实现后续的具体内容操作。在这里我们先找到webpack的配置文件, 这里面所有的js文件都是交给babel-loader进行处理的。

如果我们想要集成ESLint, 那我们同样需要在babel-loader之前对js文件进行处理。

module.exports = {
    mode: 'production',
    entry: './src/main.js',
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'babel-loader'
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
}

因为在这之前我们已经完成了相应的模块安装,所以我们可以直接在webpack配置文件当中去配置,这里我们把js使用的loader配置为一个数组,然后将eslint-loader放置在最后,这里提醒一下,loader执行的顺序是从后向前执行的,也就是后配置的先执行。

或者说我们也可以使用另一种配置方法,就是单独的为js文件再添加一个loader规则,然后在这个规则当中我们去添加一个enfore属性,把他的值设置为pre,确保这个loader当中配置的优先级是优于其他的loader,这样也可以实现去通过eslint校验。然后再去执行babel的一个转换。

这两种方式我们在操作的时候都可以去使用,我个人更推荐去使用第二种,因为这样的配置会显得更加清晰一些,我们这里按照第二种方式来完成这样的使用。具体就是把js配置重新添加一个loader。

module.exports = {
    mode: 'production',
    entry: './src/main.js',
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'eslint-loader',
                enfore: 'pre'
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'babel-loader'
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
}

完成这样的配置之后我们去使用webpack打包的时候,就会报出eslint的error错误。这也就实现了将eslint集成到webpack当中。非常的简单。

由于React当中存在jsx的语法,所以对应的eslint的配置就会显得相对复杂一些,所以这里我们就先介绍如何通过eslint-loader的方式把当前的eslint集成至webpack项目当中就可以了,后续的使用我们会具体的单独进行处理的操作。

接着我们再来看一下eslint集成到webpack当中后续配置操作相关的内容介绍,配合webpack的操作完成代码的检查工作。

当我们使用webpack打包的时候eslint会自动使用规则去校验我们的javascript代码。

我们都知道eslint会检查出很多定义但没有使用到的问题,比如说React is define but never used, 但是我们都知道,React是jsx编译之后代码所必须要使用到的,所以像这种特殊的情况,ESLint就必须要靠一些额外的插件来进行实现。

社区当中已经为React这种特殊的语法专门准备了一个插件,就叫做eslint-plugin-react, 我们需要安装这样一个模块到我们本地的开发依赖当中。

npm install eslint-plugin-react --save-dev

安装完成之后我们就可以先找到node_modules当中这个插件对应的模块,你会发现这个模块中定义了很多规则,这些规则都是专门针对于react项目的。如果我们想要去使用这些规则就直接去eslint的配置文件当中通过plugins属性去进行配置。

简单来说plugins是一个数组,在他的内部可以直接去指定我们去使用哪些插件,我们这里的插件模块叫做eslint-plugin-react, 在这个数组中添加react就可以了,因为这里的模块名会去掉eslint-plugin-

完成之后这个规则就可以进行使用了。我们可以在rules中开启react/jsx-uses-react这个规则。可以用error也可以用数字的2代表error。这个规则的作用就是避免React定义了但是没有使用的一个报错。

module.exports = {
    env: {
        browser: true,
        es2020: true
    },
    extends: [
        'standard'
    ],
    parserOptions: {
        ecamVersion: 11
    },
    rules: {
        'react/jsx-uses-react': 2,
        'react/jsx-uses-vars': 2,
    },
    plugins: [
        'react'
    ]
}

这就是插件的作用,以及他的基本使用,不过对于大多数ESLint的插件来说一般都会提供一个共享的配置从而去降低我们使用的成本,我们这里使用的eslint-plugin-react他里面就导出了两个共享的配置,分别是recommend和all,我们一般使用recommend,它里面提供的共享的配置我们就可以直接通过继承来去使用。

不过在继承的时候我们要遵守他的语法规则,具体来说就是我们使用plugin:插件名称/配置名字就可以完成使用了。

module.exports = {
    env: {
        browser: true,
        es2020: true
    },
    extends: [
        'standard',
        'plugin:react/recommend'
    ],
    parserOptions: {
        ecamVersion: 11
    }
}

这样共享配置当中的内容就不需要单独的去配置了,使用起来会更加的方便一些。这就是eslint继承到webpack。

现代化项目集成 ESlint

这里我们来看一下现代化项目当中对于ESLint集成的支持,随着React或者说Vue这种框架的普及,这些框架的中间生态也都相应的完善了,最明显的感觉就是现阶段如果我们再去开发一个基于react或者vue的项目,我们基本上都已经不需要再去自己配置webpack或者说eslint这些工程化的工具了。

在官网的cli当中都已经将他们直接集成进去了我们这里就以vue-cli来创建一个vue项目作为演示。首先需要全局安装vue-cli

npm install @vue/cli -g

然后我们使用vue create 创建一个vue项目。

vue create yd-vue-app

在这个过程中会提示我们需要选择很多选项,我们选择babel运行环境,然后代码风格上选择eslint 和 standard风格配置,然后会询问需要在什么样的环节执行lint操作。

第一个是save环节,也就是webpack在构建时自动进行校验,并不是文件保存时,因为很多时候我们采用的是监视的模式,也就是文件刚刚被修改过后webpack就会自动执行编译,这里就称之为保存时的校验。

第二个选项是commit环节这个环节指的就是利用git的钩子进入commit之前自动的校验当前的代码这样就确保了提交到仓库中的代码都是经过校验的。

一般这两个环节都选择上。

接着会询问想要怎样存放babel和eslint的配置文件,我们这里选择独立文件的单独存放,之所以有这个选项是因为大多数文件都支持package.json配置的。

最后询问是否保存这些选择,下次可以直接使用,最好不要选择。完成之后就会自动安装相关的依赖,并且在最后给我们创建一个基本的项目结构。

在这个基本的结构当中eslint就被自动的集成进去了,通过这样的操作就可以让我们不需要在工具的配置和使用上花费太多的时间,也就是说开发者可以更加专注于业务功能的开发。这就是现代化开发工具自动去集成ESLint的相关操作。

ESLint 检查 TypeScript

这里我们来看一下ESLint对于TypeScript的语法支持,在现阶段,前端项目中去使用TypeScript开发的情况越来越多,所以在这里我们就来去看一下,ESLint是如何校验TypeScript代码的。

这里我们先介绍一下,以前在TypeScript的lint操作都是使用一个叫做tslint的工具,不过后来由于这个tslint官网直接放弃维护了,然后转而建议我们使用eslint配合typescript插件来实现对TypeScript代码的校验。

说完这些之后我们就来具体的看一下ESLint对于TypeScript语法的校验支持。首先我们还是要初始化eslint。这里涉及到TypeScript选择的时候我们要去选择y而不是N。这里同样需要去安装两个模块,这两个模块是eslint检查TypeScript代码所必须的,会自动安装。

安装完成过后之后就可以使用eslint去检查TypeScript的代码了。

function foo (ms: string): void {
    console.log(ms);
}

foo('hello typescript~')

我们来看一下.eslintrc.js配置选项。这里的parser的作用是指定一个语法解析器,这里需要配置语法解析器的原因就是TypeScript相比较于普通的JavaScript代码来说会有很多特殊的语法,所以我们需要去给他指定一个语法解析器。

module.exports = {
    env: {
        browser: true,
        es2020: true
    },
    extends: [
        'standard'
    ],
    parser: '@typescript-eslint/parser',
    parserOptions: {
        ecamVersion: 11
    },
    plugins: [
        '@typescript-eslint'
    ],
    rules: {}
}

搞定这些之后就可以对TypeScript代码进行校验了,从而去发现它里面的一些问题。以上这些就是ESLint如何去检测TypeScript以及他里面的一些操作过程。

Stylelint 认识

这里我们来看一下关于stylelint的使用介绍,目前我们都知道在前端项目中除了javascript代码需要被lint之外css代码同样也需要被lint,对于css代码的lint操作我们一般都会使用一个叫做stylelint的工具进行完成。

stylelint的使用与之前所提到的eslint其实基本上是一致的。

首先stylelint他也提供了一系列代码检查的规则,供我们直接进行使用,和之前一样我们也可以直接在配置文件当中去选择性的开启或者说是关闭某些规则。

其次stylelint同样也提供了一个cli的工具,我们可以在终端当中直接去调用stylelint这样的命令,然后去检查项目当中的css文件。

再者stylelint也可以去通过插件来实现对于sass less postcss这些css衍生语法的代码检查。

最后stylelint同样可以去与gulp自动化工具或webpack这样的打包工具进行集成使用。

知道这些之后我们就应该明白,对于stylelint的使用我们其实是可以完全去参考eslint的操作方式,因为他们两者之间是极其类似的。下面我们就来快速了解一下stylelint的相关使用操作。

首先我们需要去安装stylelint的模块,安装完成之后就可以使用stylelint的cli命令了。

npm install stylelint -D

安装完成之后我们同样需要去配置stylelint,与eslint类似,样式文件的配置文件名字叫.stylelintrc.js, 这里我们手动添加这个文件。这个配置文件当中的属性基本和eslintrc中的是相同的。

我们这里安装stylelint-config-standard这个共享配置模块。

npm install stylelint-config-standard -D

安装之后,直接对他进行一个继承的使用,这里和eslint不太一样的地方是这里的共享模块名称必须是一个完整模块的名称。也就是说我们这里写的必须是

module.exports = {
    extends: "stylelint-config-standard"
}

完成这样的配置之后我们就相当是继承了这个配置模块,这个时候我们再去执行stylelint命令。

npx stylelint ./index.css

发现这些问题后我们就可以直接去定位然后修改,那么也可以和以前一样通过--fix这个参数让stylelint帮我们完成大多数问题的自动修复。

以上这些内容就是关于stylelint配置和使用的相关介绍。接下来我们再说一下关于sass代码的检查,如果说你要使用stylelint去校验我们项目当中的sass代码当然也是可以实现的。

这里我们需要安装stylelint-config-sass-guidelines

npm install stylelint-config-sass-guidelines -D

安装之后配置extends属性, 然后就可以使用stylelint去检测sass代码了。

module.exports = {
    extends: ["stylelint-config-standard", "stylelint-config-sass-guidelines"]
}

除了这些之外,至于说我们还有一些其他的less或者说postcss他们的操作其实也都是类似的,在这里我们就不去一一的赘述了,那如果说你还想要把stylelint集成到gulp这样的工作流当中你也可以参考eslint的集成方法,因为他们都是非常类似的操作。

以上这些就是我们对stylelint的一个认识,其实不管是eslint还是stylelint,他们本身并没有什么难的地方,重点在于你能否去学会变通做到举一反三,比如说你学会javascript的lint工具之后是不是就能想到要为你的css或者说html也去找一个对应的lint工具。

Prettier 的使用

在这里我们来看一下prettier的介绍,他其实就是近两年来使用频率比较高的通用的前端代码格式化工具,他很强大几乎能完成所有前端代码文件的格式化工作,在日常的使用中我们也可以通过它来完成代码的自动格式化工作,或者说针对于markdown这样的文档去进行格式化操作。

简单来说就是通过使用prettier我们就很容易的落实前端项目中的规范化标准,而且他的使用也是非常简单的。接下来我们就来具体的看一下关于他的一些使用操作。如何使用prettier将代码直接去进行格式化操作。

首先我们需要去安装prettier,一般我们安装在开发依赖当中。

npm install prettier -D

安装完成之后我们可以直接使用prettier命令来格式化我们的代码。这里可以跟上一个文件路径。

npx prettier style.css

默认情况下这个命令会将格式化之后的代码直接输出到控制台当中,一般我们是需要将格式化后的代码覆盖到源文件当中,我们可以在命令中跟上--wirte参数来实现。

npx prettier style.css --write

prettier支持通过命令通配符的方式格式化所有文件,比如格式化所有的文件。

npx prettier . --write

这就是prettier的一个作用,我们可以发现关于prettier的使用是非常简单的,我们可以直接调用命令去找到相应的文件,然后跟上一个参数就可以了。

最后希望大家不要完全的依赖这种工具来写出一些格式良好的代码,因为我们在编码的过程中本身就应该去严格的遵守这些格式,这也是我们作为开发人员来说一个最基本的素质。

Git Hooks 工作机制

这里我们先来看一下关于githooks使用功能的一些介绍,因为我们在后续功能需要去使用到一些eslint和githooks的一些结合。

目前来说我们都已经了解了我们代码规范化的一个重要性,同时我们也知道了如何去通过使用一些工具来确保我们代码规范是如何落地的,但是在这个过程中还是有一些遗漏的问题,例如我们的团队中如果某一个成员没有按照要求去使用lint工具。或者说他压根就忘记了去使用lint工具。最终去把有问题的代码提交到了我们的远程仓库,这种情况下我们在后期去进行项目集成的时候导致我们整个项目的代码就有可能不被ci所通过。

所以这个时候我们的lint工具就丧失了他的意义。而本身来说我们使用lint就是为了确保,我们提交到仓库当中的代码是没有问题的。而且格式也是良好的。所以我们该怎样去解决这个问题呢。

如果我们只是单纯的靠口头约束,要求团队成员在提交代码之前必须执行lint,这必然是流于形式,所以更好的办法应该是通过某种方式强制要求代码在提交之前必须先通过lint检查。这也就是githooks的价值。

下面我们简单介绍一下githooks的使用。

githooks也称之为git钩子,每个钩子都关联着一些具体的git操作,比如我们的commit和push等等,第二我们也可以直接去找到某个具体的钩子,然后通过编写一些具体的shell脚本,然后去定义一些当我们的git钩子在触发时要去执行的任务。

我们这里来看一下具体关于钩子的使用机制,我们找到一个git仓库的目录,然后找到本地的.git目录,默认这是一个隐藏的目录。

进入之后我们可以找到一个hooks子目录,子目录里面存放了很多sample的文件。这里面的每一个sample其实就是每一个钩子,这里我们只需要关心pre-commit.sample的钩子即可,因为他对应的就是我们的commit操作。

当我们去执行commit的时候就会触发它里面定义的一些任务,我们可以打开这个文件。它里面有很多的shell脚本,我们可以修改里面的shell脚本来演示一下,比如替换为下面的代码。

#!/bin/sh
echo "before commit"

这个时候我们执行commit操作就会在终端中看到打印的"before commit"信息。这也就说明我们的githooks是工作了的。这里修改的pre-commit.sample别忘了回复回来,不然影响后面使用。

这就是githooks的工作机制,就是通过钩子来对应具体的操作,然后再操作发生的时候就会自动的去执行钩子里面定义的任务。

明确了这个操作之后,我们将来就可以在commit执行之前强制的去执行eslint。这里有些配置上的东西我们需要说明。

ESLint 结合 Git Hooks

这里我们来看一下关于eslint去结合githooks的具体使用,之前我们已经知道githooks是如何完成工作的。我们现象想要去通过git钩子在代码提交之前强制的去实现对应代码的lint操作。

但是这里我们就遇到了一个很现实的问题,当下很多开发者并不是很擅长使用shell脚本编写功能。而当前的这个功能又是我们必须要使用的。所以就有人开发了一个npm模块。

直接去将githooks的操作简单化的实现,这个模块叫做husky,简单说明一下就是有了这个模块就可以实现在不编写shell脚本的情况下也能使用git钩子所带来的一些功能。

下面我们就来说一下husky的相关使用,首先需要安装husky模块, 作为开发依赖。

npm install husky --save-dev

安装成功之后他会自动在.git/hooks的目录中添加一些自定义的钩子,我们这里可以不用关心他是如何实现的,也不用去看这些文件。

接着我们在package.json文件中添加一个husky配置, 在这个配置中定义一个hooks对象,接着在hooks对象的内部就可以直接去为不同的hook也就是钩子去定义不同的任务。

我们这里关心的都是commit之前的操作,所以我们添加一个pre-commit, 值为一个npm命令。这样我们在执行commit提交的时候会直接帮助我们找到,script里面对应的test命令。

{
    ...
    "scripts": {
        "test": "eslint ."
    },
    "husky": {
        "hooks": {
            "pre-commit": "npm run test"
        }
    }
    ...
}

现在我们就基本知道了,关于husky模块的使用,具体来说就是我们可以直接在我们的配置文件当中新增一个husky的字段,然后在他里面针对具体的钩子来设置要执行的命令,从而就可以在将来执行一些npm操作的时候可以去调用设置好的命令。

那么我们该怎么样利用这个规则来实现我们代码在commit之前进行一个lint的操作呢,其实也非常简单,就是相当于执行了npm run test命令而已。这就是关于husky相关的具体操作。这已经可以让我们在commit之前完成代码的校验操作。

但是如果我们想要在检查之后继续的做一下后续的操作,例如我们想经过检查的代码进行一个格式化或者说直接将进行格式化的代码去添加到暂存区。这个时候husky就显得有些不够用了。

lint-stage模块可以配合着husky在继续提升一些其他的功能,我们这里具体看一下他的一些用法。首先我们需要安装这个模块。

npm install lint-staged --save-dev

安装完成之后也需要在package.json文件当中去进行一个具体的配置,就是添加一个lint-staged属性,同时在他的内部设置一个*.js的属性,这个属性值可以是一个数组,可以在这个数组中添加一些后续需要执行的任务,比如我们这里先添加一个eslint,然后再设置一个git add,这样一来我们就相当于设置了两个命令。

我们还需要设置scripts,将precommit钩子执行的脚本设置为lint-staged, 不过这里我们需要注意因为这里修改了这个钩子的名称叫precommit,所以我们还是要到我们设置的husky,把npm执行的命令修改为precommit

同时也要指定他要完成的具体任务。有了这些操作我们也要回到scripts当中进行脚本设置。

{
    ...
    "scripts": {
        "test": "eslint .",
        "precommit": "lint-staged"
    },
    "husky": {
        "hooks": {
            "pre-commit": "npm run precommit"
        }
    },
    "lint-staged": [
        "eslint",
        "git add"
    ]
    ...
}

配置完这些之后,我们再提交的时候就会先触发npm run precommit,然后precommit中就会执行eslint操作和git add操作。这也就实现了在commit之前给代码强行的lint,同时我们也可以完成后续的需求。

以上就是githooks配合eslint的使用介绍。一般我们建议使用husky配合lint-staged, 因为这样的话我们不仅可以在commit之前强制的验证我们的代码同时我们还可以在验证之后或者之前完成一些其他的操作。

转载须知

如转载必须标明文章出处文章名称文章作者,格式如下:

转自:【致前端 - https://madaozhijian.com】 eslint代码检查规范  "隐冬"