一.安装
vue add @vue/typescript
二.vue中三种使用方式
- class-style
<script lang="ts">
import { Component, Prop, Vue, Emit } from "vue-property-decorator";
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string; // 这行约束是写给ts编译器的
// 属性将成为data中数据
features: string[] = ["类型注解", "编译类型语言"];
}
</script>
- option-stytle
import Vue from 'vue'
export default Vue.extend({
data(){}
})
- 直接创建一个tsx文件(写法类似react)
import { Component, Prop, Vue } from 'vue-property-decorator';
import { CreateElement } from 'vue';
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
onclick(){
console.log(this.msg);
}
render(h: CreateElement) {
return <div onClick={this.onclick}>{this.msg}</div>
}
}
三.TS特点
类型注解、编译时类型检查
备注:使用类型注解约束变量类型,编译器可以做静态类型检查,使程序更加健壮
- 类型基础
// 类型注解
let var1: string;
var1 = '曾小白';
// var1=1 // no ok
// 类型推断
let var2 = true;
// var2 = 1 // no ok
// 常见类型: string, boolean, number, undefined, null
// 类型数组
let arr :string[]
arr = ['tom', 'jerry']
// arr = [1,2] // no ok
// 任意类型any
let varAny: any;
varAny = 'tom'
varAny = 1
let arrAny: any[]
arrAny = [1,2,3]
// 函数中的类型约束
function great(person: string): string {
return 'hello,' + person
}
const res = great('tom')
// void类型,常用于没有任何返回值的函数
function warn(): void {}
- 类型别名(使用类型别名自定义类型)
// 可以用下面这样方式定义对象类型
const objType: { foo: string, bar: string }
// 使用type定义类型别名,使用更便捷,还能复用
type Foobar = { foo: string, bar: string }
const aliasType: Foobar
- 接口(接口仅约束结构,不要求实现,使用更简单)
// Person接口定义了结构
interface Person {
firstName: string;
lastName: string;
}
// greeting函数通过Person接口约束参数解构 function greeting(person: Person) {
return 'Hello, ' + person.firstName + ' ' + person.lastName;
}
greeting({firstName: 'Jane', lastName: 'User'}); // 正确
greeting({firstName: 'Jane'}); // 错误
- 联合类型(希望某个变量或参数的类型是多种类型其中之一)
let union: string | number;
union = '1'; // ok
union = 1; // ok
- 交叉类型(想要定义某种由多种类型合并而成的类型使用交叉类型)
type First = {first: number};
type Second = {second: number};
// FirstAndSecond将同时拥有属性first和second
type FirstAndSecond = First & Second;
- 函数
// 必填参:参数一旦声明,就要求传递,且类型需符合
function greeting(person: string): string {
return "Hello, " + person;
}
greeting('tom')
//可选参数:参数名后面加上问号,变成可选参数
function greeting(person: string, msg?: string): string {
return "Hello, " + person;
}
// 默认值
function greeting(person: string, msg = ''): string {
return "Hello, " + person;
}
//函数重载:以参数数量或类型区分多个同名函数
// 重载1
function watch(cb1: () => void): void;
// 重载2
function watch(cb1: () => void, cb2: (v1: any, v2: any) => void): void; // 实现
function watch(cb1: () => void, cb2?: (v1: any, v2: any) => void) {
if (cb1 && cb2) {
console.log('执行watch重载2');
} else {
console.log('执行watch重载1');
}
}
- 类class的特性(ts的特性和es6中大体相同,重点关注ts带来的访问控制等特性)
class Parent {
private _foo = "foo"; // 私有属性,不能在类的外部访问
protected bar = "bar"; // 保护属性,可以在子类中访问
// 参数属性:构造函数参数加修饰符,能够定义为成员属性
constructor(public tua = "tua") {}
// 方法也有修饰符
private someMethod() {}
// 存取器:属性方式访问,可添加额外逻辑,控制读写性
// 定义getter作为计算属性
get foo() {
return this._foo;
}
set foo(val) {
this._foo = val;
}
}
- 泛型(泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定 类型的一种特性。以此增加代码通用性。 )
// 不用泛型
// interface Result {
// ok:0|1;
// data: Feature[];
// }
// 使用泛型
interface Result<T> {
ok: 0 | 1;
data: T; }
// 泛型方法
function getResult<T>(data: T): Result<T> {
return {ok:1, data};
}
// 用尖括号方式指定T为string getResult<string>('hello') // 用类型推断指定T为number getResult(1)
// 泛型的优点:
// 1.函数和类可以支持多种类型,更加通用
// 2.不必编写多条重载,冗长联合类型,可读性好
// 3.灵活控制类型约束
四.声明文件
使用ts开发时如果要使用第三方js库的同时还想利用ts诸如类型检查等特性就需要声明文件,类似xx.d.ts
// vue项目中还可以在shims-vue.d.ts中对已存在模块进行补充
npm i @types/xxx
// 利用模块补充$axios属性到Vue实例,从而在组件里面直接用
// main.ts
import axios from 'axios'
Vue.prototype.$axios = axios;
// shims-vue.d.ts
import Vue from "vue";
import { AxiosInstance } from "axios";
declare module '*.vue' {
export default Vue
}
declare module "vue/types/vue" {
interface Vue {
$http: AxiosInstance;
}
}
五. 装饰器
装饰器用于扩展类或者它的属性和方法。@xxx就是装饰器的写法
- 属性声明:@Prop
export default class HelloWorld extends Vue {
// Props()参数是为vue提供属性选项
// !称为明确赋值断言,它是提供给ts的
@Prop({type: String, required: true})
private msg!: string;
}
- 事件处理:@Emit
// 通知父类新增事件,若未指定事件名则函数名作为事件名(羊肉串形式)
@Emit()
private addFeature(event: any) {
// 若没有返回值形参将作为事件参数
const feature = { name: event.target.value, id: this.features.length + 1 }; this.features.push(feature);
event.target.value = "";
return feature;
// 若有返回值则返回值作为事件参数
}
- 变更监测:@Watch
@Watch('msg')
onMsgChange(val:string, oldVal:any){
console.log(val, oldVal);
}
六. 状态管理推荐使用:vuex-module-decorators
- 安装
npm i vuex-module-decorators -D
- 根模块清空,修改store/index.ts
export default new Vuex.Store({})
- 定义counter模块,创建store/counter
import {Module, VuexModule, Mutation, Action, getModule} from 'vuex-module-
decorators
'
import store from './index'
// 动态注册模块
@Module({dynamic: true, store: store, name: 'counter', namespaced: true})
class CounterModule extends VuexModule {
count = 1
@Mutation
add() {
// 通过this直接访问count
this.count++
}
// 定义getters
get doubleCount() {
return this.count * 2;
}
@Action
asyncAdd() {
setTimeout(() => {
// 通过this直接访问add this.add()
}, 1000);
}
}
// 导出模块应该是getModule的结果
export default getModule(CounterModule)
- 使用,App.vue
<p @click="add">{{$store.state.counter.count}}</p>
<p @click="asyncAdd">{{count}}</p>
import CounterModule from '@/store/counter'
@Component
export default class App extends Vue {
get count() {
return CounterModule.count
}
add() {
CounterModule.add()
}
asyncAdd() {
CounterModule.asyncAdd()
}
}
七. 装饰器原理
装饰器是工厂函数,它能访问和修改装饰目标。
- 类装饰器
// 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
function log(target: Function) {
// target是构造函数
console.log(target === Foo); // true
target.prototype.log = function() {
console.log(this.bar);
}
}
@log
class Foo {
bar = 'bar'
}
const foo = new Foo();
// @ts-ignore
foo.log();
- 方法装饰器
// 方法装饰器:区别是参数数量和类型
// target类实例,name:方法名,最后的是描述符
function rec(target: any, name: string, descriptor: any) {
// 这里通过修改descriptor.value扩展了bar方法
const baz = descriptor.value;
descriptor.value = function (val: string) {
// 扩展功能
console.log('run method', this.bar);
// 本来功能
baz.call(this, val);
console.log('run method', this.bar);
}
}
class Foo {
@rec
setBar(val: string) {
this.bar = val
} }
foo.setBar('lalala')