前言
离上一篇博客又过去了差不多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 {
@State('name') vuexName : string; @State(state => state.age) vuexAge : number; @Getter('userInfo') vuexUserInfo : any; @Mutation('updateName') updateVuexName; @Action('updateNameAsync') updateVuexNameAsync;
mounted() { console.log(this.vuexName); console.log(this.vuexAge); console.log(this.vuexUserInfo);
setTimeout(() => { console.log("修改name"); this.updateVuexName("sakura"); console.log("4s后修改name"); this.updateVuexNameAsync("snow"); }, 1000);
}
@Watch("vuexName") watchVuexName(newVal) { console.log(`newVuexName更新为: ${newVal}`) }
}
|
运行结果
使用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 {
@AppModule.State("appName") vuexAppName : string; @AppModule.Getter("appSum") vuexAppSum : number; @AppModule.Mutation("updateAppName") vuexUpdateAppName; @AppModule.Action("updateAppNameAsync") vuexUpdateAppNameAsync;
mounted() {
console.log(this.vuexAppName); console.log(this.vuexAppSum);
setTimeout(() => { console.log("修改appName"); this.vuexUpdateAppName("sakura"); console.log("4s后修改appName"); this.vuexUpdateAppNameAsync("snow"); }, 1000);
}
@Watch("vuexAppName") watchVuexAppName(newVal) { console.log(`newVuexAppName更新为: ${newVal}`) }
}
|
运行结果
使用时的一些坑
导入图片报错
当你想引入某个本地图片时,发现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,还可以记录很多东西,嘛,下次再见~