Press "Enter" to skip to content

Flow知识图谱及其在前端项目中的应用

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

Flow <JavaScript静态类型检测工具>

 

一、Flow 是什幺

 

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

 

// @flow
function square(n: number): number {
  return n * n;
}
square("2", "2"); // Error!

 

使用flow之后,square函数的参数和返回值,都可以指定类型。当你在代码中写square(‘2’, ‘2’)的时候,用 flow
命令一检查,不需要看业务逻辑,就知道调用的参数有错误

 

1.1 Flow的作用

 

Flow可以帮助找出由于不合理的类型操作引起的错误,包括运算符操作,函数参数类型和返回值类型等。Flow也支持自定义类型声明,泛型声明等类型语言相关的操作。

 

1.2 Flow的优点

 

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

 

1、使得大型项目可维护

 

2、增加代码的可读性

 

3、通常会有更好的IDE支持

 

4、几乎消灭了由函数数据类型引起的bug

 

5、无需额外的关于变量、参数、返回值类型的注释,可以让读者了解必要的附加信息

 

6、大量减少由于使用第三方库不当引起的类型错误

 

7、可以在CI系统中集成

 

8、工具链配置成本比较低,只需要很少的工作量即可达到这些效果

 

9、

 

1.3 Flow 与 TypeScript 的区别

工具 Flow TypeScript
出品公司 facebook 微软
star 20.7k+ 61.2k+
文档支持程度 一般 广泛
第三方库支持工具 Flow-typed tsd
IDE支持 Webstorm自带插件支持 Webstorm支持,Visual Studio原生支持
其他 自由度更高,老项目的迁移成本低 工程化强,社区活跃度和官方支持力度更高,适合新项目

 

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

 

1.4 Flow如何进行类型检查

 

Flow 的类型检查方式

 

1.类型推断:通过变量的使用上下文来推断出变量类型,然后根据这些推断来检查类型。

 

2.类型注释:事先注释好我们期待的类型,Flow 会基于这些注释来判断。

 

类型推断

 

function split(str) {
    return str.split('')
}
split(11)

 

Flow 检查上述代码后会报错,因为函数 split 期待的参数是字符串,而我们输入了数字。

 

类型注释

 

添加类型注释可以提供更好更明确的检查依据

 

/*@flow*/
function test(x: number, y: number): number {
    return x + y
}
test('str', 0)

 

因为函数参数的期待类型为数字,而我们提供了字符串。flow会报错

 

Flow 的工作流程

 

第一步:模仿 C#/Java 之类的语言,在编码过程中对类型进行定义。

 

下面demo根据 Flow 的规则,进行类型声明的代码。虽然看起来没有什幺用,但可以简单讲述如何定义类型了。这个函数接收一个 string 类型的参数,参数名为 str,函数的返回值是 number 。定义了一个类型为 number 的常量 len,它的值为 str 的长度,并且将其返回。

 

function getStringLength(str: string):number {
    const len: number = str.length;
    return len;
}

 

第二步:通过 Flow 提供的工具进行类型检查。如果有类型不匹配的地方,工具会提示错误。

 

第三步:发布到线上的代码是不能加类型声明的,因为这样不符合规范,浏览器也不认。所以,我们需要对源代码进行一次编译,将所有声明的类型去掉,像正常的 JS 代码一样了。上面的代码编译过后将会变成:

 

function getStringLength(str) {
    const len = str.length;
    return len;
}

 

二、基础类型检测

 

flow.js 中定义了的5种最简单的类型,( 都是小写
),其中void对应js中的undefined

 

要想加入到javascript中,只需要在关键的地方声明想要的类型。其它时间我们的代码还是熟悉的javascript

 

2.1 boolean

 

//flow/src/demo.js
//在文件的头部加入,用注释加入 `@flow` ,这样flow.js才会检查这个文件。
//@flow
//在声明变量时,在变量名加入 `:[Type]` 来表明变量的类型,其它类型同理。
var bol:boolean = true;
//当然,也可以不加类型,这样就跟原来的js一样了。
var bol = true;

 

2.2 number

 

//flow/src/demo.js
//在文件的头部加入,用注释加入 `@flow` 声明,这样flow.js才会检查这个文件。
//@flow
//在声明变量时,在变量名加入 `:[Type]` 来表明变量的类型,其它类型同理。
var num:number = 1;
//当然,也可以不加类型,这样就跟原来的js一样了。
var num = 1;

 

2.3 string

 

