前言

离上一篇博客又过去了差不多10天的样子,然后上次里的flag又完美打脸了,主要是最近项目比较繁忙,所以也莫得时间整理,直到今天似乎闲了一点,于是决定随缘写篇博客。那就是分享在vue项目中使用ts的一些心得,这篇文章会长期不定期更新。

项目起手

从头配置ts项目比较困难,在这里我们可以使用vue脚手架自带的ts可选项,只要在新建项目时勾选ts,脚手架就可以自动帮我们完成相关配置,非常方便。

然后一路回车,等待几分钟,项目就好了。

这里会介绍vue中如何简单使用ts,想了解更多关于ts的知识,可以选择看看官方文档,当然,我其实挺觉得这个文档不是那么好懂,所以我推荐看这个入门,学习了更多TS的骚操作,你就可以玩的更Happy了。

下面先来讲讲一些常用功能的TS写法

组件写法

如图,组件的写法不再是导出一个对象,而是导出一个类,注意事项有两个,一个是要在类的上面使用Component装饰器装饰这个类,另一个是这个类要继承于Vue,这样就一个组件的声明就大功告成啦。接着我们来看看我们一些常用功能的写法。

数据

声明数据非常简单,你只需要使用下面的方法声明即可

1
2
3
4
5
6
export default class Parent extends Vue {
// 声明数据
name = SakuraSnow;
age = 19;
}

该代码等效于

1
2
3
4
5
6
7
8
9
export default {
date() {
return {
name : SakuraSnow,
age : 19
}
}
}

当然这样就体现不出ts的优势了,你可以加点别的东西,比如数据类型和权限,

1
2
3
4
5
6
export default class Parent extends Vue {
// 声明数据
private readonly name = SakuraSnow;
private readonly age = 19;
}

加上private和readonly后,这个属性就变成了私有属性,外部访问会报错,同时是只读的,想要修改它的值也会报错。(这里的报错均是指静态类型检查报错,要访问实际上还是可以访问的,要修改实际上也是可以修改的)

属性

声明会接收的属性需要使用Prop装饰器(从vue-property-decorator导入),可以传入一些配置项

1
2
3
4
5
6
7
8
9
10
11
export default class Parent extends Vue {
// 声明会接收的属性
@Prop({
default() {
return this is some message;
},
required : false
})
message : string | undefined;
}

以上代码等效于

1
2
3
4
5
6
7
8
9
10
11
export default {
props : {
message : {
default() {
return this is some message;
},
require : false
}
}
}

函数

直接把函数定义在类上即可

1
2
3
4
5
6
7
export default class Parent extends Vue {
// 声明函数
handleClick() {
console.log(被点击了);
}
}

该代码等效于

1
2
3
4
5
6
7
8
export default {
methods : {
handleClick() {
console.log(被点击了)
}
}
}

计算属性

计算属性需要定义成一个访问器属性来使用,这不是TS的特有语法,在ES6的类中就已经出现。

1
2
3
4
5
6
7
8
9
10
export default class Parent extends Vue {
private age = 19;
// 声明一个计算属性
get doubleAge() {
return this.age * 2;
}
set doubleAge(val) {
this.age = val / 2;
}
}

该代码等效于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default {
data() {
return {
age : 16
}
},
computed : {
doubleAge : {
get() {
return this.age;
},
set(val) {
this.age = val / 2;
}
}
}
}

Watch

Watch需要使用Watch装饰器,同样要从 vue-property-decorator 导入

1
2
3
4
5
6
7
8
9
10
export default class Parent extends Vue {
private age = 19;
@Watch(age, {
immediate : true
})
handleAgeUpdate(newVal : number, oldVal : number) {
console.log(age的值更新了:, newVal, oldVal);
}
}

该代码等效于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default {
data() {
return {
age : 16
}
},
watch : {
age : {
handler(newVal, oldVal) {
console.log(age的值更新了:, newVal, oldVal);
},
immediate : true
}
}
}

生命周期

直接定义方法即可

1
2
3
4
5
6
export default class Parent extends Vue {
mounted() {
console.log(挂载完成);
}
}

