如何搭建一个“摩登”的前端开发环境
js类型系统
最近纠结一个问题,前端的js由于其动态的特性,写几行代码,在浏览器刷新一下就能看到结果了,非常适合快速开发和迭代。但随着代码的规模越来越大,到了后期就会变得难以维护,任何的改动都有可能引入新的bug,js工程师需要花费越来越多的时间来调试修复各种bug。
造成这样结果的原因有多样,而其中之一的原因,是由于js缺乏类型系统,导致我们无法通过工具来在开发的过程中检测到那些可能会发生的错误,也无法通过具体的类型定义来约束别人如何调用自己写的代码库。
这样的想法其实在js社区里已经得到越来越多人的认同,最近几年也陆续推出了多种不同的js类型系统用于增强js的健壮性,其中像typescript就是其中的佼佼者。当然我今天要讲的并不是typescript,而是由facebook推出的flow。
flow和typescript不同,typescript是js的超集,是另外一门语言(向下兼容js),而flow,则是一个静态类型检测工具,并没有修改js的语言特性。flow通过自动推断js代码中各个变量的类型,来约束代码的行为,举个例子,在js中对两个变量进行相加,在不同情况下会得到不一样的结果:
let strA = "hello ";
let strB = "world";
let numC = 1;
let numD = 2;
let objE = { "key": "value" };
let arrF = [ 1, 2, 3 ];
// 情况1
strA + srtB // "hello world"
// 情况2
numC + numD // 3;
// 情况3
strA + numC // "hello 1"
// 情况4
strA + objE // "hello [object Object]"
// 情况5
strA + arrF // hello 1,2,3"
上面的5种情况,在js中都是被允许的,然而情况4和5,在一般情况下并不是我们所期望的。而在flow中,则只允许情况1~3通过检测,而对于情况4和5则直接报错了。
strA + objE;
^^^^ object literal. This type cannot be added to
strA + objE;
^^^^ string
strA + arrF;
^^^^ array literal. This type cannot be added to
strA + arrF;
^^^^ string
flow除了可以自动的进行类型推断外,还可以通过类型声明的来进一步限制代码的行为,例如我们声明一个函数,接受一个参数,并返回一个字符串,如果我们不进行额外的类型声明,flow默认是会接受string和number两种类型的参数
function hello(val) {
return "hello " + val;
}
hello("world");
hello(1);
但如果我们希望我们的函数只接受string作为参数,并且明确返回string,则可以
function hello(val: string): string {
return "hello " + val;
}
这是如果再传入number则会报错,对于完整的flow使用,大家可以参考这里
hello(1);
^ number. This type is incompatible with the expected param type of
function hello(val: string): string {
^^^^^^ string
flow环境的搭建
要在项目中使用flow,需要完成三件事情
第一安装flow命令行工具
$mkdir flow-proj && cd flow-proj
$npm init
$npm install flow-bin --save-dev
第二,编写js代码(hello.js),并在代码的第一行,加入注释// @flow
// @flow
exports.hello = function (val: string): string {
return "hello " + val;
}
这时候,flow已经知道该文件是需要做类型检测的,这时候运行flow init
命令初始化flow
$./node_module/.bin/flow init
flow会自动在该目录下创建.flowconfig文件,接着我们运行flow
命令,就可以在后台启动flow进程进行类型检测了
Spawned flow server (pid=28687)
Logs will go to /private/tmp/flow/zSUserszSlizZzZzZledzSDevzSflow-proj.log
No errors!
如果需要停止flow进程,只需要运行flow stop
命令即可
$./node_module/.bin/flow stop
到现在为止,虽然flow已经可以正常运行了,然而因为我们在js代码里添加了额外类型声明,导致js代码不能直接在浏览器里执行,这时候我们需要做第三步,通过代码构建的方式,把js转换成浏览器能执行的形式。
这里我采用的webpack+babel作为我们构建环境,所以首先我们需要安装webpack和babel
$npm install webpack babel-core babel-loader --save-dev
然后我们需要安装babel的flow套装,使得babel支持flow
$npm install babel-preset-flow --save-dev
然后我们编写webpack的配置文件webpack.config.js
module.exports = {
entry: "./hello.js",
output: {
path: __dirname,
filename: "hello.bundle.js"
},
module: {
loaders: [
{
test: /.js$/,
exclude: /node_module/,
loaders: ["babel-loader"]
}
]
}
};
还有我们的babel配置文件.babelrc
{
presets: [
"flow"
]
}
到这里为止,我们已经完成了构建环境的配置,接下来执行webpack
命令,就会产生我们的目标文件(hello.bundle.js)
$./node_modules/.bin/webpack
打开目标文件,我们就会发现,flow的类型声明已经被正确去掉了。
到现在为止,整个flow的环境已经算搭建完成了,然而在写了没几行代码之后,我们就会发现,每次要对代码进行检测,都需要打开terminal,敲上一堆命令才能看到结果,实在是不爽。有没有办法可以节省这些多余的工作,把flow集成到编辑器中呢?答案当然是肯定的。
这里我使用的编辑器是sublime text3,如果有的同学是使用其他编辑器,可以在这里,找一下
对与像我一样使用st3的同学,首先我们要在st3里安装SublimeLinter插件,Ctrl + Shift + P
打开Package Control,选中Package Control:Install Package。输入SublimeLinter
,安装即可。
SublimeLinter是一个语法校验的框架,但其本身并不会去做实际的校验工作,我们需要另外安装SublimeLinter的flow插件,同样是打开Package Control,输入SublimeLinter-flow
,安装即可。
这时SublimeLinter-flow就会在当前文件夹向上查找.flowconfig文件,并对带有// @flow
的文件进行自动检测,如果出现错误,就会直接在编辑器上提示,十分的方便。
加入eslint语法校验
除了类型检测,有时候我们还需要对js进行语法校验,当然很多成熟都工具都可以帮我们完成这样的功能,这里我使用的eslint,对于其他的例如jshint,jslint,小伙伴都可以自行google。
首先我们要安装eslint以及eslint-loader,使得eslint可以集成到webpack里
$npm install eslint eslint-loader --save-dev
安装好eslint后,就可以执行eslint --init
命令来初始化eslint了
$npm ./node_modules/.bin/eslint --init
这时eslint就会问你一些问题,一步步帮你完成初始化的工作,并生成对应.eslintrc文件,这时候,我们需要更新一下webpack.config.js文件,加入对eslint的支持,记得eslint-loader一定要写在babel-loader之后,这样eslint才能正确执行
module.exports = {
entry: "./hello.js",
output: {
path: __dirname,
filename: "hello.bundle.js"
},
module: {
loaders: [
{
test: /.js$/,
exclude: /node_module/,
loaders: ["babel-loader", "eslint-loader"]
}
]
}
};
不过如果这时候,你试着执行webpack
命令进行构建,你会发现,报错了,原因是eslint不认识flow的类型声明语法。为了让eslint能通过flow的类型声明,我们需要安装两个工具,一个是flow的eslint插件eslint-plugin-flowtype,另一个是eslint的babel版js解析器babel-eslint,这是由于eslint默认的espree解析器认不得flow的类型声明
$npm install eslint-plugin-flowtype babel-eslint --save-dev
接着我们修改一下eslint的配置文件.eslintrc,把parser
字段设置为babel-eslint
,然后分别在extends
和plugins
加入flow相应的设置
{
"env": {
"node": true,
"browser": true,
"commonjs": true,
"es6": true
},
"extends": [
"eslint:recommended",
"plugin:flowtype/recommended",
],
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module"
},
"plugins": [
"flowtype"
],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"]
}
}
done,现在我们再次执行webpack
命令就能正常进行构建了。
最后最后,我们再通过在sublime中安装在SublimeLinter的插件SublimeLinter-contrib-eslint就可以让我们的编辑器也支持eslint的语法校验了。
这就是我这次给大家分享的,如何大家一个“摩登”的前端开发环境