Skip to content

Vue2 中使用 TS

基本结构

html
<template>
   
  <div id="app">   <router-view />  </div>
</template>

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

<style lang="scss"></style>

script 标签

html
<!--在vue文件中使用,需要在script标签上添加属性lang=“ts”,声明使用的是ts-->

<script lang="ts"></script>

生命周期函数

保持不变,只是函数之间不需要逗号分隔

html
<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    created() {}
    mounted() {}
  }
</script>

data 数据

直接使用等号即可

html
<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    firstName: string = "George";
    count: number = 1;
    loading: boolean = false;
    list: any = [];
  }
</script>

methods 方法

不需要用 methods 选项包裹了,直接提出来,方法之间不需要使用逗号分隔(注意不要使用生命周期的钩子的名称)

html
<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    sum(x: number, y: number): number {
      return x + y;
    }
  }
</script>

@watch 监听

  • 方法名称可以随意取
html
<script lang="ts">
  import { Component, Vue, Watch } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    @Watch("count")
    onWatchCount(newVal: number, oldVal: number) {
      console.log(newVal, oldVal);
    }
    @Watch("list", { deep: true, immediate: false })
    onHandleList(newVal: any, oldVal: any) {
      console.log(newVal, oldVal);
    }
  }
</script>

computed 计算属性

在函数前加上 get 关键字即可

html
<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    get computedCount(): string {
      return "计算属性" + this.count;
    }
  }
</script>

@Component

html
<template>  <ChildCompont></ChildCompont> </template>

<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  import ChildCompont from "./components/ChildCompont.vue"; // 引入子组件
  @Component({
    name: "test", // 当前组件的name
    components: {
      childCompont, // 子组件
    },
  })
  export default class Test extends Vue {}
</script>

@Prop

html
// Test.vue ​
<template>  <ChildCompont :propName="firstName"></ChildCompont> </template>
<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    firstName: string = "George";
  }
</script>
​ ​ // ChildCompont.vue ​
<template> </template>
<script lang="ts">
  import { Component, Vue, Prop } from "vue-property-decorator";
  @Component({
    name: "childCompont",
  })
  export default class ChildCompont extends Vue {
    @Prop({
      type: String,
      default: "George",
      validator: (str: any): boolean => {
        return str.length < 6;
      },
      required: true,
    }) // Prop对象里面的配置和原来的一样,类型、默认值、校验函数和是否必须,可写可不写,按需求去写
    propName!: string; //!表示非空值,否则ts初始化会报错
  }
</script>

@PropSync

  • 与 Prop 的区别是子组件可以对 props 进行更改,并且同步给父组件
html
// Test.vue
<template>  <ChildCompont :propCount.sync="count"></ChildCompont> </template>
<script lang="ts">
  import { Component, Vue } from "vue-property-decorator";
  @Component({
    name: "test",
  })
  export default class Test extends Vue {
    count: number = 1;
  }
</script>
​ ​ // ChildCompont.vue
<template>
   
  <div>
       
    <p>{{propCount}}</p>
       <button @click="innerCount += 1">点击加1</button>  
  </div>
</template>

<script lang="ts">
  import { Component, Vue, PropSync } from "vue-property-decorator";
  @Component({
    name: "childCompont",
  })
  export default class ChildCompont extends Vue {
    @PropSync("propCount") innerCount!: number; //在改变innerCount的时候,相当于会执行this.$emit('update:count', value)
  }
</script>

@Ref

@Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息。

如果没有提供这个参数,会使用装饰器后面的属性名充当参数

html
<template>
   
  <div>
       <el-form ref="formRef"> ​    </el-form>    <ChildCompont
      ref="ChildCompontRef"
      :propName="firstName"
      :propCount.sync="count"
    ></ChildCompont>
     
  </div>
</template>