等效于

1
2
3
4
5
6
export default {
mounted() {
console.log(挂载完成);
}
}

想了解更多写法,点击这里查看文档]

vuex

首先我们需要先安装vuex-class

1
npm install vuex-class --save

不是module时

配置vuex

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
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name : 'sena',
age : 16
},
getters : {
userInfo (state) {
return {
name : state.name,
age : state.age
}
}
},
mutations: {
updateName(state, payload) {
state.name = payload;
}
},
actions: {
async updateNameAsync({commit}, payload) {
setTimeout(() => {
commit("updateName", payload)
}, 3000)
}
},
})

在App.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
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
import {State, Getter, Action, Mutation, namespace} from 'vuex-class'

@Component({})
export default class App extends Vue {

// root
@State('name') vuexName : string;
@State(state => state.age) vuexAge : number;
@Getter('userInfo') vuexUserInfo : any;
@Mutation('updateName') updateVuexName;
@Action('updateNameAsync') updateVuexNameAsync;

mounted() {
// root
console.log(this.vuexName);
console.log(this.vuexAge);
console.log(this.vuexUserInfo);

// root
setTimeout(() => {
// commit
console.log("修改name");
this.updateVuexName("sakura");
// dispatch
console.log("4s后修改name");
this.updateVuexNameAsync("snow");
}, 1000);

}

@Watch("vuexName")
watchVuexName(newVal) {
console.log(`newVuexName更新为: ${newVal}`)
}

}

运行结果
file

使用module时

配置vuex

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
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
modules: {
app : {
namespaced : true,
state : {
appName : "app",
a : 1,
b : 2
},
getters : {
appSum(state) {
return state.a + state.b;
}
},
mutations : {
updateAppName(state, payload) {
state.appName = payload;
}
},
actions : {
updateAppNameAsync({commit}, payload) {
setTimeout(() => {
commit("updateAppName", payload);
}, 3000)
}
}
}
}
})

在App.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
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
import {State, Getter, Action, Mutation, namespace} from 'vuex-class'
const AppModule = namespace("app");
@Component({})
export default class App extends Vue {

// app module
@AppModule.State("appName") vuexAppName : string;
@AppModule.Getter("appSum") vuexAppSum : number;
@AppModule.Mutation("updateAppName") vuexUpdateAppName;
@AppModule.Action("updateAppNameAsync") vuexUpdateAppNameAsync;

mounted() {

// app module
console.log(this.vuexAppName);
console.log(this.vuexAppSum);

// app module
setTimeout(() => {
// commit
console.log("修改appName");
this.vuexUpdateAppName("sakura");
// dispatch
console.log("4s后修改appName");
this.vuexUpdateAppNameAsync("snow");
}, 1000);

}

@Watch("vuexAppName")
watchVuexAppName(newVal) {
console.log(`newVuexAppName更新为: ${newVal}`)
}

}

运行结果
file

使用时的一些坑

导入图片报错


当你想引入某个本地图片时,发现ts会报个不能找到模块的错误,这是因为ts没有把图片识别成一个模块,所以需要一点声明,你可以在src下随便找个目录建个文件,后缀是.d.ts然后加上下面的代码

1
2
3
4
5
6
7
8
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';


然后就不会报错了

在vue实例上挂自定义属性时会报错


在prototype上放了个$bus用作事件总线,然后使用时就会报错


没错,还是因为没有声明,后来我去element-ui的库里找到了它的声明文件,然后复制改了一下,就解决了这个问题。

同理随便建一个d.ts文件,然后导出一个声明,在上面定义你要的属性,然后就搞定了~

1
2
3
4
5
6
import vue from vue;
declare module 'vue/types/vue' {
interface Vue {
$bus : vue
}
}

同理,如果你喜欢使用this.$http这种写法,你也可以整个这样的声明

后记

这篇文章从开写到发布,间隔一个月,其实这个月,我难得的很忙,项目每天脑壳疼,然后陷入人生的大思考,在一切告一段落后总算补完了这个博客,这个博客也会长期更新,因为项目还没完工2333,还可以记录很多东西,嘛,下次再见~