//flow/src/demo.js
//在文件的头部加入,用注释加入 `@flow` 声明,这样flow.js才会检查这个文件。
//@flow
//在声明变量时,在变量名加入 `:[Type]` 来表明变量的类型,其它类型同理。
var str:string = 'xiaofang';
//当然,也可以不加类型,这样就跟原来的js一样了。
var str = 'xiaofang';

 

2.4 null

 

//flow/src/demo.js
//在文件的头部加入,用注释加入 `@flow` 声明,这样flow.js才会检查这个文件。
//@flow
//在声明变量时,在变量名加入 `:[Type]` 来表明变量的类型,其它类型同理。
var aa:null = null;
//当然,也可以不加类型,这样就跟原来的js一样了。
var aa = null;

 

如果先让任意类型可以是 null 或者 undefined 则需要写成 ?T 的格式即可,注意T就是类型

 

/*@flow*/
var age: ?number = null
age可以为数字或者 null

 

2.5 void

 

//flow/src/demo.js
//在文件的头部加入,用注释加入 `@flow` 声明,这样flow.js才会检查这个文件。
//@flow
//在声明变量时,在变量名加入 `:[Type]` 来表明变量的类型,其它类型同理。
var vv:void = void;
//当然,也可以不加类型,这样就跟原来的js一样了。
var vv = void;

 

三、复杂类型检测

 

以下几个类型比较复杂,而且可以相互嵌套。

 

3.1 对象:Object

 

//flow/src/demo.js
//@flow
//Object大写的O
var o:Object = {
  hello:'h'
};
//声明了Object的key
var o2:{key:string} = {
  key:'z233'
};

 

3.2 数组:Array

 

//flow/src/demo.js
//@flow
//基于基本类似的数组,数组内都是相同类型
var numberArr:number[] = [12,3,4,5,2];
//另一个写法
var numberAr2r:Array<number> = [12,3,2,3];
var stringArr:string[] = ['12','a','cc'];
var booleanArr:boolean[] = [true,true,false];
var nullArr:null[] = [null,null,null];
var voidArr:void[] = [ , , undefined,void(0)];
//数组内包含各个不同的类型数据
//第4个原素没有声明,则可以是任意类型
var arr:[number,string,boolean] = [1,'a',true,function(){},];

 

3.3 函数

 

函数比较特殊,因为函数的核心在于参数和返回值,函数作为类型本身并没有作用。

 

//flow/src/demo.js
//@flow
/**
 * 声明带类型的函数
 * 这里是声明一个函数fn,规定了自己需要的参数类型和返回值类型。
 */
function fn(arg:number,arg2:string):Object{
  return {
    arg,
    arg2
  }
}
//同理,ES2015箭头函数的写法
var fn2 = (arg:number,arg2:string):Object => {
  return {
    arg,
    arg2
  }
}
/**
 * 这里是声明变量fn2,规定了它所需的函数的特征:
 * 参数: (arg:string,arg2:number)
 * 返回值:Object
 */
var fn3:(arg:string,arg2:number)=>Object = function(){
  return {}
}
/**
 * 对比下面这种写法,
 * 两者的声明的地方不一样,造成的意义也不同。
 */
var fn4 = function(arg:string,arg2:Object):number{
  return 1;
}

 

3.4 自定义Class

 

声明一个自定义类,然后用法如同基本类型

 

//flow/src/demo.js
/*@flow*/
class Person {
constructor(name: string, age: string | number) {
this.name= name
this.age= age
this.sex= false
}}
var per: Person = new Person('xiaoli', 4)
var obj: { arr: Array<string>, per: Person} = {
arr: ['hello']
per: new Person('hello', 3)
}

 

四、如何使用

 

4.1 配置过程

 

4.1.1 设置编译器

 

yarn add --dev babel-cli babel-preset-flow

 

在根目录新建一个 .babelrc
,并配置flow作为presets

 

{
  "presets": ["flow"]
}

 

其中 "flow"
就是指刚才安装的 babel-preset-flow
的简写,省略了 babel-preset-

 

也可以将这个命令配置到 package.json
文件中:

 

{
  "name": "flow-demo",
  "main": "lib/index.js",
  "scripts": {
    "build": "babel src/ -D lib/",
    "prepublish": "yarn run build"
  }
}

 

通过以上配置babel,经过编译去掉了类型约束

 

去类型前

 

// @flow
function square(n:number): number {
    return n * n;
}
square(2)

 

编译去类型之后

 

function square(n) {
    return n * n;
}
square(2);

 

删除flow类型注解的另一种方法 flow-remove-types

官方文档: https://flow.org/en/docs/tool…

 

# 如果没有package.json文件,先生成
yarn add --dev flow-remove-types
# or
npm install --save-dev flow-remove-types

 

