Vue 中使用 typescript

Vue 中使用 typescript

什么是typescript

typescript 为 javaScript的超集,这意味着它支持全部都JavaScript都语法。它很像JavaScript都强类型版本,除此以外,它还有一些扩展的语法,如interface/module等。
typescript 在编译期会去掉类型和特有语法,生成纯粹的JavaScript。javascript

Typescript 5年内的热度随时间变化的趋势,总体呈现一个上升的趋势。也说明ts愈来愈️受你们的关注了。
google 趋势vue

安装typescript

npm install -g typescript
tsc greeter.ts

举个栗子

左右对比能够看出typescript 在编译期会去掉类型和特有语法,生成纯粹的JavaScript。
greeter.tsjava

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };

greeter.jswebpack

function greeter(person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}
var user = { firstName: "Jane", lastName: "User" };

为何须要使用它?

优点:web

  1. 静态类型检查
  2. IDE 智能提示
  3. 代码重构
  4. 可读性

1. 静态类型检查

静态类型检查首要优势就是能尽早的发现逻辑错误,而不是上线以后才发现。
1.1 类型分析
传参过程字段错误,或类型错误使用。(进行参数标注后,在编码过程当中便可检查出错误。)
1.2 类型推断:函数的返回值可经过ts类型推断得出.这一步骤是 在编译时进行
在编译时进行类型分析vue-router

example:
eg1: 我在使用ts写vue-router 的 动态路径参数时就发现了一个问题, 动态路径参数 以冒号开头 path: '/user/:id',咱们会误认为id为一个number,若是使用ts你将获得提示 咱们应该传入一个string类型的id. 传入一个number类型的id可能并不会出错,js会对它进行隐式类型转换,可是传入一个string会使它更安全和规范.typescript

eg2: 我的使用后的效果shell

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person): string {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: 1223, lastname: "User" };

greeter(user);

greeter error

2.智能补全

在编写代码时ide就会提示函数签名.npm

interface Person {
    firstName: string;
    lastName: string;
}
/**
 * 问候语句
 * @param {Person} person
 * @returns {string}
 */
function greeter(person: Person): string {
    return "Hello, " + person.firstName + " " + person.lastName;
}

/**
 * hello word!
 *
 * @param {string} word
 * @returns {string}
 */
function Hello(word: string): string {
    return "hello," + word;
}

export { greeter, Hello };

直接将这个ts文件引入到其余ts文件中,不只补全了全部的参数类型,还告诉你须要填入一个参数,而且你只有填入一个Person类型的对象才不会报错。(智能补全和参数校验)
greeter 函数编程

3.在重构上

动态一时爽,重构火葬场.
typescript 在重构上的优点,咱们主要从三方面说明。

  1. 重命名符号,可将一切引用的地方都进行修改。
    在vs code 中若是咱们想修改函数、变量或者类的名称,咱们可使用重命名符号的功能,在当前项目中正确的修改全部的引用.这个既能够在ts中使用,也能够在js中使用,而它的底层实现都是依靠ts 的语法分析器实现的。
  2. 自动更新引用路径(vs code)。
    在重构的过程当中,咱们可能须要移动文件的路径,这每每会致使其余地方的import失效,这时候vs code提供了自动更新引用路径的功能。它的底层实现也是依靠ts 的语法分析器实现的。
  3. 校验函数签名。
    有时候咱们会重构类或函数的签名,若是有引用到的地方忘记修改,除了运行时候能发现,其余时候每每难以察觉,且 ESLint 也只能是排查简单的问题,因此出了BUG会很是麻烦。 而 TypeScript 不同,在编码时就能及时的发现哪里错了,哪里应该改动但没有修改。

    [函数签名 MDN][5]

4. 可读性

可读性上,TypeScript 明显占优,查看开源代码时,若是注释不是很完善,每每会看的云里雾里,而 TypeScript 在同等条件下,至少有个类型,能让本身更容易明白代码的参数、返回值和意图。

TS+Vue初探

配置

在正式开发以前,咱们须要了解一些基本的配置。
1.tsconfig.json 是 ts 项目的编译选项配置文件. 在 ts 项目中若是你不添加这份文件,ts 会使用默认的配置. 扫描二维码获取配置项目。
tsconfig.json 配置

  1. ts-loader:Webpack 的TypeScript 加载器,就是为了让 webpack 编译 .ts .tsx文件。
  2. TSLint:.ts .tsx文件的代码风格检查工具。(做用相似于ESLint)
  3. vue-shim.d.ts:因为 TypeScript 默认并不支持 *.vue 后缀的文件,因此在 vue 项目中引入的时候须要建立一个 vue-shim.d.ts 文件,放在项目根目录下,例如 src/vue-shim.d.ts。
    vue-shim.ts

