醉临武-临武县第一中学官网

如何用Flow来提升前端的健壮性

看一段常见代码:

//例1 function foo(x) {   return x + 10 } foo('Hello!') //例2 function main(params){    //fn1函数获取了一个数据    var object = fn1(params)    //fn2根据获数据,产生一个结果    var result = fn2(object)    return result }

例2很明显,这个过程非常的‘黑’,如果你想知道object包含什么数据的话,可以:

  1. 打印一下 console.log(object)

  2. 查看fn1的注释,并且保佑它的注释是正确,全面的

  3. 或结合1,2,然后仔细查看fn1的源码,希望它不是很复杂

被上述步骤折磨完之后,终于能真正的写点代码了,但是依旧得非常小心,因为这里还有另一个函数:fn2

在修改代码的时候,得保证result这个结果没有被影响,那么如何保证呢?

很简单,重复上面的步骤,搞清楚result包含的数据,在测试的时候确保其数据跟原先的相同。 ...

动态类型一时爽,代码重构火葬场 知乎传送门:为什么说“动态类型一时爽,代码重构火葬场”

是时候彻底优化这个烦人的问题了

引入类型系统

其实问题的根源就是因为javascript太灵活了,在代码运行期间几乎可以做任何的修改,

没有东西可以在代码层面保证 某个变量,某个函数 跟预期的一致

所以要加入类型系统来确保代码的可靠性,在后期维护的时候同样能够传达出有效的信息

Flow & TypeScript

Flow是个JavaScript的静态类型检查工具,由Facebook出品的开源码项目,问世只有两三年,是个相当年轻的项目。简单来说,它是对比TypeScript语言的解决方式。

会有这类解决方案,起因是JavaScript是一种弱(动态)数据类型的语言,弱(动态)数据类型代表在代码中,变量或常量会自动依照赋值变更数据类型,而且类型种类也很少,这是直译式脚本语言的常见特性,但有可能是优点也是很大的缺点。优点是容易学习与使用,缺点是像开发者经常会因为赋值或传值的类型错误,造成不如预期的结果。有些时候在使用框架或函数库时,如果没有仔细看文件,亦或是文件写得不清不楚,也容易造成误用的情况。

这个缺点在应用规模化时,会显得更加严重。我们在团队开发协同时,一般都是通过统一的代码规范,来降低这个问题的发生,但JS语言本身无法有效阻止这些问题TypeScript这样的强(静态)类型的JavaScript超集语言就开始流行,用严格的角度,以JavaScript语言为基底,来重新打造另一套具有强(静态)类型特性的语言,就如同Java或C#这些语言一样,这也是为什么TypeScript称自己是企业级的开发JavaScript解决方案。

TypeScript存在的问题

TypeScript自然有它的市场,但它有一些明显的问题:

  • 首先是JavaScript开发者需要再进一步学习,内容不少

  • 有一定陡峭的学习曲线

  • 已经在使用的应用代码,需要整个改用TypeScript代码语法,才能发挥完整的功用。这对很多已经有内部代码库的大型应用开发团队而言,将会是个重大的决定,因为如果不往全面重构的路走,将无法发挥强(静态)类型语言的最大效用eg:angular2

所以许多现行的开源码函数库或框架,并不会直接使用TypeScript作为代码的语言,另一方面因为TypeScript并非是普及到一定程度的语言。 当然TypeScript也是个活跃的开源码项目,发展到现在也有一段时间,它的背后有微软公司的支持,全新打造过的Angular2框架中(由Google主导),也采用了TypeScript作为基础的开发语言

Flow你的新选择

现在,Flow提供了另一个新的选项,它是一种强(静态)类型的辅助检查工具Flow的功能是让现有的JavaScript语法可以事先作类型的声明(定义),在开发过程中进行自动检查,当然在最后编译时,一样可以用babel工具来移除这些标记

相较于TypeScript是另外重新制定一套语言,最后再经过编译为JavaScript代码来运行。Flow走的则是非强制与非侵入性的路线。

Flow的优点

  • 且易学易用 它的学习曲线没有TypeScript来得高,虽然内容也很多,但半天学个大概,就可以渐进式地开始使用

  • Flow从头到尾只是个检查工具 不是新的程序语言或超集语言,所以它可以与各种现有的JavaScript代码兼容,如果你哪天不想用了,就去除掉标记就是回到原来的代码,没什么负担

