vue ts装饰器应用

220 阅读2分钟

声明文件

使用ts开发时如果要使用第三方js库的同时还想利用ts诸如类型检查等特性就需要声明文件,类似 xx.d.ts

同时,vue项目中还可以在shims-vue.d.ts中对已存在模块进行补充


npm i @types/xxx
//范例:利用模块补充$axios属性到Vue实例,从而在组件里面直接用
// main.ts
import axios from 'axios'
Vue.prototype.$axios = axios;

// shims-vue.d.ts
import Vue from "vue";
import { AxiosInstance } from "axios";
declare module "vue/types/vue" {
    interface Vue {
        $axios: AxiosInstance;
    }
}

范例:给krouter/index.js编写声明文件,index.d.ts
import VueRouter from "vue-router";
declare const router: VueRouter
export default router

装饰器 vue的应用

装饰器用于扩展类或者它的属性和方法。@xxx就是装饰器的写法

# 状态管理推荐使用:vuex-module-decorators
vuex-module-decorators 通过装饰器提供模块化声明vuex模块的方法,可以有效利用ts的类型系统。

#安装
npm i vuex-module-decorators -D

@Prop

原始写法

export default {
  props: {
    title: {
      type: String,
      required: true
    },
    count: {
      type: Number,
      default: 0
    }
  }
}

@Prop写法

import { Component, Vue, Prop } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
  @Prop({ type: String, required: true }) readonly title!: string;
  @Prop({ type: Number, default: 0 }) readonly count!: number;
} 

@Emit

原始

export default {
  methods: {
    emitSubmit(payload) {
      this.$emit('submit', payload);
    }
  }
} 

@Emit写法

import { Component, Vue, Emit } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  @Emit('submit')
  emitSubmit(payload: string) {
    return payload;
  }
}
  • 通知父类新增事件,若未指定事件名则函数名作为事件名
  • 若没有返回值形参将作为事件参数
  • 若有返回值则返回值作为事件参数

@watch

原始

export default {
  data() {
    return {
      message: ''
    };
  },
  watch: {
    message(newVal, oldVal) {
      console.log(`Message changed from ${oldVal} to ${newVal}`);
    }
  }
}

@Watch写法

import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  message: string = '';

  @Watch('message')
  onMessageChanged(newVal: string, oldVal: string) {
    console.log(`Message changed from ${oldVal} to ${newVal}`);
  }
}

@component

原始

export default {
  // 组件逻辑
} 

@component

import { Component, Vue } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  // 组件逻辑
 mounted() {
   //...
  }
}

为什么还要继承Vue, 因为TS需要强类型判断,会找不到 mounted等方法报错

Vuex

//根模块清空,修改store/index.ts
export default new Vuex.Store({})

//定义counter模块,创建store/counter.ts
import { Module, VuexModule, Mutation, Action, getModule } from 'vuex-module-decorators'
import store from './index'
// 动态注册模块
@Module({ dynamic: true, store: store, name: 'counter', namespaced: true })
class CounterModule extends VuexModule {
    count = 1
    @Mutation
    add() {
        // 通过this直接访问count
        this.count++
    }
    // 定义getters
    get doubleCount() {
        return this.count * 2;
    }
    @Action
    asyncAdd() {
        setTimeout(() => {
            // 通过this直接访问add
            this.add()
        }, 1000);
    }
} // 导出模块应该是getModule的结果
export default getModule(CounterModule)

//使用,App.vue
<p @click="add" > {{ $store.state.counter.count }}</p >
<p @click="asyncAdd" > {{ count }}</p >
<script>
import CounterModule from '@/store/counter'
@Component
export default class App extends Vue {
    get count() {
        return CounterModule.count
    } a
    dd() {
        CounterModule.add()
    } a
    syncAdd() {
        CounterModule.asyncAdd()
    }
}
</script>

当然也可以使用vuex-class实现

@Component源码实现

<template >
    <div>{{ msg }}</div>
</template >
<script lang='ts'>
import { Vue } from "vue-property-decorator";
function Component(options: any) {
    return function(target: any) {
        return Vue.extend(options);
    };
} 
 @Component({
  props: {
    msg: {
    type: String,
    default: ""
  }
}
})
export default class Decor extends Vue {}
</script>