在Vue中使用TypeScript

1,239 阅读3分钟

在Vue中使用JavaScript

<script>
  export default {
    name: 'Types',
    props: ['xxx'],    //接收外部属性xxx
    data() {           //声明data
      return {
        type: '-'
      }
    },
    mounted() {
      console.log(this.xxx)
    },
    methods: {         //方法
      selectType(type) {  
        if (type !== '-' && type !== '+') {
          throw new Error('type is unknown')
        }
        this.type = type
      }
    }
  }
</script>

使用TypeScript(与JavaScript最大的区别是不用构造选项,构造选项没有类型,TS必须使用class)

npm i -S vue-property-decorator                    //安装 vue-property-decorator 

<script lang="ts">
import Vue from 'vue';
import {Component} from 'vue-property-decorator'; //从vue-property-decorator引入Component装饰器

@Component                        //使用装饰器,作用告诉TypeScript这个东西是vue组件
export default class Types extends Vue {      //导出一个class,名字是组件名,继承 Vue
    type = '-';                  //声明data,语法:任何的赋值语句,会变成实例的属性
    selectType(type: string) {   //methods方法,直接写函数就行,要声明data的类型             
      if (type !== '-' && type !== '+') {
        throw  new Error('type is unknown');
      }
      this.type = type;
    }
  }
</script>

总结
从vue-property-decorator 库里引入 Component 装饰器
把装饰器修饰到class上,作用是(type会自动处理为data,selectType处理为methods,按组件进行处理)
class可以声明 data methods 声明周期等等,注意data的类型

@Prop装饰器(告诉class这个类,后面这个属性不是data,不写@Prop就是data,写就是prop)

@Prop(options: (PropOptions | Constructor[] | Constructor) = {}) decorator

import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
  @Prop(Number) readonly propA: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) readonly propC: string | boolean | undefined
}

propA                //属性名
Number               //propA运行时的类型
number | undefined;  //propA编译时的类型
readonly             //只读的,不能修改
this.propA           //使用prop

声明prop时如果写了类型为undefined,在其它任何地方使用prop时,必须检查是不是undefined,否则会编译错误
mounted(){
  if(this.propA === undefined){
    console.log('undefined')
  }else{
    console.log(this.propA)
  }
}

总结如何使用TypeScript声明一个vue组件
import {ComponentProp} from 'vue-property-decorator';     //引入装饰器
@Component                                                  //加上装饰器
export default class YourComponent extends Vue              //声明一个继承vue的类
type = '-';                                                 //声明data直接写
@Prop(Number) xxx: number | undefined;                      //声明prop,运行和编译时的类型

@PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {}) decorator

@PropSync装饰器与@prop用法类似,区别在于:@PropSync 会生成一个新的计算属性
@PropSync接收两个参数:
propName: string                                   // 表示父组件传递过来的属性名
options: Constructor | Constructor[] | PropOptions //与@Prop的第一个参数一致
                      
import { Vue, Component, PropSync } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
  @PropSync('name', { type: String }) syncedName!: string
}

@PropSync需要配合父组件的.sync修饰符使用

@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {}) decorator

@Model装饰器允许我们在一个组件上自定义v-model,接收两个参数:
event: string                                      // 事件名
options: Constructor | Constructor[] | PropOptions //与@Prop的第一个参数一致

import { Vue, Component, Model } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
  @Model('change', { type: Boolean }) readonly checked!: boolean
}

@Watch(path: string, options: WatchOptions = {})

@Watch 装饰器接收两个参数:
path: string              //被侦听的属性名
options                   //包含两个属性(immediate,deep)
immediate?:boolean        //侦听开始之后是否立即调用该回调函数
deep?:boolean             //被侦听的对象的属性被改变时,是否调用该回调函数

侦听开始,发生在beforeCreate勾子之后,created勾子之前
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) {}
}

@Emit(event?: string)

@Emit装饰器接收一个可选参数,该参数是$Emit的第一个参数,充当事件名
如果没有提供这个参数,$Emit会将回调函数名的camelCase转为kebab-case,并将其作为事件名

@Emit会将回调函数的返回值作为第二个参数,如果返回值是一个Promise对象
$emit会在Promise对象被标记为resolved之后触发

@Emit的回调函数的参数,会放在其返回值之后,一起被$emit当做参数使用

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)
    })
  }
}

@Ref(refKey?: string)

@Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息
如果没有提供这个参数,会使用装饰器后面的属性名充当参数

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
}