Flow简易教程——安装篇

时间:2023-03-09 00:21:33
Flow简易教程——安装篇

.mydoc_h1{
margin: 0 0 1em;
}
.mydoc_h1_a{
color: #2c3e50;
text-decoration: none;
font-size: 2em;
}
.mydoc_h1_h1{
margin: 45px 0 8px;
padding-bottom: 7px;
font-size: 28px;
}
.mydoc_h1_content{
}.mydoc_p{
line-height: 1.6em;
margin: 1.2em 0 -1.2em;
padding-bottom: 1.2em;
position: relative;
z-index: 1;
color: #333;
}/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.mydoc_code{
overflow-x: auto;
position: relative;
background-color: #f8f8f8;
padding: 0;
line-height: 1.1em;
border-radius: 2px;
margin: 1.2em 0;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8AgMAAABHkjHhAAAACVBMVEWAgIBaWlo+Pj7rTFvWAAAAA3RSTlMHCAw+VhR4AAAA+klEQVQoz4WSMW7EQAhFPxKWNh2FCx+HkaZI6RRb5DYbyVfIJXLKDCFoMbaTKSw/8ZnPAPjaH2xgZcUNUDADD7D9LtDBCLZ45fbkvo/30K8yeI64pPwl6znd/3n/Oe93P3ho9qeh72btTFzqkz0rsJle8Zr81OLEwZ1dv/713uWqvu2pl+k0fy7MWtj9r/tN5q/02z89qa/L4Dc2LvM93kezPfXlME/O86EbY/V9GB9ePX8G1/6W+/9h1dq/HGfTfzT3j/xNo7522Bfnqe5jO/fvhVthlfk434v3iO9zG/UOphyPeinPl1J8Gtaa7xPTa/Dk+RIs4deMvwGvcGsmsCvJ0AAAAABJRU5ErkJggg==');
}
.mydoc_code_pre{
padding: 1.2em 1.4em;
line-height: 1.5em;
margin: 0;
}:not(.mydoc_li) + .mydoc_li, .mydoc_li:first-child{
margin-top: 10px;
}
.mydoc_li + .mydoc_li{
margin-top: -10px;
}
.mydoc_li{
margin: 0;
color: #34495e;
margin-bottom: 10px;
position: relative;
}.mydoc_h2{
margin: 35px 0 0.8em;
}
.mydoc_h2_a{
font-size: 1.5em;
text-decoration: none;
color: #2c3e50;
}
.mydoc_h2_a::before{
content: '';
display: block;
margin-top: -40px;
height: 40px;
visibility: hidden;
}
.mydoc_h2_h2{
margin: 5px 0 8px;
border-bottom: 1px solid #ddd;
font-size: 22px;
padding-bottom: 1em;
}
.mydoc_h2_content{
}.mydoc_strong{
font-weight: 600;
color: #2c3e50;
}.mydoc_a{
color: #42b983;
font-weight: 400;
text-decoration: none;
cursor: pointer;
}.mydoc_blockquote{
padding: 12px 5px 12px 30px;
margin: 2em 0 0 8px;
border-width: 0;
border-left: 4px solid #f66;
background-color: #f8f8f8;
position: relative;
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
line-height: 1.6em;
}
.mydoc_blockquote::before{
position: absolute;
top: 14px;
left: -12px;
background-color: #f66;
color: #fff;
content: "!";
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
font-weight: bold;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 14px;
border-radius: 10px;
}
.mydoc{
font-size: 14px;
overflow: hidden;
}

这是一个Flow的简易教程,属于笔者学习Flow并不断踩坑的过程总结。内容大多翻译Flow官网已经笔者自己的实践。

JavaScript是一个弱类型的解释性语言,无法在编译环节进行静态类型校验,这给JavaScript代码重构带来了巨大的困难,有一句话说的好——动态类型一时爽,重构全家火葬场,充分说明动态类型在编码阶段的高效和重构阶段的低效,因此大家都希望能够让JavaScript也具备静态类型检查功能。Flow就是一个用于JavaScript的静态类型检查的工具,最早由Facebook在2014年的Scale大会上推出,它让JavaScript也可以进行静态类型校验的开发工作,同时兼容JavaScript的弱类型、动态类型的特性,让我们可以开发出利于重构JavaScript代码。

