最近接触了一个新项目,是基于 Webpack 搭建的 Vue2+TypeScript 项目,里面用的语法有些新奇,经查阅资料,发现用的是 vue-property-decorator ,下面是整理的笔记。

装饰器是什么

1、装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法,许多面向对象的语言都有这项功能。

2、装饰器是一种函数,写成@ + 函数名。它可 以放在类和类方法的定义前面。

为什么要使用 vue-property-decorator

vue-class-component 是官方推出的vue对 typescript 支持的装饰器(库),可以将 Vue 中的组件用类的方式编写。vue-property-decoretor 即vue属性装饰器,这个库完全依赖于 vue-class-component 。在 vue 中使用 typescript,通过装饰器来简化书写。

如何使用 vue-property-decorator

基本用法

<template>
  <div class="text">测试</div>
</template>

<script lang="ts">
  import { Vue, Component } from "vue-property-decorator";
  @Component
  export default class Test extends Vue {
    
  }
</script>

<style lang="less" scoped>
  .text{color:red}
</style>

lang="ts": 表示脚本当前语言是TypeScript;

@Component: 表示这个类是一个 vue 组件,@Component不能省略,否则会报错; 

Test: 表示组件名

export default class Login extends Vue: 表示当前组件类是继承vue的

定义变量

data中的数据由原来的data()方法改成直接在对象中定义,data内的属性直接作为实例属性书写,默认都是public公有属性,当变量标记为private时,它就不能在声明它的类的外部访问。

<template>
  <div class="text">{{uName}}--{{age}}</div>
</template>

<script lang="ts">
  import { Vue, Component } from "vue-property-decorator";
  @Component
  export default class Test extends Vue {
    uName='张三'
    private age=18
  }
</script>

<style lang="less" scoped>
  .text{color:red}
</style>

生命周期钩子函数

<template>
  <div class="text">{{uName}}--{{age}}</div>
</template>

<script lang="ts">
  import { Vue, Component } from "vue-property-decorator";
  @Component
  export default class Test extends Vue {
    uName:string = '张三'
    private age:number = 18
    private created():void {
      console.log(`Hello,${this.uName}`);
    }
  }
</script>

<style lang="less" scoped>
  .text{
    color:red
  }
</style>

方法

<template>
  <button @click="sum">{{count}}</button>
</template>

<script lang="ts">
  import { Vue, Component } from "vue-property-decorator";
  @Component
  export default class Test extends Vue {
    count:number=1
    private sum(){
      this.count++
    }
  }
</script>

<style lang="less" scoped>
  .text{
    color:red
  }
</style>

@Component 类装饰器

@Component({})可以声明components、filter、directives等未提供装饰器的vue选项,也可以声明computed、watch、路由守卫函数(beforeRouteEnter、beforeRouteLeave)等。简单的说,就是框架负责为类额外添加一些成员和功能,而开发者负责通过 注解 的方式 将数据传给框架,框架收到 注解 传入的数据后,可以用在类上。

<template>
  <div>
    <button @click="sum">{{count}}</button>
    <ComponentA/>
  </div>
</template>

<script lang="ts">
  import { Vue, Component } from "vue-property-decorator";
  import ComponentA from '@/component/ComponentA.vue'
  @Component({
    watch:{
      count(n){
        console.log(n);
      }
    },
    components:{
      ComponentA    
    }
  })
  export default class Test extends Vue {
    count:number=1
    private sum(){
      this.count++
    }
  }
</script>

<style lang="less" scoped>
  .text{
    color:red
  }
</style>

@Prop 

父子组件之间的属性传值,子组件接收父组件传参
@Prop接受一个参数可以是类型变量或者对象或者数组.@Prop接受的类型比如Number是JavaScript的类型,之后定义的属性类型则是TypeScript的类型

//父组件 Test.vue
<template>
  <div>
    <button @click="sum">{{count}}</button>
    <ComponentA :propA="propA" :propB="propB" :propC="propC"/>
  </div>
</template>

<script lang="ts">
  import { Vue, Component } from "vue-property-decorator";
  import ComponentA from '@/component/ComponentA.vue'
  @Component({
    watch:{
      count(n){
        console.log(n);
      }
    },
    components:{
      ComponentA    
    }
  })
  export default class Test extends Vue {
    count:number=1
    private propA:number=1
    private propB:boolean=true
    private propC:string='你好呀'
    private sum(){
      this.count++
    }
  }
</script>

<style lang="less" scoped>
  .text{
    color:red
  }
</style>

//子组件 ComponentA.vue
<template>
    <div>
        <div>{{str}}</div>
        <div>下面是父组件传来的值</div>
        <div>{{propA}}</div>
        <div>{{propB}}</div>
        <div>{{propC}}</div>
    </div>
</template>
<script lang="ts">
    import { Vue, Component, Prop } from "vue-property-decorator";
    @Component
    export default class ComponentA extends Vue{
        str:string='我是子组件'
        @Prop(Number) propA:number|undefined
        @Prop([String,Boolean]) propB!:string|boolean
        @Prop({
            default:'default value'
        }) propC!:string 
    }
</script>
<!--下面是不用装饰器的写法-->
<!--<script>
export default {
    props: {
        propNum: {
            type: Number
        },
        propStr: {
            default: 'default value'
        },
        propArr: {
            type: [String, Boolean]
        }
    },
    data(){
        return {
            str:'我是子组件'  
        } 
    },
}
</script>-->

需要注意的是:属性的ts类型后面需要加上undefined类型;或者在属性名后面加上!,表示非null 和 非undefined 的断言,否则编译器会给出错误提示。

@PropSync()

与 Prop 的区别是子组件可以对 props 进行更改, 并同步给父组件。
子组件 ComponentA:

