阅读 3810

Flow静态类型检查及在Vue项目中的使用

前言

结合由Facebook出品的静态类型检查工具Flow,和最近将Flow引入到一个Vue.js项目中的实践经验,本文想聊聊Flow类型检查工具和它在Vue项目中实际使用的效果。

目录

  • 为什么需要引入类型检查?
  • Flow是什么?
  • Flow的作用
  • Flow在Vue项目中的使用
  • Flow的配置过程
  • 在Vue组件中使用Flow的几个方法
  • 类型检查工具对团队有什么好处?
  • 总结

为什么需要引入类型检查?

JS作为一个弱类型语言,一个著名的黑点是它很容易就写出非常隐蔽的隐患代码,在编译期甚至运行时看上去都不会报错,但是可能会发生各种各样奇怪的和难以解决的bug。
类型检查是当前动态类型语言的发展趋势,根据stateofjs的调查结果,JS的强类型超集TypeScript已经有了相当的知名度,吸引了大量开发者的学习兴趣,并且大部分开发者计划继续了解或者使用。


所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时动态检查类型),使编写js具有和编写Java等强类型语言相近的体验。它可以:

  1. 使得大型项目可维护
  2. 增加代码的可读性
  3. 通常会有更好的IDE支持

Flow是什么?

Flow是一个由Facebook出品的JavaScript静态类型检查工具,它与Typescript不同的是,它可以部分引入,不需要完全重构整个项目,所以对于一个已有一定规模的项目来说,迁移成本更小,也更加可行。除此之外,Flow可以提供实时增量的反馈,通过运行Flow server不需要在每次更改项目的时候完全从头运行类型检查,提高运行效率。
Flow和Typescript都是给Javascript增加类型检查的优秀解决方案,两者的简单对比如下:

工具 Flow Typescript
公司 Facebook 微软
star 12k+ 23k+
文档支持程度 中等 更多
第三方库支持工具 Flow-typed tsd
IDE支持 Webstorm自带插件支持 Webstorm支持,Visual Studio原生支持
其他 自由度更高,老项目的迁移成本低 工程化强,社区活跃度和官方支持力度更高,适合新项目

两者在代码语法上有大量相似的地方,除了对于一些数据类型的支持不一样,具体请查看Flow的文档。关于Flow和Typescript的比较,可以简单总结为:对于新项目,可以考虑使用TypeScript或者Flow,对于已有一定规模的项目则建议使用Flow进行较小成本的逐步迁移来引入类型检查。

Flow的作用

一个简单的demo如下。


运行Flow命令,这个demo的运行结果如下图所示:


对于需要使用 Flow 进行类型检查的 js 文件,在开头加入 @Flow 注释,即可引入Flow。通过demo可以看到,Flow可以帮助找出由于不合理的类型操作引起的错误,包括运算符操作,函数参数类型和返回值类型等。Flow也支持自定义类型声明,泛型声明等类型语言相关的操作,详细的内容可以参考文档:Flow.org/en/docs/lan…

Flow在Vue项目中的使用