Flow看似与微软的TypeScript有些类似,不过笔者看来TypeScript更像是另一门新语言,而Flow则更像是仅给JavaScript增加了用于静态类型检查的注解(甚至可以在完全符合es语法的情况下通过增加注释来做静态类型检测)。与其说Flow是一门语言不如说他说一个工具。目前已经有很多JavaScript项目都是用了Flow开发的,例如:vue2、react生态下的很多项目等。

我们可以在前端开发中使用它,当然你也可以使用在nodejs中,或者在任何你想使用它的地方。

Flow的安装方法很多,这里仅介绍npm安装,事实上Facebook提供了一个新一代node包管理工具——yarn,与Flow算是相同生态下的工具,不过两者使用起来差异不大,因此以下实例均仅基于npm。使用npm安装Flow:

npm install flow-bin -g

Flow的通用使用流程是:

  • 初始化Flow项目
  • 开启Flow的后台进程,这样Flow会自动在每一次文件内容变化的时候执行静态类型校验
  • 使用Flow注释书写JavaScript代码
  • Flow自动检测出关于类型的错误,并提示

初始化Flow

因此第一步是初始化一个项目,安装完flow-bin后就可以初始化一个项目了,如果如上所示将flow-bin安装在全局,则可以使用flow命令:

flow init

这个命令要在工程的根目录下执行,执行后就会出现一个名为.flowconfig的文件,Flow监听文件变化需要一个后台进程,而.flowconfig文件就是告诉这个后台进程应该做什么。

[ignore]
[include]
[libs]
[options]
[version]

如上所示,.flowconfig有5部分组成:

  • [include]告诉Flow那些文件是需要监听的,通常Flow仅会校验项目根目录下拥有Flow注解的文件,使用include可以将工程外的文件也指定进来。
  • [ignore]告诉Flow忽略哪些目录或文件,这些目录里面的文件即使有Flow注解也不会被监听。这很适合给node_modules这样的目录使用,以增加Flow执行效率。
  • [libs]告诉Flow使用哪些库定义(library definitions),至于什么是库定义,笔者准备到了高级篇在介绍。
  • [version]指定Flow的版本

开启Flow的后台进程

Flow的最大好处就是能快速地校验你代码的错误,一旦你初始化好你的工程后,就可以开始启动Flow进程去校验代码了:

flow status

这实际上是启动了一个后台进程,停止这个进程只需执行:

flow stop

使用Flow注释书写JavaScript代码

现在我们就可开始写Flow文件了,我们之前说了,Flow的后缀名也是js,那么Flow是怎么知道一个文件是Flow文件还是JavaScript文件呢?Flow虽然完全兼容JavaScript代码,但是如果JavaScript代码出现了type相关的错误,Flow也会在校验时候提示错误的,而对于JavaScript则是运行时候才去报错。两种虽然都是报错,但是有本质上的区别,因此我们必须让Flow区分出哪些是JavaScript文件,哪些是Flow的文件。

Flow区别JavaScript文件的方式非常简单,就是给文件加注解:

// @flow
// js的代码部分

注意注解前边的注释不能省,而且注解要放到文件的最上面。除了// @flow注解,还可以用/* @flow */

加上注解后,Flow的后台进程就会自动监听这些文件的变化,并对其做静态类型校验。

到此为止,理论上我们每修改一个文件就应该能够看到文件的校验结果,但实际上并没有出现更改后的校验结果。因为flow status是一个后台进程,所有我们看不到校验结果,如果想要获得当前校验结构需要再执行一下flow status,这一次执行速度明显会比第一次快很多。虽然速度快了,但是和我们想要的自动监听并提示还有差距,不过没关系,稍后笔者会告诉大家解决方法。

前面介绍的是基于后台进程做自动执行校验,除此之外还可以通过cli命令进行手动校验:

flow check

这种方法的执行速度要比后台进程方式慢。

通过以上方法我们就实现了静态类型校验的工作,但是想要Flow能够像JavaScript一样运行还需要“编译”一下才行。接下来介绍编译器的安装。

Flow需要“编译”为JavaScript才能运行,因此必须选择一个“编译器”。其实将FLow解析为JavaScript的过程称为编译其实并不准确,因为这个“编译”过程仅仅就是把Flow提供的注解语法去除。Flow目前支持两种去除注解的方法——babelflow-remove-types

