TypeScript都比《隐秘的角落》还火,而我却姗姗来迟的才研究他。现在来看看TS在Vue项目中的配置和一些简单的用法,我们在单独看TS语法的时候有些东西还是很好理解的但是在和框架(vue或react)一起使用的时候有些特殊的用法和障碍,查看框架提供的.d.ts的声明文件中一些复杂类型的定义、组件的书写方式等都要做出不小的调整。
今天没有具体说TS是什么怎么用,只是简单的学习一下TS结合框架Vue中的一些j基本用法。
Vue脚手架已经集成了TS所以在创建项目的时候可以像安装router和Vuex似的一样安装TS,安装好打开目录具体如下:
1 |-- ts-vue 2 |-- .browserslistrc # browserslistrc 配置文件 (用于支持 Autoprefixer) 3 |-- .eslintrc.js # eslint 配置 4 |-- .gitignore 5 |-- babel.config.js # babel-loader 配置 6 |-- package-lock.json 7 |-- package.json # package.json 依赖 8 |-- postcss.config.js # postcss 配置 9 |-- README.md 10 |-- tsconfig.json # typescript 配置 11 |-- vue.config.js # vue-cli 配置 12 |-- public # 静态资源 (会被直接复制) 13 | |-- favicon.ico # favicon图标 14 | |-- index.html # html模板 15 |-- src 16 | |-- App.vue # 入口页面 17 | |-- main.ts # 入口文件 加载组件 初始化等 18 | |-- shims-tsx.d.ts 19 | |-- shims-vue.d.ts 20 | |-- assets # 主题 字体等静态资源 (由 webpack 处理加载) 21 | |-- components # 全局组件 22 | |-- router # 路由 23 | |-- store # 全局 vuex store 24 | |-- styles # 全局样式 25 | |-- views # 所有页面 26 |-- tests # 测试
其实对比我们js的项目区别不是很大,一些js后缀的文件被改成了ts,还多了几个文件
①tscinfig.json:配置文件,主要用于指定待编译的文件和定义编译选项,这个和脚手架2.x版本的congif.json一样的作用;
②shims-tsx.s.ts:允许.tsx 结尾的文件,在 Vue 项目中编写 jsx 代码;
③shims-vue.d.ts:由于TS默认不支持导入Vue文件,而这个文件就是用来识别Vue文件。
在组件中使用TS
1.TypeScript写组件的时候有俩种方式:Vue.extend or Vue-class-component
①Vue.extend():使用基础 Vue 构造器,创建一个“子类”。此种写法与 Vue 单文件组件标准形式最为接近,唯一不同仅是组件选项需要被包裹在 Vue.extend() 中。
②vue-class-component:通常与 vue-property-decorator 一起使用,提供一系列装饰器,能让我们书写类风格的 Vue 组件。
两种形式输出结果一致,同是创建一个 Vue 子类,但在书写组件选项如 props,mixin 时,有些不同。特别是当你使用 Vue.extend() 时,为了让 TypeScript 正确推断类型,你将不得不做一些额外的处理。所以更推荐使用vue-class-component。
vue-property-decorator社区出品,但是完全依赖于vue-class-component(推荐使用这个哦)
vue-class-component官方出品,提供了Vue、Component等;
2.使用vue-property-decorator
当我们在vue
单文件中使用TypeScript
时,引入vue-property-decorator
之后,script
中的标签就变为这样:
1 <script lang="ts"> 2 import {Component, Prop, Vue} from 'vue-property-decorator' 3 4 @Component({}) 5 export default class App extends Vue { 6 name:string = 'yingaxiang' 7 8 // computed 9 get MyName():string { 10 return `My name is ${this.name}` 11 } 12 13 // methods 14 sayHello():void { 15 alert(`Hello ${this.name}`) 16 } 17 18 mounted() { 19 this.sayHello(); 20 } 21 } 22 </script>
等同于js中的
1 <script lang="es6"> 2 export default { 3 data () { 4 return { 5 name: 'yingaxiang' 6 } 7 }, 8 9 mounted () { 10 this.sayHello() 11 }, 12 13 computed: { 14 MyName() { 15 return `My name is ${this.name}` 16 } 17 }, 18 19 methods: { 20 sayHello() { 21 alert(`Hello ${this.name}`) 22 }, 23 } 24 } 25 </script>
小结:
①@component声明成一个vue的组件实例,如果不使用则不能得到一个vue组件,所以这个无论有没有引入自定义组件都要声明,和js的components有些区别;
②vue中的计算属性,我们只需要将该计算属性名定义为为一个函数,并在在函数之前加上get关键字就可,所以原本写在computed中的计算属性现在只要在前边加上get。
3.在vue-class-decorator中还有其他属性下边详解
①@Emit:在Vue的事件监听与触发子父传参中都要用到$emit和$on,那么在 vue-property-decorator中提供了@Emit
1 <script lang="ts"> 2 import {Vue, Component, Emit} from 'vue-property-decorator'; 3 4 @Component({}) 5 export default class "组件名" extends Vue{ 6 mounted(){ 7 this.$on('emit-todo', function(n) { 8 console.log(n) 9 }) 10 11 this.emitTodo('yingaxiang'); 12 } 13 14 @Emit() 15 emitTodo(n: string){ 16 console.log('yingaxiang'); 17 } 18 } 19 </script>
等同于js中:
1 <script lang="es6"> 2 import Vue from 'vue'; 3 4 export default { 5 mounted(){ 6 this.$on('emit-todo', function(n) { 7 console.log(n) 8 }) 9 10 this.emitTodo('yingaxiang'); 11 }, 12 methods: { 13 emitTodo(n){ 14 console.log('yingaxiang'); 15 this.$emit('emit-todo', n); 16 } 17 } 18 } 19 </script>
小结:
在Vue
中我们是使用$emit
触发事件,使用vue-property-decorator
时,可以借助@Emit
装饰器来实现.@Emit
修饰的函数所接受的参数会在运行之后触发事件的时候传递过去。@Emit触发事件有俩种写法:
@Emit()
不传参数,那么它触发的事件名就是它所修饰的函数名.
@Emit(name: string)
,里面传递一个字符串,该字符串为要触发的事件名.
②利用vue-property-decorator
提供的@Watch
装饰器来替换Vue
中的watch
属性,以此来监听值的变化.
1 <script lang="ts"> 2 import {Vue, Component, Watch} from 'vue-property-decorator';、 3 @Watch('child') 4 onChangeValue(newVal: string, oldVal: string){ 5 // todo... 6 } 7 8 @Watch('person', {immediate: true, deep: true}) 9 onChangeValue(newVal: Person, oldVal: Person){ 10 // todo... 11 } 12 </script>
等同于js中
1 export default{ 2 watch: { 3 'child': this.onChangeValue 4 // 这种写法默认 `immediate`和`deep`为`false` 5 , 6 'person': { 7 handler: 'onChangeValue', 8 immediate: true, 9 deep: true 10 } 11 }, 12 methods: { 13 onChangeValue(newVal, oldVal){ 14 // todo... 15 } 16 } 17 }
③@prop属性子组件接收父组件传递来的参数.我们需要定义Prop
属性.
1 <script lang="ts"> 2 import {Vue, Component, Prop} from 'vue-property-decorator'; 3 4 @Component({}) 5 export default class "组件名" extends Vue{ 6 @Prop(Number) propA!: number; 7 @Prop({default: 'default value'}) propB!: string; 8 @propC([String, Boolean]) propC: string | boolean; 9 } 10 </script>
等同于js的:
1 export default { 2 props: { 3 propA: { 4 type: Number 5 }, 6 propB: { 7 default: 'default value' 8 }, 9 propC: { 10 type: [String, Boolean] 11 }, 12 } 13 }
这里需要特别强调:
!: 表示一定存在,?: 表示可能不存在。这两种在语法上叫赋值断言