Vue2总结(六)Flow静态类型检查

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Vue2总结(六)Flow静态类型检查

目前社区有很多 Vue.js 的源码解读文章,但是质量层次不齐,不够系统和全面,最近我将从各个细枝末节解读 Vue.js 的实现原理,针对 vue3 版本之前,让同学们可以彻底掌握 Vue.js。 友情提示:阅读本文大概需要** 23****分钟**

前言

当大家翻阅 vue 源码的时候可以看到


const vm: Component = this

这样一行。出自 src/core/instance/init.js 。const 是 javascript ES6 中常见的常量定义方法。但是我没有看到过这种带:的语法结构。其实这是flow在起作用。VUE是用flow进行静态类型检测。

Flow

Flow 是 facebook 出品的 JavaScript 静态类型检查工具。Vue.js 的源码利用了 Flow 做了静态类型检查,所以了解 Flow 有助于我们阅读源码。

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

为何用Flow

正如上面所言 JavaScript 是动态类型语言,它的灵活性有目共睹,但是过于灵活的副作用是很容易就写出非常隐蔽的隐患代码,在编译期甚至看上去都不会报错,但在运行阶段就可能出现各种奇怪的 bug。类型检查是当前动态类型语言的发展趋势,所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,而不影响代码运行(不需要运行时动态检查类型),使编写 JavaScript 具有和编写 Java 等强类型语言相近的体验。

Vue2总结(六)Flow静态类型检查

项目越复杂就越需要通过供具的手段来保证项目的维护性和增强代码的可读性。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: Arraynumber = [1, 2, 3]
    arr.push('Hello')
    

    数组类型注释的格式是 Array, T 表示数组中每项的数据类型。在上述代码中,arr 是每项均为数字的数组。如果我们给这个数组添加了一个字符串,Flow 能检查出错误。

    类和对象
    
    /**
     *@flow
     */
    
    class Bar {
      x: string;
      y: string | number;
      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: string, c: Arraystring, d: Bar} = {
      a: 'Hello',
      b: 11,
      c: ['Hello', 'VueJs'],
      d: new Bar('Hello', 3)
    }
    

    类的类型注释格式如上,可以对类自身的属性做类型检查,也可以对构造函数的参数做类型检查。这里需要注意的是,属性 y 的类型中间用 | 做间隔,表示 y 的类型即可以是字符串也可以是数字。对象的注释类型类似于类,需要指定对象属性的类型.

    Null

    若想任意类型 T 可以为 null 或者 undefined ,只需类似如下写成 ?T 的格式即可。

    
    // @flow
    var foo: ?string = null
    

    此时, foo 可以为字符串,也可以为 null 。

    Flow在Vue中的应用

    有时候我们想引用第三方库,或者定义一些类型,但 Flow 并不认识,因此检查的时候会报错。为了解决这类问题,Flow 提出了一个 libdef 的概念,可以用来识别这些第三方库或者是自定义类型,而 Vue.js 也利用了这一特性。

    在 Vue.js 的主目录下有 .flowconfig 文件, 它是 Flow 的配置文件。这其中的 [libs] 部分应用来描述包含指定库定义的目录,默认是名为 flow-typed 的目录。这里 [libs] 配置的是 flow ,表示指定的库定义都在 flow 文件夹内。我们打开这个目录,会发现文件如下:

    
    // flow 文件夹
    flow
    ├── compiler.js # 编译相关
    ├── component.js # 组件数据结构
    ├── global-api.js # Global API 结构
    ├── modules.js # 第三⽅库定义
    ├── options.js # 选项相关
    ├── ssr.js # 服务端渲染相关
    ├── vnode.js # 虚拟 node 相关
    

    可以看到,Vue.js 有很多自定义类型的定义,在阅读源码的时候,如果遇到某个类型并想了解它完整的数据结构的时候,可以回来翻阅这些数据结构的定义。

    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的配置过程

    假设目前有一个从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
  • Webstorm自带了Flow的支持,需要开启,结合eslint配置Flow相关的rules,在编辑时通过eslint即可自动报错。安装Flow,运行Flow init && Flow check。配置.vue文件为Flow的检查范围。

    使用 Flow-typed 处理第三方的 npm packages 的类型声明。必要的话增加自定义的类型声明文件,如自定义的对象等,具体可以参考Flow文档。

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

    在前面已经说明了纯JS文件里面怎么用Flow,那么在一个vue组件文件中应该如何配置呢?有下面几种方法。

    方法一:

    直接在 script标签中,像纯js文件处理一样添加Flow注释,发现可以正常编译运行,但是运行Flow check是无效的。

    方法二:

    注释掉 template , style script标签,由于Vue的编译器即使注释了也会识别其中的 template, style script 标签,而Flow 检查会忽略注释,因此对于Flow来说可以当做一个 javascript文件进行处理。对于这样处理的vue文件,Flow命令能够报出关于一般的函数声明的类型检查错误,但是对于绑定到Vue实例(this)上的方法是无效的。因此Flow类型检查不是100%覆盖。这种方法的主要问题在于代码和注释混用不便于阅读,目前Flow社区有一个open issue就是关于这个问题的,即不能自动检测中文件中的 script标签。

    方法三:

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

    方法比较:

    Vue2总结(六)Flow静态类型检查

    总结

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

    Vue2总结(六)Flow静态类型检查

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

    最后

    今天的 Vue2总结(六)Flow静态类型检查 就分享到这里,我的公众号没有留言功能哈,有问题大家心里默念,我能感受到,谢谢 ~ 

    原文始发于微信公众号(程序员思语):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Vue2总结(六)Flow静态类型检查


     上一篇
    Vue2总结(七)Vue事件机制 Vue2总结(七)Vue事件机制
    目前社区有很多 Vue.js 的源码解读文章,但是质量层次不齐,不够系统和全面,最近我将从各个细枝末节解读 Vue.js 的实现原理,针对 vue3 版本之前,让同学们可以彻底掌握 Vue.js。 友情提示:阅读本文大概需要** 30*
    2021-04-05
    下一篇 
    Vue2总结(五)单元测试 Vue2总结(五)单元测试
    趁着Vue3还没发布,先把vue2的各个知识点、源码、轮子全部温习一遍,今天的主题是Vue项目的单元测试重要性及其使用。 友情提示:阅读本文大概需要** 30****分钟** 前言当你的项目足够大的时候,在叠加模块和组件的过程中,是
    2021-04-05