so

选择flow.js工具而不选择TypeScript强类型语言的原因显而易见? flow.js对工程的侵入性很小,无需大量的额外工作就能使用起来

从一个小例子演示

这种类型不符的情况在代码中非常容易发生,例如上面的例1:

function foo(x) {   return x + 10 } foo('Hello!')

x这个传参,我们在函数声明时希望它是个数字类型,但最后使用调用函数时则用了字符串类型。最后的结果会是什么吗? "Hello!10",这是因为加号(+)在JavaScript语言中,除了作为数字的加运算外,也可以当作字符串的连接运算。想当然这并不是我们想要的结果。

聪明如你应该会想要用类型来当传参的识别名,容易一眼看出传参要的是什么类型,像下面这样:

function foo(number) {   return number + 10 }
  • 如果在复合类型的情况,例如这个传参的类型可以是数字类型也可以是布尔类型,你又要如何写得清楚?

  • 如果是个复杂的对象类型时,结构又该如何先确定好?

  • 另外还有函数的返回类型又该如何来写?

利用Flow类型的定义方式,来解决这个小案例的问题,可以改写为像下面的代码:

// @flow function foo(x: number): number {   return x + 10 } foo('hi')

当使用非数字类型的值作为传入值时,就会出现由Flow工具发出的警告消息,像下面这样:

[flow] Cannot call foo with 'hi' bound to x because string 1 is incompatible with number 2. (a.getting-start.js:6:5)

如果是要允许多种类型也是很容易可以加标记的,假使这个函数可以使用布尔与数字类型,但返回可以是数字或字符串,就像下面这样修改过:

// @flow function foo(x: number | boolean): number | string {   if (typeof x === 'number') {     return x + 10   }   return 'x is boolean' } foo(1) foo(true) foo(null)  // 这一行有类型错误消息

在多人协同开发某个有规模的JavaScript应用时,这种类型的输出输入问题就会很常遇见。如果利用Flow工具的检查,可以避免掉许多不必要的类型问题

真实案例

可能你会认为Flow工具只能运用在小型代码中,其实不然,Vue源码中大量使用flowjs中类型检测:

此处输入图片的描述


Flow使用


此处输入图片的描述


  1. flow init

  2. // @flow  或   /* @flow */

  3. IDE插件 或 flow check 在Visual Studio Code中因为它内建TypeScript与JavaScript的检查功能,如果要使用Flow工具来作类型检查,需要在用户设置中,加上下面这行设置值以免冲突:

"javascript.validate.enable": false

4 . babel插件在编译时就会一并转换Flow标记

{   "plugins": [     "transform-flow-strip-types"   ] }

Flow支持的数据类型

Flow支持原始数据类型,如下面的列表:

  • boolean

  • number

  • string

  • null

  • void

类型别名&常见语法

// @flow export type Test = {   titleOne?: string,   titleTwo: ?string } var a: Test = {titleOne:"3",titleTwo:4} var b:string = "" //any export type NavigationGestureDirection = 'horizontal' | 'vertical'; type T = Array<string> var x: T = [] x["Hi"] = 2 //有Flow警告 type TT = Array<Test> var xx:TT = [] xx = [{titleOne: '1',   titleTwo: false}] type MyObject = {   foo: number,   bar: boolean,   baz: string, }; let val:MyObject = {foo:2,bar:false,baz:'444'}; var val1: MyObject = {foo:2,bar:false,baz:null}; var val2: MyObject = {foo:2,bar:false}; function method(val: MyObject):MyObject { return {foo:2,bar:false,baz:'2'}}  class Foo { constructor(val: MyObject) { /* ... */ } }

React中的应用

如果你在React class里面使用了React.PropTypes规范,你可以对JSX上的attributes做静态类型检查:

var Hello = React.createClass ({   propTypes: {     name: React.PropTypes.string.isRequired   }   ... }); //<Hello/> //Flow就会发现 缺少属性的错误 //<Hello name={42}/>//属性类型的错误
import * as React from 'react'; type Props = {   foo: number,   bar?: string, }; function MyComponent(props: Props) {   props.doesNotExist; // Error! You did not define a `doesNotExist` prop.   return <div>{props.bar}</div>; } <MyComponent foo={42} />