在Vue里面写TS的方式

图中内容应写在script中的lang="ts" 。
上图:使用 Vue.extend的基础用法。
vue.extend
下图:基于类的Vue组件
vue 组件
经过对比咱们发现,上面的编码方式更接近咱们平时JS的写法,可是咱们并不能感觉到ts的类型检查和类型推断。
下面张图的写法更有助于咱们获得ts的类型检查。这须要咱们引入 vue-class-component ,虽然template中仍是不能获得补全,可是script 中的内容获得了更好的补全。 下面咱们了解一下vue-class-component的做用。

vue-class-component & vue-property-decorator

vue-class-component 强化 Vue 组件,使用装饰器语法使 Vue 组件更好的跟TS结合使用。
vue-property-decorator在 vue-class-component 的基础上增长了更多与 Vue 相关的装饰器,使Vue组件更好的跟TS结合使用。

这二者都是离不开装饰器的,(decorator)装饰器已在ES提案中。Decorator是装饰器模式的实践。装饰器模式呢,它是继承关系的一个替代方案。动态地给对象添加额外的职责。在不改变接口的前提下,加强类的性能。下面咱们以 钢铁侠 为例讲解如何使用 ES7 的 decorator。

以钢铁侠为例,钢铁侠本质是一我的,只是“装饰”了不少武器方才变得那么 NB,不过再怎么装饰他仍是一我的,它本质是没有被改变的。因此,装饰器没有改变其继承关系,但也一样可以为它添加不少厉害的技能。简单的说,装饰器是在不修改一个类的继承关系的前提下,为一个类修改或添加成员。
superman
装饰器主要接收的三个参数:
target 要在其上定义属性的对象。
key 要定义或修改的属性的名称。
descriptor 将被定义或修改的属性描述符。
下面咱们经过代码中咱们为一我的添加了飞行的功能:
decoractor

typescript VS JavaScript

了解了上面的基础知识,如今我将同一段代码分别使用js 和 ts来书写,如今咱们来对比他们之间的差异。
js vs ts

  1. Props (Properties)
    使用js,咱们有不少中方式来定义组件的 Props,可是大多都掺杂了 Vue 的私有特征,与 ES 格格不入,例如左边的代码,明明咱们是把这个对象的 prop 属性定义成为了一个包含两个 string 元素的对象,可是咱们却能够直接经过这个对象来访问 "name" 字段,这很明显是不符合 ES 语义的。
    再来看看右边的 TS 选手,经过 Prop 装饰器把指定的字段标记为了 Prop,既保留了 ES 语法的语义,并且还能与 Vue 完美的配合,更棒的是,咱们能够在编码的过程当中享受 TS 对 Prop 字段的静态类型检查。
  2. Method 和 data
    再来看看 Method,JS 中定义 method 仍是有咱们上面提到的那个不符合 ES 语义的毛病。而在 TS 中,method 不须要额外的装饰器——实例方法就会自动成为 Vue 组件的 method。相似的还有 data ,使用 TS 的语法,实例字段便可自动成为 Vue 组件的 data。
  3. Computed
    在传统的使用 JS 编写的 Vue 代码中,若是要定义计算属性,咱们须要在 computed 属性中定义相应的函数。而这在 ES 中其实早就已经有了对应语义的语法——getter,因此在使用了 vue-class-component 的 vue 组件中,咱们能够直接使用 getter 来定义计算属性,不论是在语法上仍是在语义上,相比普通的 JS 都略胜一筹

总结:咱们使用vue-class-component让vue组件的定义更加符合ES语义,使得TS可以更好的进行语法分析,并基于此进行类型检查。

业务场景中使用TS + Vue

1. 在Vue项目中定义data和props

vue-property-decorator 这样的写法,让我产生了两个疑惑。
1.1 为何使用 的写法
@Prop(Number!:) propA!: number
而不是
@Prop(Number) propA: number
1.2 为何Prop须要先后写两次类型?

自我自答环节:
答1.1:由于咱们定一个Phone这个类,没有对phone、condition这些字段经过constructor进行初始化,因此须要在属性上使用 显式赋值断言来帮助类型系统识别类型,这样可以让属性会被间接地初始化。
答1.2:前面括号里面的类型标注是Vue提供的类型检查。冒号后面的是为TS提供的类型标注。

2. 编写一个函数

这里咱们将使用到js的接口,它常常用于定义复杂的参数类型和返回值类型。
interface
上面的例子,咱们须要为opts这个参数定义类型,方便后面编码时的使用,这时咱们就须要编写一个接口来对它进行类型定义。这个接口里面包括三个必须属性和一个可选属性。必须属性在对象初始化的时候必须赋值,可是有时候某个对象中的属性能够被忽略的,不必定会被须要。咱们能够将它设置为可选项。

