typescript + vue 基础操作

1,629 阅读3分钟

创建项目

新建一个基于ts的vue项目

vue create xxx

如下配置勾选TS

在已存在的项目中安装typescript

vue add @vue/typescript

TS语法特点

  • 类型注解、类型检测
  • 接口
  • 泛型

类型注解和编译时类型检查

使用类型注解约束变量类型,编译器可以做静态类型检查,使程序更加健壮

// 类型注解
let var1: string;   
var1 = "慧永诺" //正确
var1 = 4        //错误

// 编译器类型推断可省略这个语法
let var2 = true;
var2 = 1    //错误

//类型数组
let arr: string[];
arr = ['hello','world'];   //正确
arr = [1,2]                //错误

// 任意类型any
let varAny: any;
varAny = 1;
varAny = 'asd'

// any类型也可用于数组
let arrAny: any[];
arrAny = ['1',3,true]

// 函数中的类型约束  person: string代表参数类型   (person: string): string代表返回值类型
function greet(person: string): string{
    return person + 'hello';
}

//函数中必填参:参数一旦声明,就要求传递,且类型须符合;可选参:参数名后面加上问号;默认值:直接赋值
function greeting(person: string, msg = '', desction?: string): string{
	if(desction){
    	return person + msg + desction + 'hello'
    }else{
    	return person + msg + 'hello';
    }
}

// void类型,常用于没有返回值的函数
function warn(person: string): void{

}

// 使用类型别名自定义类型
let objType: {foo: string, bar: string}
objType = {foo: '1',bar: '2add'}

// 使用type定义类型别名,使用更便捷,还能复用
type Foobar = {foo: string, bar: string}
let aliasType: Foobar;

// 联合类型:希望某个变量或参数的类型是多种类型其中之一
let union: string | number;
union = '1';    //正确
union = 1       //正确

// 交叉类型:想要定义某种由多种类型合并而成的类型使用交叉类型
type First = {first: number};
type Second = {second: number};
// FirstSecond将同时拥有属性first和second
type FirstSecond = First & Second

类class的特性

ts中的类和es6中的类大体相同,重点介绍一下ts中类的访问控制这一特性

 class Parent{
    private work = 'work';    //私有属性,不能在类的外部访问
    protected money = 'money';  //保护属性,可以在子类中访问
    // 参数属性:构造函数参数加修饰符,能够定义为成员属性
    constructor(public appearance = 'appearance'){}

    // 方法也有修饰符
    private someMethod(){
        console.log('111')
    }

    //存取器:属性方式访问,可添加额外逻辑,控制读写性
    // 除了public可以直接访问,其他私有属性通过get方法才能外部访问
    get fool(){
        return this.foo
    }
    set fool(val){
        this.foo = val
    }
}

class Child extends Parent{
    log(){
       console.log(this.money) 
    }
}

接口 interface

接口仅约束结构,不要求实现,使用更简单;接口中只需要定义结构,不需要初始化,和上方的type aliases无特殊区别,写库的时候会推荐使用interface,因为他出现的更早

//Person接口定义了结构
interface Person{
	firstName: string;
    lastName: string;
}
//greeting函数通过Person接口约束参数结构
function greeting(person: Person){
	return 'Hello,' + person.firstName +person.lastName;
}
greeting({firstName: 'Zhang',lastName: 'San'});	//正确
greeting({firstName: 'Zhang'});	//错误

泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。以次增加代码通用性

泛型优点:

  • 函数和类可以支持多种类型,更加通用
  • 不必编写多条重载,冗长类和类型,可读性好
  • 灵活控制类型约束 不仅通用且能灵活控制,泛型被广泛应用于通用库的编写
//使用泛型
interface Result<T> {
	ok: 0 | 1;
    data: T
}

//泛型方法
function getResult<T> (data: T): Result<T>{
	return data
}

//用尖括号方式指定T为string
getResult<string>('hello')

//用类型推断指定T为number
getResult(1)

声明文件

使用ts开发时如果要使用第三方js库的同时还想利用ts诸如类型检查等特性就需要声明文件,类似xx.d.ts 同时vue项目中还可以在shims-vue.d.ts中对已存在模块进行补充

示例1:利用模块补充$axios属性到vue示例,从而在组件中直接使用

//main.ts
//在没有用ts的vue中,挂载在Vue.prototype上就可以直接使用了
import Vue from 'vue';
import axios from 'axios';

Vue.prototype.$axios = axios
//shims-vue.d.ts
//ts中还需要对模块补充声明,才能在组件中直接使用

