在Vue 中使用Typescript的示例代码


Posted in Javascript onSeptember 10, 2018

Vue 中使用 typescript

什么是typescript

typescript 为 javaScript的超集,这意味着它支持所有都JavaScript都语法。它很像JavaScript都强类型版本,除此之外,它还有一些扩展的语法,如interface/module等。

typescript 在编译期会去掉类型和特有语法,生成纯粹的JavaScript。

Typescript 5年内的热度随时间变化的趋势,整体呈现一个上升的趋势。也说明ts越来越️受大家的关注了。

在Vue 中使用Typescript的示例代码 

安装typescript

npm install -g typescript
tsc greeter.ts

举个栗子

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

greeter.ts

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

为什么需要使用它?

优势:

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

1. 静态类型检查

静态类型检查首要优点就是能尽早的发现逻辑错误,而不是上线之后才发现。

1.1 类型分析

传参过程字段错误,或类型错误使用。(进行参数标注后,在编码过程中即可检查出错误。)

1.2 类型推断:函数的返回值可通过ts类型推断得出.这一步骤是 在编译时进行

在编译时进行类型分析

example:

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

eg2: 个人使用后的效果

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);

在Vue 中使用Typescript的示例代码

2.智能补全

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

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类型的对象才不会报错。(智能补全和参数校验)

在Vue 中使用Typescript的示例代码 

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 会使用默认的配置. 扫描二维码获取配置项目。

在Vue 中使用Typescript的示例代码 

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

在Vue 中使用Typescript的示例代码 

在Vue里面写TS的方式

图中内容应写在script中的lang="ts" 。

上图:使用 Vue.extend的基础用法。

在Vue 中使用Typescript的示例代码

下图:基于类的Vue组件

在Vue 中使用Typescript的示例代码

通过对比我们发现,上面的编码方式更接近我们平时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,不过再怎么装饰他还是一个人,它本质是没有被改变的。所以,装饰器没有改变其继承关系,但也同样能够为它添加很多厉害的技能。简单的说,装饰器是在不修改一个类的继承关系的前提下,为一个类修改或添加成员。

装饰器主要接收的三个参数:

target 要在其上定义属性的对象。

key 要定义或修改的属性的名称。

descriptor 将被定义或修改的属性描述符。

下面我们通过代码中我们为一个人添加了飞行的功能:

在Vue 中使用Typescript的示例代码 

typescript VS JavaScript

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

在Vue 中使用Typescript的示例代码 

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 中使用Typescript的示例代码

这样的写法,让我产生了两个疑惑。

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的接口,它经常用于定义复杂的参数类型和返回值类型。

在Vue 中使用Typescript的示例代码

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

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

在Vue 中使用Typescript的示例代码

上面的代码: 定义了一个键为string,他的值为number或string类型的接口,并且该接口的所有字段都是可选的,你甚至可以传入一个空对象。所以我们可以使用上面的接口可以代替下面的接口,但是反之不行.

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

3. 编写第三方依赖

在日常的开发过程中,我们经常会遇到将第三方依赖引入到 TS 项目中没有类型检查的问题,这往往是因为这些项目没有提供类型定义文件。这个时候,我们可以手动为这些第三方库编写类型定义文件,类型定义文件在编译后会被完全忽略,所以并不会对现有代码产生影响。以上面这个较为复杂的函数为例,它的作用是将传入的所有的参数的所有字段合并到一个新的对象中并返回,尽管他的功能比较简单,但是为它编写类型定义还是需要一些 TS 的技巧。

在Vue 中使用Typescript的示例代码

1. 外部模块声明: 首先我们需要创建一个拓展名为 .d.ts 的文件,并在其中声明一个模块,声明的模块名称需要跟我们在其他文件中引入的路径相同。

在Vue 中使用Typescript的示例代码

2. 类型参数——泛型:首先让我们考虑最简单的情况,当传入一个参数的时候,extend 函数应该返回与参数类型相同的对象,但是我们在编写函数的时候并不知道用户会传入何种类型的参数,所以我们可以定义一个类型参数 T1,这时,extend 就被称为泛型函数,T1 也被称做泛型参数。在上面的例子中,extend 函数接受一个类型为 T1 参数并返回一个类型为 T1 的值,T1 需要用户手动传入,还好 TS 足够聪明,在绝大多数情况下,TS 可以根据参数类型来自动推断类型参数,免去了我们输入类型参数的繁琐步骤。只接受一个参数的 extend 函数并没有很复杂,我们可以继续考虑一些更复杂的情况。

在Vue 中使用Typescript的示例代码

这次让我们定义一个接受三个参数的 extend 函数,同时它也接受三个泛型参数,而它返回值类型呢,则是 T1, T2, T3 的交叉类型。交叉类型让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性,相当于对这些类型的成员求并集。 例如, T1 & T2 & T3 这个类型的对象同时拥有了这三种类型的成员。在这里,交叉类型就满足了我们对 extend 函数返回值类型的要求。值得注意的是,实际的 extend 函数可以接受不定个数的参数,也就是说,我们为它编写的类型定义也需要同时兼容接受不定个数参数的情况,这就需要 TS 提供的函数重载功能。

在Vue 中使用Typescript的示例代码