Flow在Vue项目中的具体使用价值有:

  • 使用Flow可以在不需要重构整个Vue项目(如UI组件迁移成本)、不需要引入大量的工具链(eslint+babel)、不需要第三方库一定支持的情况下引入静态类型检查
  • Vue.js官方对TypeScript做了支持,但是项目所依赖的第三方库不一定支持TypeScript,从全局考虑TypeScript的迁移成本比较大
    在尝试Flow+Vue.js的实践过程中,主要的步骤包括:1,使用Flow-typed工具(github.com/Flowtype/Fl… packages的支持;2,在一个由Vue cli (webpack + babel + eslint) 生成的脚手架项目中配置 Flow(见后文);3,Vue 的单文件组件结构如何支持 Flow,在业务项目的实践中前后使用了三种方案,也会在后文分别介绍这几种方法和其优缺点。

Flow的配置过程

  1. 假设目前有一个从vue-cli命令行生成的项目:vue init webpack-simple Flow-vue-demo。关于Babel,Eslint(可选)和Flow,需要安装所需的 npm packages,参考列表如下:

Babel:

  • babel-plugin-syntax-Flow
  • babel-plugin-transform-class-properties
  • babel-plugin-transform-Flow-strip-types

Eslint: (可选)

  • eslint
  • babel-eslint
  • eslint-plugin-html
  • eslint-plugin-Flowtype-errors
  • eslint-plugin-vue
  • eslint-config-vue

Flow:

  • Flow-bin
  1. Webstorm自带了Flow的支持,需要开启,结合eslint配置Flow相关的rules,在编辑时通过eslint即可自动报错。
  2. 安装Flow,运行Flow init && Flow check。配置.vue文件为Flow的检查范围。
  3. 使用 Flow-typed 处理第三方的 npm packages 的类型声明。
  4. 必要的话增加自定义的类型声明文件,如自定义的对象等,具体可以参考Flow文档。

在Vue组件中使用Flow的几个方法

在前面的demo中已经展示了纯JS文件里面怎么用Flow,那么在一个vue组件文件中应该如何配置呢?有下面几种方法。
方法一:直接在script标签中,像纯js文件处理一样添加Flow注释,发现可以正常编译运行,但是运行Flow check是无效的。
方法二:注释掉template, style和script标签,由于Vue的编译器即使注释了也会识别其中的<template>, <style> 和 <script> 标签,而Flow检查会忽略注释,因此对于Flow来说可以当做一个javascript文件进行处理。demo如下图所示。

对于这样处理的vue文件,Flow命令能够报出关于一般的函数声明的类型检查错误,但是对于绑定到Vue实例(this)上的方法是无效的。因此Flow类型检查不是100%覆盖。这种方法的主要问题在于代码和注释混用不便于阅读,目前Flow社区有一个open issue就是关于这个问题的,即不能自动检测中文件中的script标签,请见:github.com/facebook/Fl…

方法三:Vue文件引用外部的js文件,将js部分单独抽离出来进行类型检查。该方法的优点在于可以用到Flow的所有功能,但是没有了vue单文件组件的结构,项目结构略显臃肿。(每个组件都会有至少两个文件)。如下图:

三种解决方法的优缺点对比如下表所示:

方法 Pros Cons
标签中直接添加Flow 代码添加量最小 Flow 类型检查无效. 不予考虑
注释template中的标签 1. 可以通过Flow check检查出部分的类型错误 2. 最接近使用直觉. 目前是一个open issue 1. 对于和组件无关的函数以及import. 可以正常工作. 但是不是100%覆盖 2. 看上去样式比较糟糕
Vue文件引用外部的js文件 1. 通过eslint中通过使用Flow插件. 配置Flow规则. 可以在编辑时实时提示 2. 没有影响文件结构 3. 单独的js文件可以几乎完全使用Flow的所有功能 1. method仍然不能自动识别. 由于Vue中的一些函数一般没有return value. 需要手动判断类型防止bug 2. 一个组件的代码被分拆到多个文件. 不如单文件组件那么直观

类型检查工具对团队有什么好处?

通过在一个Vue技术栈的实际业务项目中引入Flow,我们大致获得了这些收益:

  1. 几乎消灭了由函数数据类型引起的bug
  2. 无需额外的关于变量、参数、返回值类型的注释,可以让读者了解必要的附加信息
  3. 大量减少由于使用第三方库不当引起的类型错误
  4. 可以在CI系统中集成
  5. 工具链配置成本比较低,只需要很少的工作量即可达到这些效果

关于类型检查工具,读者可能需要考虑的问题,回答如下表所示。

Question Answer
类型检查可以让我的代码bug free吗? 不能保证bug free,只能检查类型错误
可以提高我的生产力吗? 需要多写一些代码,但是相应地可以减少很多runtime debug的时间
将Flow引入我的项目,所需要的工作量大吗? 不大,可以逐步引入
我的项目需要长期维护? 请使用Flow或者Typescript
我的项目非常简单? 简单项目不一定需要类型检查,可能会有些多余
我的项目需要重构? 请引入类型检查
我的项目对于bug free的要求非常高? 请引入类型检查,减少类型错误等难以发现的bug
我的项目开发人员流动很频繁? 请引入类型检查,增加项目可读性
我的项目有大量的算法计算? 请引入类型检查,减少隐蔽的类型转换错误等

总结

  1. Flow或者TypeScript都是静态类型检查的优秀解决方案,能够给有类型检查需求的一定规模的项目带来实际收益。
  2. Flow+Vue目前看来有些使用上的不便,期待尽早解决open issue,能够自动识别Vue组件文件的标签,从而使得Flow在vue项目中的使用更加流畅。