import Vue from 'vue';
import {AxiosInstance} from "axios";

declare module '*.vue' {
  export default Vue
}

// 声明:扩展模块
declare module "vue/types/vue" {
  interface Vue{
    $axios: AxiosInstance
  }
}

示例2:在以存在的项目中替换为ts,比如router/index.js,js文件我们需要编写声明文件router/index.d.ts

import VueRouter from 'vue-router';

declare const router: VueRouter
export default router

装饰器

装饰器用于扩展类或者它的属性和方法。@xxx 就是装饰器的写法

下列用的装饰器需要导入 import {Componen,Prop,Emit,Watch} from "vue-property-decorator";

@Component

创建vue组件,ts写法如下:

<script lang="ts">
import {Componen} from "vue-property-decorator";
@Component
export default class Hello extends Vue{}
</script>

那么组件中的components怎么声明呢?

@Component中添加参数@Component({components:{xxx,xxx}})

@Prop

采用@Prop的方式声明组件属性

@Component
export default class Hello extends Vue{
	// @Prop()参数是为vue提供属性选项
    // ! 称为明确赋值断言
	@Prop({type: String, required: true})
  	msg!: string
}

@Emit

派发事件

 //HelloWorld.vue
 // 通知父类新增事件,若未指定事件名则函数名作为事件名(父类以羊肉串形式调用); @Emit('事件名')
  @Emit()
  addFeature(event: any){
  	//e.target是EventTarget类型,需要断言为HTMLInputElement
    const inp = event.target as HTMLInputElement;
    const feature = {id: this.features.length + 1,name: inp.value,selected:true};
    this.features.push(feature);
    event.target.value = '';
    return feature;
  }
//App.vue  父类接收事件
<template>
<HelloWorld  @add-feature="addFeature"></HelloWorld>
</template>
<script lang="ts">
  @Component({components:{HelloWorld}})
  export default class App extends Vue{
    addFeature(feature: FeactureSelect) {
      console.log('新增了一个特性', feature.name);
    }
 }
</script>

@Watch

@Watch('msg')
onMsgChange(val: string,oldVal: any){
	console.log(val,oldVal);
}

data()在ts中怎么写?

//之前js写法
data(){
return{
		features:[{id:1,name:"张三",selected:true},{id:2,name:"李四",selected:false}];
	}
}
//ts写法省去了定义data
 features: FeactureSelect[] = [{id:1,name:"张三",selected:true},{id:2,name:"李四",selected:false}];

methods在ts中怎么写?

//之前js写法
methods:{
	greet(person,msg){
    if(! msg){
      console.log(person + 'hello')
    }else{
      console.log(person + msg + 'hello')
    }
  }
}
//ts写法省去了定义methods
greet(person: string,msg?: string): void{
    // return person + 'hello';
    if(! msg){
      console.log(person + 'hello')
    }else{
      console.log(person + msg + 'hello')
    }
 }

computed在ts中怎么写?

//js写法
computed:{
  count(){
    return this.features.length;
  }
}
//ts写法
// 定义getter作为计算属性
  get count(){
    return this.features.length;
  }

生命周期在ts中怎么调用?

//和之前写法无异
 created(){
    const parent = new Parent()
    console.log(parent.fool)
 }

vuex状态管理

vuex-module-decorators 通过装饰器提供模块化声明vuex模块的方法。

  • 安装 npm i vuex-module-decorators -D

  • 根模块清空,修改store/index.ts

    export default new Vuex.store({})

  • 定义user模块,创建store/counter

import { Module, VuexModule, Mutation, Action, getModule } from 'vuex-module-decorators'
import store from './index'

// 动态注册模块
@Module({ dynamic: true, store: store, name: 'user', namespaced: true })
class UserrModule extends VuexModule {
  token:localStorage.getItem('token');

  @Mutation
  setToken(token) {
  //通过this直接访问token
    this.token = token
  }
  
  //定义getters
  get tokenMi() {
      return this.count + 'sadasd';
  }
    
  @Action
  login(userinfo) {
    return login(userinfo).then(res => {
      this.setToken(res.data);
      localStorage.setItem("token", res.data);
    });
  }

}

export default getModule(UserModule)
//使用,App.vue
//第一种
<p>{{$store.state.user.token}}</p>
//第二种
<template>
<p @click="setToken()">{{UserModule.token}}</p>
</template>
<script type="ts">
import UserModule from '@/store/user';
@Component({components:{HelloWorld}})
export default class App extends Vue{
  setToken(){
      UserModule.login()
   }
}
</script>