关于Vue中如何使用TypeScript可以看使用vue-cli3创建TypeScript项目步骤
在Vue中使用TypeScript,其实就是使用官方维护的vue-class-component[中文文档][英文文档]装饰器的API,而vue-property-decorator[本文参考]就是在vue-class-component的基础上再封装,建议不懂得可以先看看官方文档
vue-property-decorator装饰器和Mixin函数:
- @Component
- @Prop
- @PropSync
- @Model
- @Watch
- @Emit
- @Ref
- mixins
1. @Component(options:ComponentOptions = {}) 装饰器
@Component装饰器可以创建一个Class组件,它接受一个对象作为参数[参数说明]
<script lang="ts">
import { Vue, Component } from "vue-property-decorator"; //导入Component装饰器
import HomeComponent from "@/components/HomeComponent.vue"; // 引入组件
import { NavigationGuardNext, Route } from "vue-router";
@Component({
components: {
HomeComponent,
},
beforeRouteEnter(to: Route, from: Route, next: NavigationGuardNext) {
next((vm: any) => {
console.log(vm); // vm就是当前组件的实例
});
},
beforeRouteUpdate(to: Route, from: Route, next: NavigationGuardNext) {
next();
},
beforeRouteLeave(to: Route, from: Route, next: NavigationGuardNext) {
const answer = window.confirm(
"Do you really want to leave? you have unsaved changes!"
);
if (answer) {
next();
} else {
next(false);
}
},
})
export default class Home extends Vue {
private title = "HomeTitle";
}
</script>
2. @Prop(options: (PropOptions | Constructor[] | Constructor) = {})装饰器
@Prop装饰器接收一个参数,这个参数可以有三种写法:
- Constructor,例如String,Number,Boolean等,指定 prop 的类型;
- Constructor[],指定 prop 的可选类型;
- PropOptions,可以使用以下选项:type,required,default,validator。
// 父组件:
<template>
<div>
<PropComponent height="175" sex="boy" age="26" />
</div>
</template>
<script lang='ts'>
import { Component, Vue } from "vue-property-decorator";
import PropComponent from "@/components/PropComponent.vue";
@Component({
components: {
PropComponent,
},
})
export default class PropPage extends Vue {}
</script>
<style scoped>
</style>
// 子组件:
<template>
<div>我是一个{{ sex }},身高有{{ height }}公分,{{ age }}岁了</div>
</template>
<script lang='ts'>
import { Component, Vue, Prop } from "vue-property-decorator";
@Component
export default class PropComponent extends Vue {
@Prop(String)
// 参数类型的写法
// private height!: string;
// private height?: string;
// private height = "175";
private height: string | undefined;
@Prop({ type: String, required: true, default: "boy" })
private sex!: string;
@Prop([String, Number])
private age!: string | number;
}
</script>
<style scoped>
</style>
3. @PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})装饰器
@PropSync装饰器与@prop用法差不多,区别在于:
- @PropSync 装饰器接收两个参数:
-
- propName: string 表示父组件传递过来的属性名;
-
- options: Constructor | Constructor[] | PropOptions 与@Prop的参数一致;
@PropSync 会生成一个新计算属性的 getter 和 setter.
注意:父组件要结合.sync来使用
- options: Constructor | Constructor[] | PropOptions 与@Prop的参数一致;
@PropSync 会生成一个新计算属性的 getter 和 setter.
// 父组件:
<template>
<div>
<PropSyncComponent :title.sync="title" />
<br />
<button @click="onChangeTitle">父组件按钮</button>
</div>
</template>
<script lang='ts'>
import { Component, Vue } from "vue-property-decorator";
import PropSyncComponent from "@/components/PropSyncComponent.vue";
@Component({
components: {
PropSyncComponent,
},
})
export default class PropSyncPage extends Vue {
private title = "这是父组件传给子组件的一个title";
private onChangeTitle(): void {
this.title = "这是一个父组件改变的title";
}
}
</script>
<style scoped>
</style>
// 子组件:
<template>
<div>
<h1>{{ syncTitle }}</h1>
<button @click="onChangeTitle">子组件按钮</button>
</div>
</template>
<script lang='ts'>
import { Component, Vue, PropSync } from "vue-property-decorator";
@Component
export default class PropSyncComponent extends Vue {
@PropSync("title", { type: String }) syncTitle!: string;
private onChangeTitle(): void {
this.syncTitle = "这是一个子组件改变的title";
}
}
</script>
<style scoped>
</style>
4. @Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})装饰器
@Model装饰器允许我们在一个组件上自定义v-model,接收两个参数:
- event: string 事件名。
- options: Constructor | Constructor[] | PropOptions 与@Prop的参数一致。
// 父组件:
<template>
<div>
<ModelComponent
v-model="title"
@changeInput="onChangeInput"
/>
<div>父组件title:{{ title }}</div>
</div>
</template>
<script lang='ts'>
import { Component, Vue } from "vue-property-decorator";
import ModelComponent from "@/components/ModelComponent.vue";
@Component({
components: {
ModelComponent,
},
})
export default class ModelPage extends Vue {
private title = "通过v-model实现子父组件数据双向绑定";
private onChangeInput(evt: any) {
console.log(evt);
this.title = evt;
}
}
</script>
<style scoped>
</style>
// 子组件:
<template>
<div>
<div>子组件title:{{ title }}</div>
<input
style="width: 50%"
type="text"
:value="title"
@input="onInputHandle($event)"
/>
</div>
</template>
<script lang='ts'>
import { Component, Model, Vue, Emit, Prop } from "vue-property-decorator";
@Component
export default class ModelComponent extends Vue {
//@Model第一个参数changeInput可以随便写,实际上只是规定了子组件要更新父组件值需要注册的方法
//即@Emit第一个参数名,不同也不影响什么
@Model("changeInput", String) readonly title!: string;
@Emit("changeInput")
private onChangeInput(evt: string) {
// console.log(evt);
}
// 监听输入
private onInputHandle(evt: any) {
this.onChangeInput(evt.target.value);
}
}
</script>
<style scoped>
</style>
5. @Watch(path: string, options: WatchOptions = {})装饰器
@Watch 装饰器接收两个参数:
- path: string 被侦听的属性名;
- options?: WatchOptions={} options可以包含两个属性 :
-
- immediate?:boolean 侦听开始之后是否立即调用该回调函数;
-
- deep?:boolean 被侦听的对象的属性被改变时,是否调用该回调函数; 下面的代码中用了不同的方法介绍了@Watch的使用:[JS用法参考]
// 父组件:
<template>
<div>
<WathcComponent :enscore="enscore" :score="score" />
<p>语文分数:{{ score.cnscore }}</p>
<br />
<button @click="onChangeScore">changeScore</button>
</div>
</template>
<script lang='ts'>
import { Component, Vue, Watch } from "vue-property-decorator";
import WathcComponent from "@/components/WatchComponent.vue";
@Component({
components: {
WathcComponent,
},
})
export default class WatchPage extends Vue {
private enscore = 80;
private score = { cnscore: 80, name: "中文分数" };
private onChangeScore() {
this.enscore = 90;
this.score.cnscore++;
// this.score = { cnscore: 90, name: "中文分数" };
// this.score.name = "语文分数";
}
}
</script>
<style scoped>
</style>
// 子组件:
<template>
<div>
<h1>一个{{ age }}岁的学生,<br />期末考试总分是{{ total }}</h1>
<button @click="age = age + 1">addAge</button>
</div>
</template>
<script lang='ts'>
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
@Component
export default class WatchComponent extends Vue {
@Prop(Number)
private enscore!: number;
@Prop()
private score!: any;
private age = 17;
private total = 0;
//1.常用方法
//特定:当值第一次绑定的时候,不会触发监听函数,只有值发生改变才会执行。
@Watch("age")
onAgeChanged(newValue: number, oldValue: number) {
console.log("age", newValue, oldValue);
}
//2.立即执行(immediate)属性
//当需要在绑定值的时候也触发函数,监听值的变化,就需要用到immediate属性。
@Watch("enscore", { immediate: false })
onEnscoreChanged(newValue: object, oldValue: object) {
console.log(
"绑定时触发enscore",
newValue,
oldValue,
this.score,
this.enscore
);
this.total = this.score.cnscore + this.enscore;
console.log(
"注意这里,当immediate为false进入页面时,没有执行监听函数",
this.total
);
}
//3.深度监听
//当需要监听复杂数据类型(对象)的改变时,上述两个方法无法监听到对象内部属性的改变,
//只有score中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
@Watch("score", { deep: false }) //无法监听到变化
// @Watch("score.cnscore", { deep: false })//可以监听到变化
onscoreChanged(newValue: number, oldValue: number) {
console.log(
"深度监听cnscore",
newValue,
oldValue,
this.score.cnscore,
this.score.name,
this.enscore
);
this.total = this.score.cnscore + this.enscore;
console.log(
"注意这里,当deep为false时,没有执行监听函数,total没有变化",
this.total
);
}
}
</script>
<style scoped>
</style>
6. @Emit(event?: string)装饰器
@Emit 装饰器接收一个可选参数,作为事件名称。
- 如果没有提供这个参数,@Emit会将回调函数名的camelCase转为kebab-case,作为事件名称;
- @Emit会将回调函数的返回值作为第二个参数,如果返回值是一个Promise对象,则会在触发前达到完成状态.
- @Emit的回调函数的参数,会放在其返回值之后作为参数被使用。
// 父组件:
<template>
<div>
<!-- <EmitComponent :title="title" @change-title1="onChangeTitle" /> -->
<!-- <EmitComponent :title="title" @change-title2="onChangeTitle" /> -->
<EmitComponent
:title="title"
:time="time"
:site="site"
@change-title="onChangeTitle"
@change-site="onChangeSite"
@change-time="onChangeTime"
/>
</div>
</template>
<script lang='ts'>
import { Component, Vue } from "vue-property-decorator";
import EmitComponent from "@/components/EmitComponent.vue";
@Component({
components: {
EmitComponent,
},
})
export default class EmitPage extends Vue {
private title = "EmitTitle";
private time = "2021年1月3日12点";
private site = "湖南长沙";
private onChangeTitle() {
this.title = "通过@Emit装饰器改变父组件的值";
}
private onChangeTime(data: string) {
console.log(data);
//接受子组件传过来的参数直接赋值
this.time = data;
}
private onChangeSite(data: string, evt: any) {
console.log(data, evt);
//接受回调函数参数赋值
this.site = evt.target.value;
}
}
</script>
<style scoped>
</style>
// 子组件:
<template>
<div>
<h1>{{ title }}</h1>
<h1>{{ time }}</h1>
<h1>{{ site }}</h1>
<button @click="onChangeTitle">改变标题</button>
<br />
<br />
<button @click="onChangeTime">改变时间</button>
<br />
<br />
<input
type="text"
@input="changeSite($event)"
/>
</div>
</template>
<script lang='ts'>
import { Component, Emit, Prop, Vue } from 'vue-property-decorator'
@Component
export default class EmitComponent extends Vue {
@Prop(String)
private title!: string
@Prop(String)
private time!: string
@Prop(String)
private site!: string
//第一个参数是事件名称
@Emit('change-title')
private changeTitle() {
console.log('change-title')
}
//如果未传事件名称,@Emit会将回调函数名changeTitle2的camelCase转为kebab-case,并将其作为事件名;
@Emit()
private changeTime() {
console.log('change-time')
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const min = date.getMinutes()
const second = date.getSeconds()
return (
String(year) + '年' + String(month) + '月' + String(day) + '日' + String(hour) + '点'
)
}
@Emit()
private changeSite(evt: any) {
console.log('change-site')
return 'change-site'
}
private onChangeTitle() {
//这样直接改变props里面的值会报警告
// this.title = "emit-component-title";
this.changeTitle()
}
private onChangeTime() {
this.changeTime()
}
}
</script>
<style scoped>
</style>
7. @Ref(refKey?: string)装饰器
@Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用。没有提供参数,会使用装饰器后面的属性名充当参数。
// 父组件:
<template>
<div>
<RefComponent ref="refcomponent" />
<button @click="onChangeChildText">change</button>
</div>
</template>
<script lang='ts'>
import { Component, Ref, Vue } from 'vue-property-decorator'
import RefComponent from '@/components/RefComponent.vue'
@Component({
components: {
RefComponent,
},
})
export default class RefPage extends Vue {
@Ref('refcomponent') readonly refcomponent!: RefComponent
private onChangeChildText() {
this.refcomponent.p.innerHTML = '通过父组件改变子组件元素值'
}
}
</script>
<style scoped>
</style>
// 子组件:
<template>
<div>
<p ref="p">{{text}}</p>
</div>
</template>
<script lang='ts'>
import { Component, Ref, Vue } from 'vue-property-decorator'
@Component
export default class RefComponent extends Vue {
private text = '@Ref(refKey?: string)装饰器的使用'
@Ref() readonly p!: HTMLParagraphElement
}
</script>
<style scoped>
</style>
8. mixins的使用
mixins有两种使用方法,扩展和混合:
- 将现有的类组件扩展为本机类继承,使用本机类继承语法对其进行扩展:
// mymixins.ts
import { Vue, Component } from 'vue-property-decorator';
declare module 'vue/types/vue' {
interface Vue {
methodFromMixins(value: number | string): void; // 记得声明一下,要不然会报错 Property 'methodFromMixins' does not exist on type 'App'.
}
}
@Component
export default class MyMixins extends Vue {
public text = 'method from mixins,';
public methodFromMixins(value: number | string): string {
console.log(this.text, value);
return this.text + value;
}
created() {
console.log('init data,method from mixins,');
}
}
<template>
<div>{{methodFromMixins('hello mixins')}}</div>
</template>
<script lang='ts'>
import { Component, Vue } from 'vue-property-decorator'
import mymixins from '@/components/mixins/mymixins'
@Component({
mixins: [mymixins],
})
export default class MixinPage extends Vue {
created() {
this.methodFromMixins('hi');
console.log('father init data,method from mixins,');
}
}
</script>
<style scoped>
</style>
- Vue类组件提供了mixins辅助功能,以类样式方式使用mixins。通过使用mixins帮助程序,TypeScript可以推断混合类型并在组件类型上继承它们。 声明HelloMixins和HiMixins:
//mixins.ts
import { Vue, Component } from "vue-property-decorator";
@Component // 一定要用Component修饰
export class HelloMixins extends Vue {
public hello = 'hello mixins';
}
@Component // 一定要用Component修饰
export class HiMixins extends Vue {
public hi = 'hi mixins';
}
在类样式组件中使用它们:
<template>
<div>{{hello}} | {{hi}}</div>
</template>
<script lang='ts'>
import { Component, Vue } from 'vue-property-decorator'
import { HelloMixins, HiMixins } from '@/components/mixins/Mixins'
import { mixins } from 'vue-class-component'
@Component
export default class MixinPage extends mixins(HelloMixins, HiMixins) {
created() {
console.log(this.hello + this.hi)
}
}
</script>
<style scoped>
</style>
注意:每个超类都必须是一个类组件。它需要继承Vue构造函数作为祖先并由@Component装饰器进行装饰。
以上就是vue-property-decorator的基本用法,觉得有帮助的点个赞^_^[github]
**内容原创,整理不易,转载请注明出处!!!谢谢合作!**