<script lang="ts">
  import { Component, Vue, Ref } from "vue-property-decorator";
  import ChildCompont from "./components/ChildCompont.vue";
  import type { Form } from "element-ui"; // 引入类型
  @Component({
    name: "test",
    components: {
      ChildCompont,
    },
  })
  export default class Test extends Vue {
    @Ref() readonly ChildCompontRef!: ChildCompont;
    @Ref() readonly formRef!: Form; //这时就可以使用el-from上面的方法了,有代码提示
  }
</script>

@Emit

定义 emit 事件,参数字符串表示分发的事件名,如果没有,则使用方法名作为分发事件名:

  1. @Emit()不传参数,会将回调函数名的 camelCase 转为 kebab-case,并将其作为事件名

  2. @Emit(name: string),里面传递一个字符串,该字符串为要触发的事件名

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

  @Component
  export default class ChildCompont 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);
      });
    }
  }
</script>

相当于

html
<script>
  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);
        });
      },
    },
  };
</script>

@Provide & @Inject

父组件

html
<script lang="ts">
  import { Component, Vue, Provide } from "vue-property-decorator";
  import ChildCompont from "./components/ChildCompont.vue";
  const symbol = Symbol("baz");
  @Component({
    name: "test",
    components: {
      ChildCompont,
    },
  })
  export default class Test extends Vue {
    @Provide() foo = "foo";
    @Provide("bar") baz = "bar";
  }
</script>

后代组件

html
<script lang="ts">
  import { Component, Vue, Inject } from "vue-property-decorator";
  @Component({
    name: "childCompont",
  })
  export default class ChildCompont extends Vue {
    @Inject() readonly foo!: string; //parentFoo
    @Inject("bar") readonly bar!: string; // parentBar
    @Inject({ from: "optional", default: "default" })
    readonly optional!: string; // default
    @Inject(symbol) readonly baz!: string; //undefined
  }
</script>

@ProvideReactive & @InjectReactive

和@Provide And @Inject 的区别是,父组件修改了提供的值,子组件是可以捕获到修改的

父组件

html
<script lang="ts">
  import { Component, Vue, ProvideReactive } from 'vue-property-decorator';
  import ChildCompont from './components/ChildCompont.vue'
  const key = Symbol('key')
  @Component({
    name: 'test',
    components: {
      ChildCompont
    }
  })

  export default class Test extends Vue {
    @ProvideReactive() one = 'value1'
    @ProvideReactive(key) two = 'value2''
  }
</script>

后代组件

html
<script lang="ts">
  import { Component, Vue, InjectReactive } from "vue-property-decorator";
  @Component({
    name: "childCompont",
  })
  export default class ChildCompont extends Vue {
    @InjectReactive() one!: string; //value1
    @InjectReactive(key) two!: string; // value2
  }
</script>

@Mixins

在使用 Vue 进行开发时我们经常要用到混合,结合 TypeScript 之后我们有两种 mixins 的方法.

一种是 vue-class-component 提供的.

js
//定义要混合的类 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/type/vue 模块,实现 Vue 接口

bash
// 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'
}

混入

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

两种方式不同的是在定义 mixins 时,如果没有定义 vue/type/vue 模块, 那么在混入的时候就要继承该 mixins; 如果定义 vue/type/vue 模块,在混入时可以在@Component 中 mixins 直接混入.

状态管理

在 js 版本中,我们一般使用 vuex 来做状态管理,ts 中,我们可用vuex-module-decorators做状态管理

js
import { Module, VuexModule, Mutation, Action, getModule } from 'vuex-module-decorators'
import store from './index'
// 动态注册模块设置,跟js类似
@Module({ 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)


// 在组件中使用
<template>
  <p @click="add">{{$store.state.counter.count}}</p>
    <p @click="asyncAdd">{{count}}</p>
</template>

<script lang="ts">
  import CounterModule from '@/store/counter'
  @Component
  export default class Home extends Vue {
    get count() {
      return CounterModule.count
    }
    add() { // 相当于js版本中 this.$store.commit('count/add')
      CounterModule.add()
    }
    asyncAdd() { // 相当于js版本中 this.$store.dispatch('count/add')
      CounterModule.asyncAdd()
    }
  }
</script>