<template>
  <div>
    <p>{{count}}</p>
    <button @click="innerCount += 1">increment</button>
  </div>
</template>

<script lang="ts">
import { Vue, Component, PropSync } from "vue-property-decorator";
@Component
export default class ComponentA extends Vue {
  @PropSync('count') private innerCount!: number // 注意@PropSync 里的参数不能与定义的实例属性同名, 因为 props 是只读的.
}
</script>

父组件:注意父组件里绑定 props 时需要加修饰符 .sync

<template>
    <ComponentA :count.sync="count"/>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import ComponentA from '@/component/ComponentA.vue'
@Component({
  components:{
    ComponentA
  }
})
export default class Test extends Vue {
  private count: number = 1
}
</script>

@Emit

定义emit事件,参数字符串表示分发的事件名,如果没有,则使用方法名作为分发事件名,会自动转连字符写法;

@Emit会将回调函数的返回值作为第二个参数,如果返回值为一个Promise对象,emit会在Promise-resolved后触发;

//父组件 Test.vue
<template>
  <div>
    <ComponentA @edit-promise="editHandle" @sum="editHandle" @editHandleEmit="editHandle" @returnValue="editHandle" />
  </div>
</template>

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import ComponentA from "@/component/ComponentA.vue";

@Component({
  components: {
    ComponentA
  },
})
export default class Test extends Vue {
  editHandle(count: any) {
    console.log(count,'====');
  }
}
</script>
//子组件 ComponentA.vue
<template>
  <div>
    <button @click="editHandle(1)">Click</button>
    <button @click="returnValue(2)">returnValue</button>
    <button @click="sum">sum</button>
    <button @click="editPromise">promise</button>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Emit } from "vue-property-decorator";

@Component
export default class ComponentA extends Vue {
  count: number = 0;
  //@Emit(name: string),里面传递一个字符串,该字符串为要触发的事件名
  @Emit("editHandleEmit")
  private editHandle(n:number) {
    this.count+=n
  }
  @Emit("returnValue")
  private returnValue(n:number) {
    return this.count+=n
  }
  //@Emit()不传参数,那么它触发的事件名就是它所修饰的函数名
  @Emit()
  sum() {
    return this.count
  }
  //这里@Emit()没有传参数名,editPromise 又是驼峰命名,所以,父组件用的时候要用中划线拼接形式 edit-promise
  @Emit()
  editPromise() {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}
</script>

@Watch 观察属性装饰器

@Watch使用非常简单,接受第一个参数为要监听的属性名 第二个属性为可选对象
{immediate?: boolean, deep?: boolean}第一个表示监听开始后是否立即调用回调函数,第二个表示监听的属性变化时是否调用回调函数

<template>
  <div>
    <button @click="sum">sum</button>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Watch } from "vue-property-decorator";

@Component
export default class ComponentA extends Vue {
  count: number = 0;
  sum() {
    this.count++
  }
  @Watch('count')
  countChange1(newVal:number, oldVal:number){
    console.log(newVal, oldVal);
  }
  //immediate:其值是true或false;immediate:true代表如果在 wacth 里声明了之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行
  @Watch('count', { immediate: true })
  countChange2(newVal:number, oldVal:number){
    console.log(newVal, oldVal);
  }
  //deep:其值是true或false;确认是否深入监听。deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器(受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除)
  @Watch('count', { deep: true })
  countChange3(newVal:number, oldVal:number){
    console.log(newVal, oldVal);
  }
}
</script>

计算属性

对于Vue中的计算属性,我们只需要将该计算属性名定义为一个函数,,在函数前加上get关键字即可,原本Vue中的computed里的每个计算属性都变成了在前缀添加get的函数。

<template>
  <div>
    <button @click="sum">sum</button>
    <div>{{computedMsg}}</div>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Watch } from "vue-property-decorator";

@Component
export default class ComponentA extends Vue {
  count: number = 0;
  sum() {
    this.count++
  }
  get computedMsg(){
    return this.count;
  }
  set computedMsg(count: number){
  }
}
</script>

@Provide 和@Inject

@Provide()、@Inject()提供了父子组件、多层嵌套组件以及兄弟组件数据传递的方法。

@Provide():父组件中通过Provide传递数据;

@Inject():子组件中通过Inject获取数据;

<template>
  <div>
    <div>{{desc}}</div>
    <ComponentA/>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Provide } from "vue-property-decorator";
import ComponentA from "@/component/ComponentA.vue";

@Component({
  components: {
    ComponentA
  },
})
export default class Test extends Vue {
  desc:string="我是父组件"
  //父组件中通过Provide传递数据,str1、provideStr2是定义的要传递的变量
  //如果@Provide()没有传参,则要传给子组件的变量就是@Provide()后面定义的变量名
  @Provide() str1:string = '你好呀!';
  //如果@Provide()有传参,则要传给子组件的变量就是@Provide()中传入的参数的变量名
  @Provide('provideStr2') private str2:boolean = true;
}
</script>
<template>
  <div>
    <div>{{str}}</div>
    <div>{{str1}}</div>
    <div>{{str3}}</div>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Inject } from "vue-property-decorator";

@Component
export default class ComponentA extends Vue {
  str:string="我是子组件"
  //子组件中通过Inject获取数据
  // str1后面加 “!”表示,str1一定有值
  // @Inject()不传参表示接受的变量名和传递的变量名一样
  @Inject() private str1!: string;
  // 如不确定provideStr2是否一定有值,可以加上 “|undefined”,这样就不用加 “!”
  // @Inject()传参表示接受的变量名和传递的变量名不一样,@Inject()后面定义的变量为接收数据的变量
  @Inject('provideStr2') str3: boolean|undefined;
}
</script>
Logo

前往低代码交流专区

更多推荐