最近切换项目,当前项目是vue+ts
,开始以为就是加个类型,打开文件一看,有点蒙,做个笔记记录下。
这边是结合vue-property-decorator
使用ts。
使用ts的vue项目,和js项目结构是类似的,多了几个文件
- tsconfig.json: typescript配置文件,主要用于指定待编译的文件和定义编译选项
- shims-tsx.d.ts: 允许.tsx 结尾的文件,可在 Vue 项目中编写 jsx 代码
- shims-vue.d.ts: 主要用于 TypeScript 识别.vue 文件,Ts 默认并不支持导入 vue 文件
配合ts使用的库
- vue-class-component: 类的装饰器
- vue-property-decorator: 基于 vue 组织里 vue-class-component 所做的拓展
import { Vue, Component, Inject, Provide, Prop, Model, Watch, Emit, Mixins } from 'vue-property-decorator'
- vuex-module-decorators: 用 typescript 写 vuex 很好用的一个库
import { Module, VuexModule, Mutation, Action, MutationAction, getModule } from 'vuex-module-decorators'
组件声明
组件声明乍一看有点复杂,以前就是export default {name:'Home',data:...}
现在必须拆分成以下这样:
import { Component, Vue } from "vue-property-decorator";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
@Component({
name:'Home'
components: {
HelloWorld
}
})
export default class Home extends Vue {}
data对象
data在ts里是直接展开属性的:
// 可以在上面使用{{name}}
export default class Home extends Vue {
private name = "哈哈哈";
}
props声明
使用@props
export default class HelloWorld extends Vue {
// <hello-world msg="颜酱">
@Prop({ default: "" }) private msg!: string;
}
-
!: 表示一定存在,?: 表示可能不存在。这两种在语法上叫赋值断言
-
@Prop(options: (PropOptions | Constructor[] | Constructor) = {})
- PropOptions,可以使用以下选项:type,default,required,validator
- Constructor[],指定 prop 的可选类型
- Constructor,例如 String,Number,Boolean 等,指定 prop 的类型
methods
直接展开写就行
export default class HelloWorld extends Vue {
// methods <div @click="clickName">
public clickName(): void {
console.log(this.name);
console.log(this.msg);
}
}
watch
watch也是分两块:
export default class HelloWorld extends Vue {
// watch,函数和参数分为两部分,函数名必须是on[key]Change
@Watch("name", { immediate: true, deep: true })
private onNameChange(newValue: string, oldValue: string) {
console.log("watch", newValue, oldValue);
}
}
- @Watch(path: string, options: WatchOptions = {})
- options 包含两个属性 immediate?:boolean 侦听开始之后是否立即调用该回调函数 / deep?:boolean 被侦听的对象的属性被改变时,是否调用该回调函数
- @Watch('arr', { immediate: true, deep: true }) onArrChanged(newValue: number[], oldValue: number[]) {}
computed
computed直接展开,就是普通方法加个get
export default class HelloWorld extends Vue {
// computed 就是普通方法前面加个get,表示此属性是计算来的 <div>{{allname}}</div>
public get allname() {
return "computed " + this.name;
}
set allname(newValue){
this.name = newValue.slice(8)
}
}
生命周期函数
生命周期函数就是普通函数的感觉
export default class HelloWorld extends Vue {
// created生命周期函数
public created(): void {
console.log("created");
}
// mounted生命周期函数
public mounted(): void {
console.log("mounted");
}
}
注意:写ts版的vue时不要在生命周期中使用其他组件的变量。最好使用js写vue,因为有很多你意想不到的问题……
mixin
ts中写mixin,mixin本身是个ts文件,这点需要注意。
// PeMixin.ts 注意这里的文件命名
import { Component, Vue, Prop } from 'vue-property-decorator';
@Component
export default class PeMixin extends Vue {
@Prop({ required: true })
structure: AnyObj;
created(){
// ...
}
}
// 使用mixin的文件
import {
Component, Prop, Watch, Mixins,
} from 'vue-property-decorator';
// 两个的话,Mixins里面继续添加即可
@Component
export default class Pe extends Mixins(PeiDuiAnswer) {
// 组件逻辑
}
但是Mixin 在方法里找不到组件中的变量和方法。
emit
emit稍微绕了点:
export default class HelloWorld extends Vue {
/* 以下相当于js时候的
clickMsg(){
this.name='xx';
this.$emit('change-msg','ddd')
}
当前组件 <div @click="clickMsg">
父组件 <hello-world :msg="msg" @change-msg="handleChangeMsg">
handleChangeMsg(value){
this.msg = value
}
*/
@Emit("change-msg")
clickMsg() {
// 组件本身的逻辑
this.name = "点击msg 之后 可以修改名字";
// emit的第二个参数,也就是给父组件的值
return "ddd";
}
}
- @Emit(event?: string)
- @Emit 装饰器接收一个可选参数,该参数是emit的第一个参数,充当事件名,没有提供这个参数的话,Emit 会将回调函数名的 camelCase 转为 kebab-case,并将其作为事件名
- @Emit 会将回调函数的返回值作为第二个参数,如果返回值是一个 Promise 对象,$emit 会在 Promise 对象被标记为 resolved 之后触发
- @Emit 的回调函数的参数,会放在其返回值之后,一起被$emit 当做参数使用