Flow
Flow是facebook出品的JavaScript静态类型检查工具,Vue.js的源码利用了Flow做了静态类型检查,所以了解Flow有助阅读源码
为什么用Flow
JavaScript是动态类型语言,他的灵活性有目共睹,但是过于灵活的副作用是很容易写出非常隐蔽的bug,在编译期甚至看上去都不会报错,但是在运行阶段就可能出现各种奇怪的bug
类型检查是当前动态类型语言的发展趋势,所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时态检查类型),使编写JavaScript具有和编写JAVA等强类型语言相近的体验。
项目越复杂就越需要通过工具的手段来保证项目的维护性和增强代码的可读性。Vue.js在做2.0重构的时候,在Es2015的基础上,出了ESLint保证代码分割之外,也引入了Flow静态类型检查。之所以选择Flow,主要是因为Babel和Eslint都有对应的Flow插件支持语法,可以完全沿用现有的构建配置,非常小成本的改动就可以拥有静态类型检查的能力。
Flow的工作方式
通常类型检查分成2种方式:
- 类型推断:通过变量的使用上下文来推断出变量类型,然后根据这些推断来检查类型。
- 类型注释:事先注释好我们期待的类型,Flow会基于这些注释来判断。
类型判断
它不需要任何代码修改既可进行类型检查,最小化开发者的工作量。他不会强制你改变开发习惯,因为他会自动推断出变量的类型。这就是所谓的类型推断,Flow最重要的特性之一。
通过一个简单例子说明一下:
/*@flow*/``
function split(str) {
return str.split(' ')
}
split(11)
复制代码
Flow检查上述代码会报错,因为函数split期待的参数是字符串,而我们输入数字。
类型注释
如上所述,类型推断是flow最有用的特性之一,不需要编写类型注释就能获取有用的反馈,但在特定的场景下,添加类型注释可以提供更好更明确的检查依据。
考虑如下代码:
/*@flow*/
function add(x, y) {
return x + y
}
add('hello', 11)
复制代码
Flow检查上述代码时检查不出任何错误,因为从语法层面考虑,+既可以用在字符串上,也可以用在数字上,我们并没有明确支出add()的参数必须为数字。
在这种情况下,我们可以借助类型注释来指明期望的类型。类型注释是以冒号:开头,可以再函数参数,返回值,变量申明中使用。
如果我们在上端代码中添加类型注释,就会变成如下:
/*@flow*/
function add (x: number, y: number): number {
return x + y
}
add('hello', 11)
复制代码
现在Flow就能检查出错误,因为函数参数的期待类型为数字,而我们提供了字符串。
上面的例子是针对函数的类型注释。接下来我们来看看flow能支持的一些常见的类型注释。
数组
/*@flow*/
var arr: Arrary<number> = [1, 2, 3]
arr.push('hello')
复制代码
数组类型注释的格式是Arrary,T表示数字中每项的数据类型,在上述代码中,arr是每项均为数字的数组。如果我们给这个数组添加一个字符串,Flow能检查出错误。
类和对象
/*@flow*/
class Bar {
x: string ; // x 是字符串
y: string | number // y 可以使字符串或者数字
z: boolean ;
constructor(x: string, y: string | number) {
this.x = x
this.y = y
this.z = false
}
}
var bar:Bar = new Bar('hello', 4)
var obj: { a: string, b: number, c: Array<string>, d: Bar} = {
a: 'hello',
b: 11,
c: ['hello', 'world'],
d: new Bar('hello', 3)
}
复制代码
类的类型注释格式如上,可以对类自身的属性做类型检查,也可以对构造函数的参数做类型检查。这里需要注意的是属性y的类型中间用|做间隔,表示y的类型既可以是字符串也可以是数字。
Null
若想任意类型T可以为null或者undefined,只需要类似如下写成?T的格式既可。
/*@flow*/
var foo: ?string = null
复制代码
此时,foo可以为字符串,也可以为null。
目前我们值列举了flow的一些常见的类型注释。如果想了解所有类型,清移步Flow的官方文档
Flow在Vue.js源码中的应用
有时候我们想引用第三方库,或者自定义一些类型,但Flow并不认识,因此检查的时候会报错。为了解决这类问题,Flow提出了一个libdef的概念,可以用来识别这些第三方库或者是自定义类型,而vue.js也利用了这一特性。
在vue.js的主目录下有.flowconfig文件,他是flow的配置文件,感兴趣的同学可以看官方文档。这其中的[libs]部分用来描述包含指定库定义的目录,默认是名为flow-typed的目录。
这里[libs]配置的是flow,表示指定的库定义都在flow文件夹内。我们打开这个目录,会发现文件如下:
flow
|--compiler.js #编译相关
|--component.js #组件数据结构
|--global-api.js #Global API结构
|--modules.js #第三方库定义
|--option.js #选项相关
|--ssr.js #服务端渲染相关
|--vnode.js #虚拟node相关
复制代码
可以到,vue.js有很多自定义类型的定义,在阅读源码的时候,如果遇到某个类型并想了解他完整的数据结构的时候,可以回来翻阅这些数据结构的定义。
总结
通过对Flow的认识,有助于我们阅读Vue的源码,并且这种静态类型检查的方式非常有利于大型项目源码的开发和维护。类似Flow的工具还有如TypeScript,感兴趣的同学可以自行了解一下。