Vue Property Decorator

Github 地址:Vue Property Decorator

Vue Property Decorator提供了一些装饰器,包括@Prop、@Watch、@Emit、@Inject、@Provide、@Model等等。这些装饰器可以帮助你更方便地定义组件的属性、监听属性的变化、触发事件、注入依赖、提供依赖等等。

下载

npm i -S vue-property-decorator

使用

Prop

使用 Prop 可以快速地让你设置你的传入属性。

比如下面 propA,可以设置为只读,number 类型,下面的 propB,可以设置默认值

import { Vue, Component, Prop } from 'vue-property-decorator';  
@Component  
export default class MyComponent extends Vue {  
 @Prop(Number) readonly propA!: number;  
 @Prop({ default: 'default value' }) readonly propB!: string; 
 // 这行代码使用了@Prop装饰器来定义一个名为propC的属性,并指定了它的类型为string或boolean。
 // [String, Boolean]是一个数组,表示propC可以是string类型或boolean类型。readonly表示这个属性是只读的,不能在组件内部修改它的值。
 // propC: string | boolean
 // 表示这个属性的类型是string或boolean,也就是说,它的值可以是string类型或boolean类型。
 // 这样定义propC的好处是,当propC被传入组件时,Vue会自动根据它的类型进行类型检查,确保它的值符合预期。
 @Prop([String, Boolean]) readonly propC: string | boolean;  
}  

等同于:

export default {
  props: {
    propA: {
      type: Number,
    },
    propB: {
      default: 'default value',
    },
    propC: {
      type: [String, Boolean],
    },
  },
}

Watch

@Watch 装饰器可以用于类中的方法上,用于监听指定的数据变化。当被监听的数据发生变化时,这个方法就会被调用,并且会传入两个参数:新值和旧值。

例如,我们可以使用 @Watch 装饰器来监听 child 这个属性的变化,如下所示:

import { Vue, Component, Watch } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
  @Watch('child')
  onChildChanged(val: string, oldVal: string) {}
  @Watch('person', { immediate: true, deep: true })
  onPersonChanged1(val: Person, oldVal: Person) {}
  @Watch('person')
  onPersonChanged2(val: Person, oldVal: Person) {}
  @Watch('person')
  @Watch('child')
  onPersonAndChildChanged() {}
}

相当于:

export default {
  watch: {
    child: [
      {
        handler: 'onChildChanged',
        immediate: false,
        deep: false,
      },
      {
        handler: 'onPersonAndChildChanged',
        immediate: false,
        deep: false,
      },
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true,
        deep: true,
      },
      {
        handler: 'onPersonChanged2',
        immediate: false,
        deep: false,
      },
      {
        handler: 'onPersonAndChildChanged',
        immediate: false,
        deep: false,
      },
    ],
  },
  methods: {
    onChildChanged(val, oldVal) {},
    onPersonChanged1(val, oldVal) {},
    onPersonChanged2(val, oldVal) {},
    onPersonAndChildChanged() {},
  },
}

Emit

当一个 Vue 组件需要与其它组件进行通信时,可以使用 emit 方法来触发自定义事件。emit 方法接收两个参数:第一个参数是自定义事件的名称,第二个参数是传递给父组件的数据。

父组件可以通过 v-on 指令监听这个自定义事件,并且在父组件中定义一个方法来处理这个事件。在 vue-property-decorator 中,可以使用 @Emit 装饰器来定义组件的自定义事件。@Emit 装饰器可以用于方法上,将这个方法标记为组件的自定义事件。在这个方法中,可以通过 return 语句来返回需要传递给父组件的数据。例如:

import { Component, Vue, Emit } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
  @Emit()
  handleClick() {
    return 'hello';
  }
}

在这个例子中,我们定义了一个名为 handleClick 的方法,并且使用 @Emit 装饰器将它标记为组件的自定义事件。当这个方法被调用时,它会返回一个字符串 'hello',这个字符串会被传递给父组件。在父组件中,可以使用 v-on 指令来监听这个自定义事件,并且在父组件中定义一个方法来处理这个事件。例如:

<template>
  <div>
    <my-component @click="handleClick"></my-component>
  </div>
</template>
<script>
import MyComponent from './MyComponent.vue';
export default {
  components: {
    MyComponent,
  },
  methods: {
    handleClick(data) {
      console.log(data); // 输出 'hello'
    },
  },
};
</script>

在这个例子中,我们在父组件中使用 @click 指令来监听 MyComponent 组件的自定义事件,并且在父组件中定义了一个名为 handleClick 的方法来处理这个事件。当 MyComponent 组件触发自定义事件时,这个事件会被传递给父组件的 handleClick 方法,并且传递的数据是 'hello'。

更普遍的用法:

import { Vue, Component, Emit } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
  count = 0
  @Emit()
  addToCount(n: number) {
    this.count += n
  }
  @Emit('reset')
  resetCount() {
    this.count = 0
  }
  @Emit()
  returnValue() {
    return 10
  }
  @Emit()
  onInputChange(e) {
    return e.target.value
  }
  @Emit()
  promise() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}

上述写法等同于:

export default {
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    addToCount(n) {
      this.count += n
      this.$emit('add-to-count', n)
    },
    resetCount() {
      this.count = 0
      this.$emit('reset')
    },
    returnValue() {
      this.$emit('return-value', 10)
    },
    onInputChange(e) {
      this.$emit('on-input-change', e.target.value, e)
    },
    promise() {
      const promise = new Promise((resolve) => {
        setTimeout(() => {
          resolve(20)
        }, 0)
      })
      promise.then((value) => {
        this.$emit('promise', value)
      })
    },
  },
}

Ref

@Ref 是用来获取其他组件的 Ref 的,比如我们像如下使用,对于第一个 @Ref() readonly anotherComponent!: AnotherComponent,我们的含义是这个ref是只读的,并且是从this.$refsanotherComponet上拿到的值,而@Ref('aButton') readonly button!: HTMLButtonElement,则是制定了我们的this.$refs后面的访问值为aButton

例子:

import { Vue, Component, Ref } from 'vue-property-decorator'
import AnotherComponent from '@/path/to/another-component.vue'
@Component
export default class YourComponent extends Vue {
  @Ref() readonly anotherComponent!: AnotherComponent
  @Ref('aButton') readonly button!: HTMLButtonElement
}

相当于

export default {
  computed() {
    anotherComponent: {
      cache: false,
      get() {
        return this.$refs.anotherComponent as AnotherComponent
      }
    },
    button: {
      cache: false,
      get() {
        return this.$refs.aButton as HTMLButtonElement
      }
    }
  }
}