vue-property-decorator 完全依赖 vue-class-component。在 Vue 单文件里用 TypeScript 时,它把 Options API 收成装饰器:类字段当 data、get 当 computed,@Emit / @Watch / @Prop / @Model 对应事件、侦听、属性和 v-model。
它提供这些能力:
-
@Component(完全继承于 vue-class-component)
-
-
-
-
-
-
-
Mixins(在 vue-class-component 中定义)
| 装饰器 |
对应 Options API |
| 类字段 |
data |
| get xxx() |
computed |
| @Emit / @Emit(‘name’) |
$emit,默认事件名是函数名(驼峰转横杠) |
| @Watch(‘key’, opts) |
watch |
| @Prop(type|object|array) |
props |
| @Model(event, propOptions) |
model + 对应 prop |
一、基本用法:data 与 computed
引入之后,script 可以写成:
<script lang="ts">
import {Vue, Component} from 'vue-property-decorator';
@Component({})
export default class "组件名" extends Vue{
ValA: string = "hello world";
ValB: number = 1;
}
</script>
等同于
<script lang="es6">
import Vue from 'vue';
export default {
data(){
return {
ValA: 'hello world',
ValB: 1
}
}
}
</script>
-
总结:data 里的变量,按 TS 类字段来写即可。
计算属性用 getter:
<script lang="ts">
import {Vue, Component} from 'vue-property-decorator';
@Component({})
export default class "组件名" extends Vue{
get ValA(){
return 1;
}
}
</script>
等同于
<script lang="es6">
import Vue from 'vue';
export default {
computed: {
ValA: function() {
return 1;
}
}
}
</script>
-
总结:把计算属性名写成函数,前面加
get。原来 computed 里每一项都变成前缀 get 的函数。
二、@Emit
Vue 用 $emit / $on 做事件。装饰器版用 @Emit:
<script lang="ts">
import {Vue, Component, Emit} from 'vue-property-decorator';
@Component({})
export default class "组件名" extends Vue{
mounted(){
this.$on('emit-todo', function(n) {
console.log(n)
})
this.emitTodo('world');
}
@Emit()
emitTodo(n: string){
console.log('hello');
}
}
</script>
运行会打印 ‘hello’ ‘world’。等同于:
<script lang="es6">
import Vue from 'vue';
export default {
mounted(){
this.$on('emit-todo', function(n) {
console.log(n)
})
this.emitTodo('world');
},
methods: {
emitTodo(n){
console.log('hello');
this.$emit('emit-todo', n);
}
}
}
</script>
@Emit 装饰的函数跑完后,会触发与函数名对应的事件(驼峰转横杠),并把参数交给 $emit。若要在 emitTodo 里触发 reset:
<script lang="ts">
import {Vue, Component, Emit} from 'vue-property-decorator';
@Component({})
export default class "组件名" extends Vue{
@Emit('reset')
emitTodo(n: string){
}
}
</script>
给装饰器传事件名即可。函数参数会在触发时传出去。两种写法:
-
-
@Emit(name: string) 传入要触发的事件名。
三、@Watch
用 @Watch 替换 Options 里的 watch。Vue 里常见写法:
export default{
watch: {
'child': this.onChangeValue
// 这种写法默认 `immediate`和`deep`为`false`
,
'person': {
handler: 'onChangeValue',
immediate: true,
deep: true
}
},
methods: {
onChangeValue(newVal, oldVal){
// todo...
}
}
}
装饰器版:
import {Vue, Component, Watch} from 'vue-property-decorator';
@Watch('child')
onChangeValue(newVal: string, oldVal: string){
// todo...
}
@Watch('person', {immediate: true, deep: true})
onChangeValue(newVal: Person, oldVal: Person){
// todo...
}
-
第一个参数是要监听的属性名,第二个是可选对象。被装饰的函数就是变化后的处理。
四、@Prop
子组件收父组件参数时要定义 Prop。例如三个属性:
-
-
-
propC 类型 String 或 Boolean
export default {
props: {
propA: {
type: Number
},
propB: {
default: 'default value'
},
propC: {
type: [String, Boolean]
},
}
}
改成:
<script lang="ts">
import {Vue, Component, Prop} from 'vue-property-decorator';
@Component({})
export default class "组件名" extends Vue{
@Prop(Number) propA!: number;
@Prop({default: 'default value'}) propB!: string;
@Prop([String, Boolean]) propC: string | boolean;
}
</script>
这里 ! 和可选参数 ? 相反,告诉 TypeScript 这里一定有值。
-
@Prop 参数可以是类型、对象或数组。里面的 Number 是 JavaScript 类型,后面属性类型才是 TypeScript 类型。
五、Mixins
结合 TypeScript 后有两种 mixins。第一种来自 vue-class-component:
//定义要混合的类 mixins.ts
import Vue from 'vue';
import Component from 'vue-class-component';
@Component // 一定要用Component修饰
export default class myMixins extends Vue {
value: string = "Hello"
}
// 引入
import Component {mixins} from 'vue-class-component';
import myMixins from 'mixins.ts';
@Component
export class myComponent extends mixins(myMixins) {
// 直接extends myMinxins 也可以正常运行
created(){
console.log(this.value) // => Hello
}
}
第二种:在 @Component 里混入。先改 mixins.ts,声明 vue/types/vue 模块、实现 Vue 接口:
// mixins.ts
import { Vue, Component } from 'vue-property-decorator';
declare module 'vue/types/vue' {
interface Vue {
value: string;
}
}
@Component
export default class myMixins extends Vue {
value: string = 'Hello'
}
混入:
import { Vue, Component, Prop } from 'vue-property-decorator';
import myMixins from '@static/js/mixins';
@Component({
mixins: [myMixins]
})
export default class myComponent extends Vue{
created(){
console.log(this.value) // => Hello
}
}
-
没声明 vue/types/vue 模块时,混入要 继承 该 mixin;声明了就可以在
@Component 的 mixins 里直接混入。
六、@Model
组件的 model: {prop?: string, event?: string} 用来定制 prop 和 event。默认 v-model 用 value 作 prop、input 作 event。单选框、复选框可能要把 value 派别的用场,用 model 避开冲突。
Vue 官网例子:
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
// this allows using the `value` prop for a different purpose
value: String,
// use `checked` as the prop which take the place of `value`
checked: {
type: Number,
default: 0
}
},
// ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
相当于:
<my-checkbox
:checked="foo"
@change="val => { foo = val }"
value="some value">
</my-checkbox>
即 foo 双向绑定的是组件的 checked,触发更新的事件是 change。装饰器版:
import { Vue, Component, Model} from 'vue-property-decorator';
@Component
export class myCheck extends Vue{
@Model ('change', {type: Boolean}) checked!: boolean;
}
-
@Model() 两个参数:第一个是 event,第二个是 prop 的类型说明(类似 @Prop,用 JS 类型)。后面再写 prop 名和 TS 类型。
暂时常用的就这些,@Provide 和 @Inject 用到了再写。
参考: https://juejin.im/post/5d31907a51882557af271be2#heading-0
链接:https://www.jianshu.com/p/d8ed3aa76e9b
一句话总结:vue-property-decorator 就是用装饰器把 Vue 选项写成 TS 类:字段是 data,get 是 computed,Emit / Watch / Prop / Model / Mixins 一一对应原来的配置项。