目前社区有很多 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 等强类型语言相近的体验。
项目越复杂就越需要通过供具的手段来保证项目的维护性和增强代码的可读性。Vue.js 在做 2.0 重构的时候,在 ES2015 的基础上,除了ESLint 保证代码风格之外,也引用了 Flow 做静态类型检查。之所以选择 Flow,主要是因为 Babel 和 ESLint 都有对应的 Flow 插件以支持语法,可以完全沿用现有的构建配置,非常小成本的改动就可以拥有静态类型检查的能力。
Flow工作方式
通常类型检查分成 2 种方式:
类型判断
它不需要任何代码修改即可进行类型检查,最小化开发者的工作量。它不会强制你改变开发习惯,因为它会自动推断出变量的类型。这就是所谓的类型推断,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
类和对象
/**
*@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 的实践过程中,主要的步骤包括:
Flow的配置过程
假设目前有一个从vue-cli命令行生成的项目:vue init webpack-simple Flow-vue-demo。关于Babel,Eslint(可选)和Flow,需要安装所需的 npm packages,参考列表如下:
Babel:
Eslint: (可选)
Flow:
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 单文件组件的结构,项目结构略显臃肿。(每个组件都会有至少两个文件)。
方法比较:
总结
Flow是一个由Facebook出品的JavaScript静态类型检查工具,它与Typescript不同的是,它可以部分引入,不需要完全重构整个项目,所以对于一个已有一定规模的项目来说,迁移成本更小,也更加可行。除此之外,Flow可以提供实时增量的反馈,通过运行Flow server不需要在每次更改项目的时候完全从头运行类型检查,提高运行效率。Flow和Typescript都是给Javascript增加类型检查的优秀解决方案,两者的简单对比如下:
对于需要使用 Flow 进行类型检查的 js 文件,在开头加入 @Flow 注释,即可引入Flow。通过demo可以看到,Flow可以帮助找出由于不合理的类型操作引起的错误,包括运算符操作,函数参数类型和返回值类型等。Flow也支持自定义类型声明,泛型声明等类型语言相关的操作。
最后
今天的 Vue2总结(六)Flow静态类型检查 就分享到这里,我的公众号没有留言功能哈,有问题大家心里默念,我能感受到,谢谢 ~
原文始发于微信公众号(程序员思语):