TypeScript推出已经很长时间了,在Angular项目中开发比较普遍,随着Vue 3.0的即将推出,TypeScriptVue项目中使用也即将成为很大的趋势,笔者也是最近才开始研究如何在Vue项目中使用TypeScript进行项目的开发。

准备

阅读博客前希望读者能够掌握如下技能,文章中也会相对应的讲解一些对于TypeScript基础进行讲解。本篇博客基于Vue cli 3.0实践,若读者使用的是Vue cli 2.0的话,需要对其webpack配置进行更改,本文不进行讲解,请读者自行百度,并进行更改。

  1. Vue cli 3.0环境
  2. Vue基础应用

读者完全不用担心不懂TypeScript基础,本文会一一对其基础进行简单的讲解。

TypeScript基础

创建完项目之后,接下来就可以对项目进行开发,在使用TypeScript开发与使用JavaScript还是有很大的区别,打开HelloWorld.vue文件内容如下:

<template>
<div></div>
</template> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; @Component
export default class HelloWorld extends Vue { }
</script> <style lang="scss"> </style>

读者若经常开发Vue项目会发现与之间的.vue有了一些变化,主要是在<script>标签部分。使用ES6开发项目时样文件格式如下:

<template>
<div></div>
</template> <script>
export default { }
</script> <style lang="scss" scoped></style>

对比一下两者之间还是有很大的区别的。原有写法是直接使用export default导出一个对象,然而TypeScript而是使用class如果对React的同学应该很熟悉,有点类似于React创建的组件的写法了。对于template的使用其实与之前的写法是一样的。唯一改变得就是对于<script>部分,如果想在项目中使用TypeScript需要在<script>添加标识:<script lang="ts">告知解析器需要使用TypeScript进行解析。

Vue中使用TypeScript编写项目,需要依赖于Vue提供的包vue-property-decorator以达到对TypeScript的支持。需要使用@修饰器其中Vue功能才能正常使用。无论在页面还是组件时,一定要使用@Component对导出类进行修饰,否则无法正常使用某些功能(如:双向绑定...)。接下来就来实现如何在模板中渲染数据,这里将不再使用data而是直接在class中写入变量。

关于vue-property-decorator的用法会在下面详细介绍。

<template>
<div>
<h1>{{message}}</h1>
</div>
</template> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; @Component
export default class HelloWorld extends Vue {
private message:string = "Hello,TypeScript";
}
</script>

仔细观察一下message的声明,与其之前使用data时已经完全不一样了有没有,除了没有使用data以外,还有很大的区别:

修饰符  变量名:数据类型 = 数据
private message:string = "Hello,TypeScript";
修饰符

如果没有接触过强类型语言的话对于修饰符可能会感觉到一丝丝的陌生,在es6class中已经有了修饰符的概念,在es6class中只有一个修饰符static修饰符,表示该类的静态方法,但是在TypeScript中添加了很多修饰符:

  • public:所有定义成public的属性和方法都可以在任何地方进行访问(默认值)
  • private:所有定义成private的属性和方法都只能在类定义内部进行访问
  • protected:多有定义成protected的属性和方法可以从类定义内部访问,也可以从子类中访问。
  • readonly:readonly关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。

修饰符不但可以修饰属性,还可以用来修饰方法,若在写入属性和方法时没有使用修饰符对其进行修饰的话默认则会是public

class A {
public message1:string = "Aaron";
private message2:string = "Angie";
protected message3:string = "Tom";
readonly message4:string = "Jerry";
message5:string = "Sony";
}
class B extends A {
constructor(){
// 这里因为受修饰器影响无法读取到`message2`
// 若写入 message2 在编码工具中则会显示红色波浪线提示错误
// Property 'message2' is private and only accessible within class 'A'.
let {message1,message3,message4,message5} = this;
console.log(message1,message3,message4,message5);
// 若去更改 message4 的数据则会抛出下面的错误
// Cannot assign to 'message4' because it is a read-only property.
// this.message4 = "Sun";
}
}
数据类型

在声明变量或属性时需要规定其对应的数据类型是什么,这样一来就可以对其变量以及属性进行更加严格的管理了。

TypeScript中提供了一下基本类型:

  • string: 字符串
  • number:数字
  • boolean:布尔值
  • array:数组
  • enum:枚举,个人理解枚举类型并不陌生,它能够给一系列数值集合提供友好的名称,也就是说枚举表示的是一个命名元素的集合
  • any:任意类型
  • void:没有任何类型,通常用于函数没有返回值时使用