flow-remove-types是一个轻量级的专门用于去除Flow注解的命令行工具,而babel大家应该个更加熟悉,它是现在最流行的es6编译工具,而且可以很容易的和webpack等前端自动化工具集成,因此笔者仅介绍基于babel的版本。

不过需要注意的是,无论是babel还是flow-remove-types,他们仅是去除Flow的注解,但是并没有进行Flow的静态类型检测。事实上babel已经提供了一个Flow预设插件组(babel-preset-flow),它的会自动集成一个叫transform-flow-strip-types的babel插件来去除Flow注解,但是这个插件并不进行flow check。如何想让babel进行Flow的静态类型校验,这就需要手动集成另外一个插件——babel-plugin-typecheck

接下来进行具体的babel集成步骤,首先安装babel、babel-preset-flow、babel-plugin-typecheck,而babel这种工具我们可以安装在全局:

npm install -g babel-cli
npm install --save-dev babel-preset-flow babel-plugin-typecheck

然后创建babel的配置文件,在根目录下创建.babelrc,然后选择Flow的预设插件组(flow),并手动加上babel-plugin-typecheck插件:

{
  "presets": ["flow"],
  "plugins": ["typecheck"]
}

接着我们就可以使用babel编译我们的Flow代码了,例如我们用Flow开发的源代码在src目录,最终输出在dist目录,只需:

babel src/ -d dist/ --watch

这样我就仅需要babel就完成了校验和编译两项工作,从而不需要Flow的初始化和后台进程,而且使用babel的“--watch”功能解决了之前Flow命令行不能同时监听、提示的缺憾。

貌似大功告成,但是其实还有个问题,分别使用flow-binbabel编译如下代码:

function foo(x){
    return x * 12;     //Flow根据代码,推断foo的x1参数是string类型,string类型不能执行“*”操作,所有这里Flow会报错
}
foo("Hello, world!")

flow-bin会报错,错误类型如注释。而babel则顺利编译。笔者推测为babel-plugin-typecheck仅会对添加了类型注解的变量做类型检验,而不会对未加注解的变量先做推断类型再检验。

除了集成到babel外,我们还希望能够将我们的源代码的编译过程集成到webpack中,实现校验、打包、编译、丑化等工作,以便能够达到一个完整的构建工作流。集成到webpack的过程非常简单,其实就是babel集成到webpack的过程。

先安装webpack,建议将webpack安装到全局,然后安装babel和Flow的loader。

npm install -g webpack

并配置好webpack的配置文件webpack.config.js。因为webpack有模块化打包的功能,所有只要给出指定的入口文件,就能将所有依赖的文件打包成一个文件。例如src下的index.js是我们的入口文件,则:

var path = require('path');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname ,'/dist/'),
        filename: '[name].js',
    },
    module: {
        loaders: [{
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader'
        }]
    }
}

最后只要运行webpack即可。

以上集成仅是个简单例子,我们还需使用webpack做热替换(HMR)交换,并且能够自动刷新页面页面刷新,甚至是和react和vue集成,这些对于熟悉react和vue的朋友都说小case,这里不再具体深入。

如果你不想手动修改babel的配置,可以使用flow-babel-webpack-plugin这个插件,简化babel对flow的配置。

之前的命令行搭配已经很合理了,但是开发时候我们还是更喜欢IDE帮我们提示语法上的错误,事实上Flow可以和几乎所有主流IDE集成,这里仅以webstorm和vscode为例。

和webstorm集成

Flow提供了自己的注解,这算是扩充了es的语法,直接用es的语法检查肯定会报错,首先让我们的webstorm不要对Flow的注解保错,打开file->settings->Language & Framework->JavaScript界面。将JavaScript Version选为Flow,这样webstorm就不会再对Flow注解报错了。但是仅仅这样还能是不提示Flow的校验结果,还需要选择Flow executable,将Flow的cli的路径配置到这里。windows一般是C:\Users\用户名\AppData\Roaming\npm\flow.cmd,mac一般是usr/local/bin/flow。这样Webstorm就能报出Flow的校验错误了。

和vscode集成

直接安装vscode-flow-ide或者Flow-Language-Support插件即可。

以上就是这一期的全部内容,下一期会着重介绍Flow的注解使用。