- @Component
- @Prop
- @PropSync
- @Model
- @Watch
- @Emit
- @Ref
- mixins
- @Provide / @Inject /@ProvideReactive / @InjectReactive
1. @Component(options:ComponentOptions = {}) 装饰器
@Component 装饰器可以创建一个 Class 组件,它接受一个对象作为参数
typescript
<script lang="ts">
import { Vue, Component } from "vue-property-decorator"; //导入Component装饰器
import HomeComponent from "@/components/HomeComponent.vue"; // 引入组件
@Component({
components: {
HomeComponent,
}
})
export default class Home extends Vue {
private title = "HomeTitle";
}
</script>
2. @Prop(options: (PropOptions | Constructor[] | Constructor) = {})装饰器
@Prop
装饰器接收一个参数,常常这样用
@Prop('String')
: 指定prop
的类型,字符串String,Number,Boolean
等@Prop({ default: 1, type: Number })
: 对象,指定默认值{default:'1'}
@Prop([String,Number])
: 数组,指定prop
的可选类型[String, Boolean]
typescript
// 父组件:
<template>
<div class="Props">
<Child :name="name" :age="age" :sex="sex"></Child>
</div>
</template>
<script lang="ts">
import {Component, Vue,} from 'vue-property-decorator';
import Child from './Child.vue';
@Component({
components: {Child}, // 上边有说 @Component 可接受的参数
})
export default class PropsPage extends Vue {
private name = 'Hs';
private age = 18;
private sex = 1;
}
</script>
// 子组件:
<template>
<div>
name: {{name}} | age: {{age}} | sex: {{sex}}
</div>
</template>
<script lang="ts">
import {Component, Vue, Prop} from 'vue-property-decorator';
@Component
export default class Child extends Vue {
@Prop(String) readonly name!: string | undefined;//告诉ts这里一定有值
@Prop({ default: 20, type: Number }) private age!: number;
@Prop([String, Number]) private sex!: string | number;
}
</script>
3. @PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})装饰器
@PropSync 装饰器与@prop 用法差不多,区别在于:
- @PropSync 装饰器接收两个参数:
- propName: string 表示父组件传递过来的属性名;
- options: Constructor | Constructor[] | PropOptions 与@Prop 的参数一致;
@PropSync 会生成一个新计算属性的 getter 和 setter. 注意:父组件要结合.sync来使用
typescript
// 父组件:
<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 的参数一致。
typescript
// 父组件:
<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 被侦听的对象的属性被改变时,是否调用该回调函数;
typescript
// 父组件:
<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 的回调函数的参数,会放在其返回值之后作为参数被使用。
typescript
// 父组件:
<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 装饰器接收一个可选参数,用来指向元素或子组件的引用。没有提供参数,会使用装饰器后面的属性名充当参数。
typescript
// 父组件:
<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 有两种使用方法,扩展和混合:
- 将现有的类组件扩展为本机类继承,使用本机类继承语法对其进行扩展:
typescript
// 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:
typescript
//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";
}
在类样式组件中使用它们:
typescript
<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装饰器进行装饰。
@Provide / @Inject 和 @ProvideReactive / @InjectReactive`
提供/注入装饰器,key 可以为 string 或者 symbol 类型,使用方式都一样
- 相同: Provide/ProvideReactive 提供的数据,在子组件内部使用 Inject/InjectReactive 都可取到
- 不同: ProvideReactive 的值被父组件修改,子组件可以使用 InjectReactive 捕获
typescript
// 顶层组件
<template>
<div class="about">
<ChildComp />
</div>
</template>
<script lang="ts">
import { Component, Provide,Vue } from "vue-property-decorator";
import ChildComp from "./Child.vue";
@Component({
components: { ChildComp },
})
export default class About extends Vue {
@Provide("provide_value") private p = "from provide";
}
</script>
// 子组件
<template>
<div>
<h3>我是子组件</h3>
<h3>provide/inject 跨级传参 {{provide_value}}</h3>
</div>
</template>
<script lang="ts">
import { Component, Inject, Vue } from "vue-property-decorator";
@Component
export default class Child extends Vue {
@Inject() readonly provide_value!: string;
}
</script>