//  定义number型变量
let test:number = 1;
// 定义number型数组
let arr:[]number = [];
let arr1:Array<number> = [];
// 定义对象数据,且对象只能有name字段
let a:{name:string}[] = [];

在进行变量或属性声明的时候,一旦使用了数据类型限定的话,如果试图想要对其数据类型进行更改的话,就会提示一个错误,如果类型是多种情况,可以使用|进行分割。若不确定使用哪种类型则可以使用any

let a:string = "";
// Type '1' is not assignable to type 'string'.
a = 1; let b:(string|number) = "";
b = 1;

注意:在声明变量时不一定要使用数据限定,如果没有使用数据限定,TypeScript则会根据默认值进行数据类型推论作为其变量的数据类型。

let a = 1;
// Type '"Aaron"' is not assignable to type 'number'.
a = "Aaron";
函数

对数据已经有了一定了解,之后就是对于函数的说明,在项目开发过程中唯一不可缺少的就是函数,同样的是在class中写入的方法,同样也需要使用修饰符,其用法与属性一致。

在函数后面添加了void标识符,标识当前函数没有函数返回值,可以根据当前函数的返回值,更改其返回类型。一旦规定了函数的返回值类型,就无法再返回其他的数据的类型,如果强行返回其他类型的话,则会抛出错误。

<template>
<div>
<h1>{{message}}</h1>
<el-button @click="clickMe"></el-button>
</div>
</template> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; @Component
export default class HelloWorld extends Vue {
private message:string = "Hello,TypeScript"; private clickMe():void{
alert("Aaron!")
// Type '"Angie"' is not assignable to type 'void'.
// return "";
}
}
</script>

有的时候函数会带有一些参数,该如何去处理这些参数呢?同样当在接收参数(形参)处同样也要规定其数据类型,一旦写入参数在调用该方法时就必须传入该参数,某些参数若不是必填的,则要在定义其类型前使用?,这样该参数就不是必填项,可以有可无,?与默认值不能共存只能使用一个,一旦使用默认值的话,改参数就是必定存在的了,不会出现不传入的情况了。

const fn = (name:string,age?:number) => {
console.log(`my name is ${name}, age ${age || 18}`)
}
fn("Aaron",3); // my name is Aaron, age 3
fn("Angie"); // my name is Angie, age 18
fn(); // Expected 1-2 arguments, but got 0.

开发过程中难免会遇到一些特殊的函数,函数内部无法确定其参数个数,但是传入的类型都是统一类型的,在JavaScript中提供了arguments属性,对于TypeScript有其他处理方式:

const fn = (...foo:number):number => {
let res:number = 0;
// 如果不使用foo,可以替换成arguments也是一样的
for(let i = 0;i<foo.length;i++){
res += foo[0];
}
return res;
}
fn(1,2,3,4,5,6,8,7,9) // 45

笔者也尝试使用过arguments,但是如果使用arguments时会抛出一个错误Cannot find name 'arguments'.

函数的重载,在没有接触过强语言的话可能是很陌生的,什么是重载?重载就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。(节选自百度百科)

function abs(name:string):string;
function abs(name:{name:string}):string;
function abs(name:string|{name:string}):string{
if(typeof name === "object"){
return name.name;
}
return name;
}
abs("Aaron");
abs({name:"Angie"});

上述中前两个都属于抽象函数,最后一个函数为对于抽象函数的实现,实现签名必须兼容所有的重载签名,总是在参数列表的最后,接受一个any类型或联合类型的参数作为他的参数。如果没有按照实现所传入参数则会抛出错误。

泛型

泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。

笔者在最开始接触泛型的时候也是一脸懵逼,因为一直都在听后端的同学说,泛型什么什么的...一直都只是知道有泛型这个东西,但是泛型应该怎么用,想问却又不敢...因为是不知道从何开始问起。

我对于泛型的理解就是去规定一种数据类型,无论是函数接收参数,还是返回结果,或者一种类型的数组,对象,等等等都可以使用泛型进行约束,泛型就是在定义方法不确定需要返回什么样类型的数值,但是当调用函数值时需要去限定返回该类型。

