众所周知,Javascript是一种弱类型(或者称为动态类型)语言,即变量的类型是不确定的。例如,下面代码中的temp
一开始是字符串,后来又变成了数字:
1temp = 'hello';
2temp = 5;
弱类型语言变量类型完全由当时的值决定,所以称为“弱类型”。这样设计有其好处,可以写出非常简洁的代码。但是在构建大型项目的时候,无法在编译期发现问题会导致很难发现问题,反而会为程序员带来非常大的负担。这实际上限制了JS项目的规模,无法开发过于复杂的项目。
1. 强类型解决方案
一直有人在尝试为Javascript增加强类型系统的支持。目前看来比较流行的有两种方案:Typescript和Flow.js。
1.1 Typescript
TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。在编译期会去掉类型和特有语法,生成纯粹的Javascript代码。由于最终执行的还是Javascript,所以Typescript不会带来兼容性问题。TypeScript 是 JavaScript 的超集,这意味着他支持所有的 JavaScript 语法。并在此之上对 JavaScript 添加了一些扩展,如 class / interface / module 等。这样会大大提升代码的可阅读性。
目前版本是2.2。官方网址为:https://www.typescriptlang.org/。
Typescript带来的好处重点有:
- 静态类型检查;
- IDE智能提示;
- 代码重构;
- 可读性。
Angular 2.0框架已经采用了Typescript语言重写。
1.2 Flow
Flow是Facebook出品的,针对JavaScript的静态类型检查工具,目前版本v0.43.1。其代码托管在github之上,并遵守BSD开源协议。官网地址:http://flow.org。同为Facebook出品,React采用了Flow。另外,Vue.js也采用了Flow作为静态检查的工具。
1.3 比较
相对来说,Flow的代码侵入性比较小一些。并且可以通过babel-plugin-transform-flow-strip-types
转一下就可以得到去除Flow特性的代码。另一点来说,代码可以部分切换采用,成本比较低,能够使用Babel+ESLint+Webpack这种比较成熟的体系。这方面的讨论可以参考附录中的 Vue 2.0 为什么选用 Flow 进行静态代码检查而不是直接使用 TypeScript? ,里面有Vue.js开发者尤雨溪关于这方面的思考与回答。
我比较认同以上观点,Flow代码侵入性比较小,可以保证代码比较符合ES的发展规律,免得受制于具体厂商的技术。所以最后开发中选择了在Vue.js开发中使用Flow技术。当然,我在后台开发(Node.js)相关代码中也引入了Flow。不过这部分配置比较容易,下面会顺带提到。
2. 工具链简介
Javascript开发对工具链的使用越来越重,通常前端开发都会用到诸如:Webpack、Babel、ESLint等工具。先简单介绍一下各工具的主要用途:
- Babel: 编译工具。鉴于执行环境(浏览器、Node)与JS规范(ES6、ES7等,或者React、Vue.js等定义的规范)之间存在差异,不能够直接在执行环境运行我们写的代码,因此需要使用编译工具将我们写的代码编译成执行环境可以理解的代码,这就是Babel的作用。
- Webpack: 打包工具。能够将各种资源(JS、JSX)、样式(CSS、LESS、SASS)、图片等都作为模块来处理,并最终打包成一个文件,即解决了代码依赖问题,也提高了加载速度;
- ESLint: 代码问题检查工具。Linter类的工具都是通过扫描代码,通过已有的规则发现代码中可能存在的问题。而ESLint就是针对JS代码的Linter工具。
3. 环境配置
3.1 目标及思路
这次配置的目标是:在VSCode中可以通过Flow发现代码中的问题,能够支持.js、.vue文件;在Webpack中配置能够对Flow相关代码正确编译。
这次配置的核心思路是利用ESLint的插件机制,通过插件eslint-plugin-flow
实现对Flow支持。而VSCode有一个很好用的ESLint插件,可以在打开文件的时候自动对文件内容进行Lint操作,从而提示出各种潜在问题。这样通过配置ESLint的规则,不光可以实现普通Lint问题的发现,还可以实现Flow相关问题的发现:例如可以要求变量必须有类型等。
下面按照顺序描述各个步骤需要做的更改。
3.2 Flow
首先就是安装Flow。虽然可以将Flow安装到全局,但是为了以后方便管理,推荐安装到项目本身。
1npm install flow-bin --save-dev
3.2.1 .flowconfig
在项目根目录,增加一个配置文件.flowconfig
,下面的配置是我现在用的。Flow默认会扫描整个工程中的所有代码,通过 ignore
配置忽略不需要检测的代码。其他配置项以后会专门写个介绍。
1[ignore]
2.*/node_modules/.*
3
4[include]
5./src/.*/*.vue
6
7[libs]
8
9[options]
3.2.2 验证
现在可以验证一下了。在代码开始的地方增加注释:/* @flow */
,然后执行命令进行检查:
1$ ./node_modules/.bin/flow src/utils.js
2...
3src/utils.js:8
4 8: module.exports.toHex = function(source) {
5 ^^^^^^ parameter `source`. Missing annotation
6...
可以看到已经能够检查出来问题了。
3.3 eslint
然后安装eslint插件(如果你的项目中已经使用了eslint,那这步可以省略):
1# eslint
2npm install eslint --save-dev
3# parser babel-eslint
4npm install babel-eslint --save-dev
5# flowtype plugin
6npm install eslint-plugin-flowtype --save-dev
7# html plugin (used for .vue)
8npm install eslint-plugin-html --save-dev
9# vue plugin & vue rules config
10npm install eslint-plugin-vue eslint-config-vue --save-dev
eslint-plugin-flowtype
就是用来进行Flow检测用的插件。eslint-plugin-html
,eslint-plugin-vue
两个插件是为了检查.vue文件用的。eslint-config-vue
是vue检测方面的一些规则,使用这个我们可以快速上手,不用逐条配置vue相关的规则。
3.3.1 .eslintrc.json
eslint依赖于一个配置文件,之前版本叫.eslintrc
,最近版本的eslint支持多种格式的配置,可以是json/yaml/js等,对应的配置文件也需要添加对应的文件后缀。这里我用的是json格式,内容如下:
1{
2 "parser": "babel-eslint",
3 "plugins": [
4 "html",
5 "flowtype"
6 ],
7 "extends": [
8 "eslint:recommended",
9 "plugin:flowtype/recommended",
10 "vue"
11 ],
12 "rules": {
13 "indent": 0,
14 "semi": 0,
15 "camelcase": 0,
16 "no-console": 0
17 }
18}
- plugins中指定的是eslint的插件:html引入vue中html内容的lint;flowtype引入的是Flow方面的lint;
- extends中指定的是ESLint的Rules集合。通常我们会在网上找到一些已经成熟的Lint规则集合,它们经过实践证明是有效的。我们可以使用一些这种规则的集合,然后通过后面的rules配置微调一些不同的地方。这样可以减少很多配置的工作量。上面配置中指定了三个集合。
- 各个公司或者各个项目都会有一些例外,指定了上面的推荐规则集之后可以通过rules进行微调。
3.3.2 .eslintignore
eslint默认会扫描项目下的所有文件,通过.eslintignore
可以将不需要检查的内容屏蔽掉。例如:node_modules中的第三方库;dist下面的Babel编译后的文件等。
1# node_modules
2node_modules/*
3
4# flow-typed: flow typed libdefs
5flow-typed/*
6
7# vendor: vendor library
8**/vendor/*.js
9
10# dist: generated by babel compiliation
11dist/*
3.4 VSCode
在VSCode中我们需要安装ESLint这个插件,方法很简单,搜索“ESLint”,安装ESLint这个插件就可以了。
3.4.1 settings.json
安装ESLint之后还需要进行一些设置,关闭VSCode本身的Javascript校验机制,另外还需要配置一下,否则会对.vue文件无效。配置如下(选择首选项-设置即可):
1...
2 "javascript.validate.enable": false,
3 "eslint.validate": [
4 "javascript",
5 "javascriptreact",
6 "html",
7 "vue"
8 ]
9 ...
完成上述配置之后,VSCode中的ESLint就配置完毕了。重启一下VSCode,随便打开一个js文件或者.vue文件,应该就能够看到配置后的效果了。
3.5 Webpack
首先是安装Flow需要的插件。Flow代码会在Babel编译的时候完全去除掉,从而变成纯粹的Javascript代码。
1npm install babel-plugin-transform-flow-strip-types --save-dev
2npm install babel-plugin-syntax-flow --save-dev
3npm install babel-plugin-transform-class-properties --save-dev
3.5.1 .babelrc
然后在.babelrc中引入这几个插件即可:
1 "plugins": [
2 ...
3 "babel-plugin-transform-class-properties",
4 "babel-plugin-syntax-flow",
5 "babel-plugin-transform-flow-strip-types"
6 ...
7 ]
4. 一些失败的方案
4.1 vscode-flow-ide & flow-language-support
这两个是VSCode的插件,看介绍能够直接对JS文件进行Flow方面的Lint工作,能够发现Flow方面的问题。但是使用中发现无法对.vue文件进行这种工作,所以最后放弃掉了。
4.2 eslint-plugin-flowtype-errors
这个插件也是与eslint配合的。配置比eslint-plugin-flowtype
简单。但是它没有办法自定义Rules。其中有个规则用于检测require
模块是否存在的。因为我们不会对第三方库进行Flow扫描,所有Flow无法知道第三方库(例如element-ui)是个什么情况。这时就会报出Required module not found
这个提示,网上目前也没有好的解决办法。需要手写libdef文件或者使用flow-typed工具生成libdef文件来解决。最后发现使用起来反倒麻烦了。所以最后放弃掉,切换到eslint-plugin-flowtype
。