随着业务的发展,一些参数的字段会变的愈来愈多,愈来愈复杂。可能你想有没有什么一劳永逸的方法,让接口更加简单,甚至让它自动的适应业务的变化。因而我想出了这样的代码:
type interface

上面的代码: 定义了一个键为string,他的值为number或string类型的接口,而且该接口的全部字段都是可选的,你甚至能够传入一个空对象。因此咱们可使用上面的接口能够代替下面的接口,可是反之不行.

然而咱们不该该去绕开这些检查,由于这样ts就不会为你检查使用接口的对象应该存在那些属性或者方法了。使用ts的意义就被大大减弱了。

3. 编写第三方依赖

在平常的开发过程当中,咱们常常会遇到将第三方依赖引入到 TS 项目中没有类型检查的问题,这每每是由于这些项目没有提供类型定义文件。这个时候,咱们能够手动为这些第三方库编写类型定义文件,类型定义文件在编译后会被彻底忽略,因此并不会对现有代码产生影响。以上面这个较为复杂的函数为例,它的做用是将传入的全部的参数的全部字段合并到一个新的对象中并返回,尽管他的功能比较简单,可是为它编写类型定义仍是须要一些 TS 的技巧。
assgin1. 外部模块声明: 首先咱们须要建立一个拓展名为 .d.ts 的文件,并在其中声明一个模块,声明的模块名称须要跟咱们在其余文件中引入的路径相同。
外部模块声明 2. 类型参数——泛型:首先让咱们考虑最简单的状况,当传入一个参数的时候,extend 函数应该返回与参数类型相同的对象,可是咱们在编写函数的时候并不知道用户会传入何种类型的参数,因此咱们能够定义一个类型参数 T1,这时,extend 就被称为泛型函数,T1 也被称作泛型参数。在上面的例子中,extend 函数接受一个类型为 T1 参数并返回一个类型为 T1 的值,T1 须要用户手动传入,还好 TS 足够聪明,在绝大多数状况下,TS 能够根据参数类型来自动推断类型参数,免去了咱们输入类型参数的繁琐步骤。只接受一个参数的 extend 函数并无很复杂,咱们能够继续考虑一些更复杂的状况。
繁星
此次让咱们定义一个接受三个参数的 extend 函数,同时它也接受三个泛型参数,而它返回值类型呢,则是 T1, T2, T3 的交叉类型。交叉类型让咱们能够把现有的多种类型叠加到一块儿成为一种类型,它包含了所需的全部类型的特性,至关于对这些类型的成员求并集。 例如, T1 & T2 & T3 这个类型的对象同时拥有了这三种类型的成员。在这里,交叉类型就知足了咱们对 extend 函数返回值类型的要求。值得注意的是,实际的 extend 函数能够接受不定个数的参数,也就是说,咱们为它编写的类型定义也须要同时兼容接受不定个数参数的状况,这就须要 TS 提供的函数重载功能。
泛型3. 重载:在大多数静态类型编程语言中,编译器容许存在参数类型、个数不一样的多个同名函数,这个特性被称为函数重载。TS 支持函数重载的特性,因此咱们能够定义多个接受不一样数量参数的 extend 方法,在用户调用时,TS 会自动的在这些同名函数中选择正确的重载定义。有了函数重载的帮助,咱们能够在使用 extend 的大多数场景下享受到类型检查的好处,只有在参数个数超过4个的时候,TS 才没法推断出返回值类型。须要注意的是在 JS 中,运行时并不提供函数重载的能力,咱们没法定义多个同名函数,即便他们接受的参数数量并不相同,为了实现函数重载的效果,开发人员须要手动在单个函数中对参数的类型、数量作出判断。
重载
到这里咱们的第三方声明就完成了,即便一个简单函数的第三方声明,咱们也运用了不少ts的相关知识。

我的感觉

前面咱们讲解了,使用在vue中使用ts能带给咱们的种种便利,如今就我我的感觉而言,说一下美中不足的地方。 1.即便使用了ts,template 部分仍没有静态类型检查和IDE智能提示,但官方成员表示在之后的 Vue 单文件中会提供这项功能。 2.将  Vue 单文件组件引入 TS  文件中,没法正确的提示其文件位置。 3.Vue  周边工具,好比  Vuex,它对ts的支持薄弱,大量的功能难以直接迁移到ts中,而且没有好的官方支持的方案。 4.毫无疑问,使用 TS 进行开发,相比于 JS ,咱们须要花费更多的时间和精力。

相关文章
相关标签/搜索