使用泛型时分为两种情况(只会提及TypeScript的泛型),一种是接口泛型,一种是类泛型其实两种方法是类似的。

//  类泛型
class User {
name:string = "";
age:number = 0;
}
// 接口泛型
interface User {
name:string,
age:number
} // 获取数据
function getData(){
// ... axios操作等
return [{}];
};
// T 泛型的形参
// T 可以自定义
function getUsers<T> ():T[]{
return getData();
}
let users:User[] = getUsers<User>();

上面代码中使用class和接口实现了两种泛型,两种都是可用的,一般不推荐使用去使用泛型,因为在使用类去做泛型的时候需要对其中的属性进行初始化,否则会抛出错误。

接口

上面提到了接口,对于接口也是前端没有涉及的一部分,接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。

对于接口来说,去定义一些抽象的方法或属性,在使用时对其进行实现,使用implements关键字对其接口进行实现,可以同时实现多个接口以,分割。

接口可以继承类,但是类不可以继承接口,只能实现接口,如果接口继承类的话,不会继承类的实现只会继承类的签名规范。

class Friend {
public friends:object[] = [];
getFriends(){
return this.friends;
}
}
interface U extends Friend {
name:string,
age:number,
seyHi(name:string):string
}
interface S {
job:string
}
class User implements U,S {
public friends:object[] = [];
constructor(
public name:string,
public age:number,
public job:string){}
seyHi(name:string):string{
return `Hi ${name} ~,my name is ${this.name}`;
}
getFriends(){
return this.friends;
}
}
new User("Aaron",3,"Code");

有关于TypeScript的类,与es6中的类是一样的,这里着重说一下抽象类。抽象类是对其属性方法进行规定,在继承时对其进行实现。

abstract class Animal{
public name:string;
constructor(name:string){
this.name=name;
}
// 抽象方法 ,不包含具体实现,要求子类中必须实现此方法
abstract eat():any;
// 非抽象方法,无需要求子类实现、重写
run(){
console.log('非抽象方法,不要子类实现、重写');
}
}
class Dog extends Animal{
// 子类中必须实现父类抽象方法,否则ts编译报错
eat(){
return this.name+"吃肉";
}
}

在类中需要对其内部的属性进行初始化赋值,可以写入默认值,同样也可以根据传入的值进行赋值,也就是在进行实例化的时候传入参数对其属性进行初始化。以下三种方法都可以为类中的属性进行初始化。

//  第一种方法
class A {
public name:string = "Aaron";
}
// 第二种方法
class A {
public name:string;
constructor(name:string){
this.name = name;
}
}
// 第三种方法
class A {
constructor(public name:string){}
}

Vue中使用TypeScript

上面已经对TypeScript的基础做了一些简单的讲解,在开发中是没有问题的了,接下来就开始在Vue项目中开始实战。

对于创建项目就不做过多赘述了,只需要在创建项目时选中TypeScript即可,其他配置项读者可以根据项目需求自行选择。

Vue中使用TypeScript编写项目以后,很多东西发生了变化,上面已经提到了事件与数据,对一些常用的方法进行简单的说明。

组件传值

父组件传入子组件的值通过vue-property-decoratorProp进行接收,传入的方式与Vue中的使用是相同的。

import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
}
组件挂载

组件挂载则是在Component中进行挂载,除了位置发生了变化,其他并没有任何不同。

import {Component, Vue } from 'vue-property-decorator';
import Search from '@/components/business/Search.vue';
@Component({
components: {
Search
}
})
export default class CreateItem extends Vue {}

通过上述代码已经可以正常使用组件了<Search/>

事件消息

事件消息使用Emit进行返回事件

注意:

  1. 当使用Emit时执行事件函数所返回的值,则作为在函数中传给父组件的值。
  2. Emit中可以接收参数,第一个参数可以自定义事件名称,参数什么样,在父组件绑定的事件名称必须一致,否则事件不会执行,如果不传入事件名称,则会默认使用子组件中触发事件的函数名称,在父组件调用时则使用烤串形式进行拼接。
