Vue.extend 和 vue-class-component 使用 TypeScript 写 Vue 组件时,有两种推荐形式:
Vue.extend()
:使用基础 Vue 构造器,创建一个“子类”。此种写法与 Vue 单文件组件标准形式最为接近,唯一不同仅是组件选项需要被包裹在 Vue.extend() 中。
vue-class-component
:通常与 vue-property-decorator
一起使用,提供一系列装饰器,能让我们书写类风格的 Vue 组件,和react写法很像,推荐这种写法,如果你会vue和react的话,用这种写法不容易对react的项目手生,不会react的话,用这种写法,会对后来对react的学习有很大的帮助。vue项目中安装
1 2 npm i -S vue-class-component npm i -S vue-property-decorator
一个基本的 vue 组件模板 @Component({})
是必须要有的,即使不用也要留着,要不然页面不加载该组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template> <div class ="content-wrapper" > </div > </template> <script lang = "ts" > import { Component , Vue } from "vue-property-decorator" ; @Component ({}) export default class Foo extends Vue { } </script > <style scoped > </style >
声明响应式属性 data 1 2 3 4 export default class App extends Vue { private name: string = 'kaelyn'; // 声明响应式属性 }
这样的写法等同于之前的:
1 2 3 4 5 6 7 8 9 export default { name : 'App' , data ( ) { return { name : 'kaelyn' } } }
计算属性 computed 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template> <div id="app"> <button @click="age = number + 1">+</button> <p>{{age}}</p> <button @click="age = number - 1">-</button> </div> </template> <script> import { Component, Vue } from 'vue-property-decorator'; @Component({}) export default class App extends Vue { private number: number = 0; get age(): string { // 计算属性的get return `I am ${this.number} years old`; } set age(value) { // 计算属性的set this.number = Number(value); } } </script>
当点击 button 的时候会执行set age(value)
去改变 number 的值,同时计算出新的 age 值,这样的写法等于之前的:
1 2 3 4 5 6 7 8 9 10 11 computed : { age : { get : function ( ) { return `I am ${this .number} years old` ; }, set : function (value ) { this .number = Number (value); } } }
分享个小技巧,如果想要传参给 computed,可以令计算属性返回一个函数:
1 2 3 4 5 6 7 8 9 get foo () { return (params: any ) => { let returnValue; return returnValue; } }
侦听属性 watch 1 2 3 4 5 6 7 8 9 10 import { Component , Vue , Watch } from 'vue-property-decorator' ;@Component ({}) export default class App extends Vue { private number : number = 0 ; @Watch ('number' ) changeAge (newValue: number, oldValue: number ) { console .log (`newValue: ${newValue} , oldValue: ${oldValue} ` ); } }
这样的写法等同于之前的:
1 2 3 4 5 6 watch : { changeAge : function (newValue, oldValue ) { console .log (`newValue: ${newValue} , oldValue: ${oldValue} ` ); } }
生命周期 声明周期还是原来的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 beforeCreate ( ) { console .log ('before create' ); } created ( ) { console .log ('created' );: } beforeMount ( ) { console .log ('before mount' ); } mounted ( ) { console .log ('mounted' ); }
组件注册与传递 Prop 父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!-- App .vue --> <template > <div id ="app" > <Son msg ="msg from parent" /> </div > </template > <script lang ="ts" > import { Component , Vue } from 'vue-property-decorator' ; import Son from './components/Son.vue' ; @Component ({ components : { Son } }) export default class App extends Vue { } </script >
子组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!-- Son.vue --> <template> <div class="son"> <h1>{{ msg }}</h1> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class Son extends Vue { @Prop() private msg!: string; } </script>
父子组件通信 Emit 父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!-- App .vue --> <template > <div id ="app" > <Son v-on:methodFromParent ="methodFromParent" /> </div > </template > <script lang ="ts" > import { Component , Vue } from 'vue-property-decorator' ; import Son from './components/Son.vue' ; @Component ({ components : { Son } }) export default class App extends Vue { methodFromParent (val: string ) { console .log ('data from sub:' , val); } } </script >
子组件:
1 2 3 4 5 mounted ( ) { this .$emit('methodFromParent' , 'hello my parent' ); }
这样子组件挂载好之后父组件就回被触发事件打印出data from sub: hello my parent
。 也可以使用 Vue Property Decorator 官方提供的 Emit 的装饰器来实现通信和传参:
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 import { Vue , Component , Emit } from 'vue-property-decorator' @Component export default class YourComponent extends Vue { count = 0 @Emit () addToCount (n: number ) { this .count += n } @Emit ('reset' ) resetCount ( ) { this .count = 0 } @Emit () returnValue ( ) { return 10 } @Emit () promise ( ) { return new Promise (resolve => { setTimeout (() => { resolve (20 ) }, 0 ) }) } }
这种写法相对于之前的:
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 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 ) }, promise ( ) { const promise = new Promise (resolve => { setTimeout (() => { resolve (20 ) }, 0 ) }) promise.then (value => { this .$emit('promise' , value) }) } } }
过滤器filters 要在@Component({})中写
1 2 3 4 5 6 7 8 @Component ({ filters : { categoryCount (index : number): number { return 123 ; }, }, })
依赖注入 provide 和 inject 该例子是子孙组件调用祖先组件依赖注入来达到刷新页面的效果。
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template> <div id ="app" > <router-view v-if ="isShow" /> </div > </template> import { Component , Vue , Provide } from "vue-property-decorator" ;@Component ({}) export default class App extends Vue { @Provide () public componentReload = this .reload ; private isShow : boolean = true ; reload ( ) { this .isShow = false ; this .$nextTick(() => { this .isShow = true ; }); } }
子组件
1 2 3 4 5 6 7 import { Component , Vue , Inject } from "vue-property-decorator" ;export default class Home extends Vue { @Inject () private componentReload!: any; mounted ( ) { this .componentReload (); } }
混入对象 Mixins 在一个 ts 文件中定义 Mixins:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { Vue , Component } from 'vue-property-decorator' ;declare module 'vue/types/vue' { interface Vue { methodFromMixins (value : number | string): void ; } } @Component export default class Mixins extends Vue { public methodFromMixins (value : number | string): void { console .log ('method from mixins' , value); } }
在想使用 Mixins 的组件中:
1 2 3 4 5 6 7 8 9 10 11 12 import { Component , Vue } from 'vue-property-decorator' ;import mixins from "./common/mixins" ;@Component ({ mixins : [mixins] }) export default class App extends Vue { created ( ) { this .methodFromMixins ('hello' ); } }
引用自两篇博客在 Vue 中使用 TypeScript 的一些思考(实践) 使用 TypeScript 来写 Vue