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 事件,参数字符串表示分发的事件名,如果没有,则使用方法名作为分发事件名:
@Emit()不传参数,会将回调函数名的 camelCase 转为 kebab-case,并将其作为事件名
@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>