import {Component, Vue, Ref, Emit} from 'vue-property-decorator';
@Component
export default class CreateItem extends Vue {
@Emit("clickMe") // 执行事件 @clickMe
private async onClickMe():string{
return "onClick";
}
@Emit // 执行事件 @on-click-me
private async onClickMe():string{
return "onClick";
}
}
Ref
<template>
<div>
<button ref="btn"><button/>
</div>
</template> <script lang="ts">
import {Component, Vue, Ref} from 'vue-property-decorator';
@Component
export default class CreateItem extends Vue {
// 定义类型可以根据当前为什么元素写入对应的名称的element
// 如果不确定元素可以直接写 HTMLElement
@Ref("btn") readonly formEle!:HTMLButtonElement;
};
</script>
mixins

对于混入的话有两种混入方法,可以依赖于vue-class-component模块中的混入,也可以在Component中进行混入。

第一种方法:

//  混入文件
import { Vue, Component} from 'vue-property-decorator'; @Component
export default class myMixins extends Vue {
values: string = 'Hello' created() {
console.log("混入第二种方法!!!")
} } // 混入
import HomeMixins1 from "@/mixins/Home1";
@Component({
mixins:[HomeMixins1]
})
export default class Home extends Vue { }

第二种方法:

//  混入文件
import Vue from 'vue';
import Component from 'vue-class-component'; @Component
export default class HomeMixin extends Vue {
valueq: string = "Hello" created() {
console.log("混入第一种方法!!")
}
}
// 混入
import Component,{mixins} from 'vue-class-component';
import HomeMixins from "@/mixins/Home";
export default class Home extends mixins(HomeMixins) {}

两种混入方法都是可行的,同样都可以混入多种方法,官网推荐使用第二种方法进行混入。

过滤器

过滤器分文两种,一种全局过滤器,一种局部过滤器,对于全局过滤器几乎没有发生变化。

全局过滤器

全局过滤器写在main.ts

Vue.filter('capitalize', function (value:string) {
if (!value) return ''
return value.charAt(0).toUpperCase() + value.slice(1)
})

局部过滤器

局部过滤器写在各个组件@Component中。

import {Component, Vue} from 'vue-property-decorator';

@Component({
filters:{
thumb(url:string){
return url += "/abcd";
}
}
})
export default class Home extends Vue {}

项目结构

简单说一下笔者在使用Vue开发项目式使用的项目结构,可能不是最好的,但是对于项目的可维护性确实有了很大的提高。在项目开始时需要创建项目结构,Vue项目中为了更好的结构化,需要将项目进行分层处理,以达到高度维护的目的。

目录结构:

├─api           //  数据请求
├─assets // 资源
├─components // 组件
│ ├─basis // 基础组件
│ └─business // 业务组件
├─domain // 业务
├─interface // 接口
├─middleware // 中间件
├─mixins // 混入
├─style // 样式
├─store // 状态管理
├─router // 路由
└─views // 视图

上面对其项目进行了项目结构进行了划分,若对工程化不太了解的同学可能不太能理解每一层的目的到底是为了什么?他们相互之间又应该如何搭配使用?简单对每一层进行简单的描述。

  1. api:其中封装的是请求后端接口数据的请求函数
  2. assets:静态资源
  3. components:组件,在文件中分为了两个文件夹,业务组件和基础组件
  4. domain:项目中的业务逻辑
  5. interface:用户TypeScript限制接口数据格式
  6. middleware:Vue项目中使用的中间件
  7. mixins:需要混入的内容
  8. style:样式
  9. store:全局状态管理
  10. router:路由

这里需要说明一点,在创建项目时如果读者选择了vue-routervuex,创建项目后会自动生成router.tsstore.ts笔者这里并没有使用原有的文件,而是单独对其进行处理。考虑到若项目业务较多可以使用使用文件或文件夹再对其路由和状态管理根据其业务对其进行文件划分,单独维护,这样更加的有利于项目的可维护性。

总结

文章篇幅过长,用了过长的篇幅讲解了TypeScript简单应用,对于Vue中的使用也只是简单的讲解了一下。与其之前是很类似的。

文章些许潦草,但是很感谢大家能够坚持读完本篇文章。若文章中出现错误请大家在评论区提出,我会尽快做出改正。

