Vue2.x API的Typescript常用写法

Vue中Typescript常用写法

[TOC]

使用Ts的原因

js是一门弱类型、动态类型的语言,存在类型隐式转换、运行时报错等难以预知的问题;而ts是静态类型、强类型语言,编译时即可报错,类型检查更为严格;使开发更为严谨,错误更易发现;其次,接口的定义以及类型的定义更利于团队中代码的维护,也便于代码的阅读

待添加…

创建项目

使用vue-cli3.x创建项目,选择typescript配置项,及class写法

项目迁移

依赖项

npm install @vue/cli-plugin-typescript typescript vue-class-component vue-property-decorator

添加tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

添加对.vue文件的ts声明

1
2
3
4
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}

vue2.x迁移 待添加…

API迁移

主要写法参考

vue-property-decorator

vue-class-component

vue官网

es6装饰器

demo

https://github.com/lijing233/vue_ts_demo.git

全局生命文件

在cli生成项目中含有一个shims-vue.d.ts文件,因为在ts文件中是无法识别vue文件的,所以添加声明让ts能够识别

1
2
3
4
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}

.vue文件写法

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<template>
<div class="container">
<div>data: {{ myMsg }}</div>
<div>{{ computedMsg }}</div>
<div>props: {{ propMessage }}</div>
<div><input type="text" v-model="inpVal" /></div>
<div><button @click="onButtonClick">click</button></div>
<div>.sync绑定:{{ syncData }}</div>
<div>子组件v-model: {{ modelData }}</div>
<div>emit val {{ sonVal }}</div>
<div><button @click="activeRef">active ref</button></div>
<div>mixin: {{ mixinValue }}</div>
<div>测试: {{ name }}</div>
<SonComp
:syncData.sync="syncData"
v-model="modelData"
@sonChange="sonChange"
ref="sonComp"
></SonComp>
</div>
</template>

<script lang="ts">
import {
Component,
Prop,
Vue,
Watch,
Provide,
Ref
} from "vue-property-decorator";
import SonComp from "./SonComp.vue";
import MyMixin from "@/mixin/mixin.ts";
@Component({
name: "Test",
props: {
propMessage: {
type: String,
default: "Props"
}
},
components: {
SonComp
}
})
export default class extends MyMixin {
// ref
@Ref("sonComp") readonly sonRef!: SonComp;
// data数据
private myMsg = "DataMsg";
syncData = "syncData";
inpVal = "";
modelData = "";
sonVal = "";
// 两种写法均可
data() {
return {
name: "lijing"
};
}
// computed
get computedMsg() {
return "computed:" + this.myMsg;
}
// watch
@Watch("inpVal", { immediate: true, deep: true })
onInpValChange(val: String, oldVal: String) {
console.log("val :", val);
console.log("oldVal :", oldVal);
}

// provid
@Provide() providData = "pppp";
@Provide() providData2 = "dddd";

// lifecycle
created() {
console.log("=====created=====");
}

mounted() {
// 全局属性 需要定义
console.log("globalVal: ", this.$globalVal);
console.log("=====mounted=====");
}

// methods
onButtonClick() {
console.log("click");
}

sonChange(val: any) {
this.sonVal = val;
}
// active ref
activeRef() {
this.sonRef.refActFun();
}

// Hook
beforeRouteEnter(to: any, from: any, next: any) {
console.log("beforeRouteEnter");
next(); // needs to be called to confirm the navigation
}
}
</script>

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

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<template>
<div class="container">
<div>SonComp</div>
<div>inject {{ providData }}</div>
<div>inject {{ pdata }}</div>
<div>binding .sync : <input type="text" v-model="syncedName" /></div>
<div>
binding v-model : {{ modelData }}
<button @click="$emit('onModelChange', modelData + '@')">
change modelData
</button>
</div>
<div><input type="text" @input="inpChange" /></div>
</div>
</template>

<script lang="ts">
import {
Component,
Prop,
Vue,
Watch,
Inject,
PropSync,
Model,
Emit
} from "vue-property-decorator";
@Component({
name: "SonComp"
})
export default class extends Vue {
// propSync
@PropSync("syncData", { type: String }) syncedName!: string;
// model
@Model("onModelChange", { type: String }) readonly modelData!: String;
// inject
@Inject("providData") readonly providData!: String;
@Inject({ from: "providData2", default: "default provid data" })
readonly pdata!: String;
// emit
@Emit("sonChange")
inpChange(e: any) {
console.log(e.target.value);
return e.target.value;
}
// method
refActFun() {
console.log("REF active");
}
}
</script>

<style lang="scss" scoped>
.container {
margin: 30px;
padding: 30px;
border: 1px solid #dddded;
}
</style>

mixin

mixin.ts

1
2
3
4
5
6
import Vue from "vue";
import Component from "vue-class-component";

export default class MyMixin extends Vue {
mixinValue = "###mixinValue###";
}

index.vue

1
2
3
4
5
import MyMixin from "@/mixin/mixin.ts";
@Component({
name: "Test",
})
export default class extends MyMixin {}

Router Hooks

如要使用路由钩子 beforeRouteEnter 、beforeRouteLeave、beforeRouteUpdate需要在入口注册,否则将被认为是普通函数

class-component-hooks.js

1
2
3
4
5
6
7
8
9
// class-component-hooks.js
import Component from "vue-class-component";

// Register the router hooks with their names
Component.registerHooks([
"beforeRouteEnter",
"beforeRouteLeave",
"beforeRouteUpdate" // for vue-router 2.2+
]);

main.ts

1
import './utils/class-component-hooks'

挂载在vue原型上的属性或方法

对于一些全局配置或封装的ajax方法,通常会挂载到vue的原型上,但在ts中必须提前声明再使用

src/types/vued.ts

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 确保在声明补充的类型之前导入 'vue'
import Vue from 'vue'

// 2. 定制一个文件,设置你想要补充的类型
// 在 types/vue.d.ts 里 Vue 有构造函数类型
// node_modules/vue/types/vue
declare module 'vue/types/vue' {
// 3. 声明为 Vue 补充的东西
interface Vue {
$globalVal: string
}
}

main.ts

1
Vue.prototype.$globalVal = "999"

vuex 迁移

vuex-module-decorators

vuex-class

待添加…