3. 重载:在大多数静态类型编程语言中,编译器允许存在参数类型、个数不同的多个同名函数,这个特性被称为函数重载。TS 支持函数重载的特性,所以我们可以定义多个接受不同数量参数的 extend 方法,在用户调用时,TS 会自动的在这些同名函数中选择正确的重载定义。有了函数重载的帮助,我们可以在使用 extend 的大多数场景下享受到类型检查的好处,只有在参数个数超过4个的时候,TS 才无法推断出返回值类型。需要注意的是在 JS 中,运行时并不提供函数重载的能力,我们无法定义多个同名函数,即使他们接受的参数数量并不相同,为了实现函数重载的效果,开发人员需要手动在单个函数中对参数的类型、数量做出判断。

在Vue 中使用Typescript的示例代码

到这里我们的第三方声明就完成了,即使一个简单函数的第三方声明,我们也运用了很多ts的相关知识。

个人感受

前面我们讲解了,使用在vue中使用ts能带给我们的种种便利,现在就我个人感受而言,说一下美中不足的地方。

1.即使使用了ts,template 部分仍没有静态类型检查和IDE智能提示,但官方成员表示在以后的 Vue 单文件中会提供这项功能。

2.将  Vue 单文件组件引入 TS  文件中,无法正确的提示其文件位置。

3.Vue  周边工具,比如  Vuex,它对ts的支持薄弱,大量的功能难以直接迁移到ts中,并且没有好的官方支持的方案。

4.毫无疑问,使用 TS 进行开发,相比于 JS ,我们需要花费更多的时间和精力。

Javascript 相关文章推荐
JavaScript 实现模态对话框 源代码大全
May 02 Javascript
jQuery EasyUI API 中文文档 - Documentation 文档
Sep 29 Javascript
jquery 延迟执行实例介绍
Aug 20 Javascript
Extjs4中的分页应用结合前后台
Dec 13 Javascript
理解JS绑定事件
Jan 19 Javascript
JS中用三种方式实现导航菜单中的二级下拉菜单
Oct 31 Javascript
Vue页面骨架屏的实现方法
May 22 Javascript
JS操作json对象key、value的常用方法分析
Oct 29 Javascript
vue prop属性传值与传引用示例
Nov 13 Javascript
vue使用video插件vue-video-player详解
Oct 23 Javascript
WebPack工具运行原理及入门教程
Dec 02 Javascript
vue3中轻松实现switch功能组件的全过程
Jan 07 Vue.js
ES6使用export和import实现模块化的方法
Sep 10 #Javascript
Vuejs 实现简易 todoList 功能 与 组件实例代码
Sep 10 #Javascript
浅谈微信小程序flex布局基础
Sep 10 #Javascript
微信小程序文章详情页面实现代码
Sep 10 #Javascript
Bootbox将后台JSON数据填充Form表单的实例代码
Sep 10 #Javascript
JavaScript读写二进制数据的方法详解
Sep 09 #Javascript
详解在vue-cli中使用graphql即vue-apollo的用法
Sep 08 #Javascript
You might like
PHP 的几个配置文件函数
2006/12/21 PHP
php 表单数据的获取代码
2009/03/10 PHP
用穿越火线快速入门php面向对象
2012/02/22 PHP
php Xdebug的安装与使用详解
2013/06/20 PHP
PHP微信开发用Cache 解决数据缓存
2016/07/11 PHP
ThinkPHP框架实现的邮箱激活功能示例
2018/06/15 PHP
强悍无比的WEB开发好助手FireBug(Firefox Plugin)
2007/01/16 Javascript
Javascript 面向对象 继承
2010/05/13 Javascript
JQuery Dialog的内存泄露问题解决方法
2010/06/18 Javascript
IE无法设置短域名下Cookie
2010/09/23 Javascript
jQuery的$.proxy()应用示例介绍
2014/04/03 Javascript
Javascript中this的用法详解
2014/09/22 Javascript
移动端JQ插件hammer使用详解
2015/07/03 Javascript
浅析jQuery事件之on()方法绑定多个选择器,多个事件
2016/04/27 Javascript
JavaScript 链式结构序列化详解
2016/09/30 Javascript
jQuery实现限制文本框的输入长度
2017/01/11 Javascript
vue-music 使用better-scroll遇到轮播图不能自动轮播问题
2018/12/03 Javascript
angular6 填坑之sdk的方法
2018/12/27 Javascript
[02:52]2017DOTA2国际邀请赛中国区预选赛晋级之路
2017/07/03 DOTA
Python实现字典依据value排序
2016/02/24 Python
python 简单的多线程链接实现代码
2016/08/28 Python
python网络爬虫之如何伪装逃过反爬虫程序的方法
2017/11/23 Python
Python装饰器(decorator)定义与用法详解
2018/02/09 Python
python和flask中返回JSON数据的方法
2018/03/26 Python
python批量解压zip文件的方法
2019/08/20 Python
python实现高斯投影正反算方式
2020/01/17 Python
Python3 pickle对象串行化代码实例解析
2020/03/23 Python
CSS3 transition 实现通知消息轮播条
2020/10/14 HTML / CSS
"引用"与指针的区别是什么
2016/09/07 面试题
英语老师推荐信
2014/02/26 职场文书
工厂采购员岗位职责
2014/04/08 职场文书
中等生评语大全
2014/05/04 职场文书
大三学年自我鉴定范文(3篇)
2014/09/28 职场文书
2015年国庆节慰问信
2015/03/23 职场文书
SQL实战演练之网上商城数据库商品类别数据操作
2021/10/24 MySQL
Java并发编程之原子性-Atomic的使用
2022/03/16 Java/Android