TypeScript基础以及在Vue中的应用的更多相关文章

  1. 在Vue 中使用Typescript

    Vue 中使用 typescript 什么是typescript typescript 为 javaScript的超集,这意味着它支持所有都JavaScript都语法.它很像JavaScript都强类 ...

  2. Vue 中使用 typescript

    Vue 中使用 typescript 什么是typescript typescript 为 javaScript的超集,这意味着它支持所有都JavaScript都语法.它很像JavaScript都强类 ...

  3. Vue基础01vue的基本示例,vue的双向数据绑定,vue中常见的几种用法,vue相关常见指令

    自学vue框架,每天记录重要的知识点,与大家分享!有不足之处,希望大家指正. 本篇将讲述:vue的基本示例,vue的双向数据绑定,vue中常见的几种用法,vue相关常见指令 前期学习基础,使用vue. ...

  4. Vue基础系列(二)——Vue中的methods属性

      写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家指出. 作者简介: 一个不知名的前端开发 ...

  5. Vue基础系列(四)——Vue中的指令(上)

    写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家和我一起交流. VUE基础系列目录 < ...

  6. Vue基础系列(五)——Vue中的指令(中)

    写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家和我一起交流. VUE基础系列目录 < ...

  7. 在Vue中使用TypeScript

    TypeScript基础学习 初始化TS项目 Vue装饰器 vue-typescript-admin框架 1.TypeScript基础学习 2.初始化TS项目 3.Vue装饰器 Vue装饰器常用的有下 ...

  8. 在 Vue 中使用 Typescript

    前言 恕我直言,用 Typescript 写 Vue 真的很难受,Vue 对 ts 的支持一般,如非万不得已还是别在 Vue 里边用吧,不过听说 Vue3 会增强对 ts 的支持,正式登场之前还是期待 ...

  9. Vue 中使用 TypeScript axios 使用方式

    Vue 中使用 TypeScript axios 使用方式 方式一 import axios from 'axios'; Vue.prototype.$axios = axios; // 在 .vue ...

随机推荐

  1. 【Swoole】计一次swoole_server配合laravel5启动报错:Address already in use[98]

     [2019-11-11 11:42:25 @21371.0] WARNING swSocket_bind(:434): bind(0.0.0.0:9501) failed, Error: Addre ...

  2. 【转发】SqlServer数据库表生成C# Model实体类SQL语句

    已知现有表T1 通过运行下面的sql即可,先配置表名. declare @TableName sysname = 'T1' declare @Result varchar(max) = ' /// & ...

  3. Linux虚拟机:发布WebService接口出现异常,无法访问接口

    Linux虚拟机:发布WebService接口出现异常,无法访问接口 今天在部署WebService工程的时候遇到的问题: 在Linux虚拟机上部署一个tomcat同时在tomcat下放置2个工程,其 ...

  4. config:fail,Error: 系统错误,错误码:63002,invalid signature [20191104 17:18:1

    需要检查下后端有没有缓存到redis.这个很重要不然也会报这个错

  5. nodejs命令行执行时带参数

    nodejs命令行执行时带参数 转 https://www.jianshu.com/p/474e6d76f867   今天项目里突然想在初始化时跑一些数据,于是想起以前在python时可以在命令行里带 ...

  6. J-CUBE Appears at AVATAR Xprize at Geneva 2019

    2019年5月27日,瑞士日内瓦,Avatar Xprize发布会隆重举行.非常荣幸的是,J-CUBE也受邀参加此次大会. 关于Avatar Xprize项目的介绍 https://avatar.xp ...

  7. PMP 第12~13章错题总结

    1.合同解释应该遵循几个主要原则: 1)主导语言原则 2)适用法律原则 3)整体解释原则 4)公平诚信原则2.合同收尾包括的工作: 1)产品核实 2)可交付成果验收 3)财务结算 4)退还保证金或担保 ...

  8. Docker容器中启动OPMS项目

    1.上传opms项目包到Linux下面 2.解压赋权 3.执行文件即可 4.浏览器输入服务器ip地址加上8088端口号

  9. 关于【vue + element-ui Table的数据多选,多页选择数据回显,分页记录保存选中的数据】的优化

    之前写的[vue + element-ui Table的数据多选,多页选择数据回显,分页记录保存选中的数据]这篇博客.功能虽然实现了相对应的功能.但是用起来很不爽.所以进行了优化. 备注:最近可能没时 ...

  10. 十分钟读懂JavaScript原型和原型链

    原型(prototype)这个词来自拉丁文的词proto,意谓“最初的”,意义是形式或模型.在JavaScript中,原型的探索也有很多有趣的地方,接下来跟随我的脚步去看看吧. 原型对象释义 每一个构 ...