去类型

 

# 为了方便,先将a.js移到src目录下
$ yarn run flow-remove-types src/ -d dist/
yarn run v1.12.3
$ F:\youshengyouse\del\node_modules\.bin\flow-remove-types src/ -d dist/
src\a.js
 ↳ dist\a.js
Done in 0.30s.

 

4.1.2 设置flow

 

推荐将flow安装到当前项目目录,而不是全局安装

 

yarn add --dev flow-bin

运行 yarn run flow init
生成配置文件 .flowconfig

运行 yarn run flow
进行代码检查

停用flow后台进程,使用 flow stop

3.flow配置文件.flowconfig

 

flowconfig大概遵循INI文档格式。一个 .flowconfig
文件,可以包含下以五个部分:

 

[include]
[ignore] 用正则表达式匹配文件或目录,满足条件的将被flow忽略;<PROJECT_ROOT>表示项目根目录
[libs]
[options]
[version]

 

[ignore]

 

[ignore]
# Ignore Docs
<PROJECT_ROOT>/docs/.*
<PROJECT_ROOT>/.*/docs/.*

 

react的ignore部分,都使用了<PROJECT_ROOT>这种绝对路径的写法。在配置中使用注释,可以在行首加 #

 

[ignore]
<PROJECT_ROOT>/.*/node_modules/y18n/.*

 

[libs]

 

[libs]
./node_modules/fbjs/flow/lib/dev.js
./flow

 

[options]

 

[options]
# 可选项node|haste,haste已不再被维护,可react还在用
module.system=haste
# 允许在class中使用static关键字
esproposal.class_static_fields=enable
# 允许在class中使用instance关键字
esproposal.class_instance_fields=enable
# 不允许在class中使用下划线作为私有函数
munge_underscores=false
# 约束的类型,可以用来代替TODO
suppress_type=$FlowFixMe
# 这个正则是什幺含义?TODO
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-3]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*www[a-z,_]*\\)?)\\)

 

[version]

 

[version]
^0.41.0

 

version是指 flow-bin
的版本,可以用 yarn run flow version
查看

 

4.2 VUE组件中使用

 

方法一

 

注释掉template, style和script标签,由于Vue的编译器即使注释了也会识别其中的<template>, <style> 和 <script> 标签,而Flow检查会忽略注释,因此对于Flow来说可以当做一个javascript文件进行处理

 

对于这样处理的vue文件,Flow命令能够报出关于一般的函数声明的类型检查错误,但是对于绑定到Vue实例(this)上的方法是无效的。因此Flow类型检查不是100%覆盖。这种方法的主要问题在于代码和注释混用不便于阅读

 

// @flow
/*
<style>
</style>
<template>
  <div>
     <p>这是一个栗子</p>
  </div>
</template>
*/
//<script>
    function square(n:number): number {
        return n * n;
    }
//</script>

 

方法二

 

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

 

<template>
  <div>
     <p>这是一个栗子</p>
  </div>
</template>
<script src="./flow-vue.js">
</script>

 

4.3 类型自动检查

 

使用flow check来进行类型检查,不是很方便,我想babel的插件来进行类型检查,并在编辑器如 vs code
中自动检查,这样效率就会高很多,这就要用到插件 babel-plugin-typecheck
,它与预置flow的功能完全不一样, babel-plugin-typecheck
是检查代码中的类型是否有错, babel-preset-flow
是负责删除类型的,这样js代码在执行时就好象从来没有加过类型一样。

 

在vs code中配置类型

 

VS Code
中搜索 flow
,发现有 vscode-flow-ide、Flow-Language-Support
两个插件,在这里以 vscode-flow-ide
为例

 

先安装 vscode-flow-ide

 

条件:

 

.flowconfig
flow-bin

 

配置(默认就行)

 

VS Code
左下角管理/设置/User Settings/Extensions/Flow-IDE Configurations(只有启用后才能配置,否则找不到这项),下面是文字版,实际上在面板中就可以设置

flowide.enable: 启用/关闭
flowide.pathToFlow: Absolute path to the Flow executable. Set it only if the default behaviour of the extension doesn’t work out for you. The extension will try first to read it from local node_modules/flow-bin or globally if not otherwise set here.
flowide.useCodeSnippetsOnFunctionSuggest – Add the function paramters when selecting a function to autocomple.

重启vs Code,就会发现可以报错了,现在可以去掉顶部的 // @flow
及传递不合要求的参数测试下。

 

如果在problem窗口有错误, [ts]'types' can only be used in a .ts file. 8010
,请在扩展中找到typescript,找到”javascript.validate.enable”: false

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注