vue-property-decorator的使用解读
作者:&活在当下&
最近接触了一个新项目,是基于 Webpack 搭建的 Vue2+TypeScript 项目,里面用的语法有些新奇,经查阅资料,发现用的是 vue-property-decorator ,下面是整理的笔记。
装饰器是什么
1、装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法,许多面向对象的语言都有这项功能。
2、装饰器是一种函数,写成@ + 函数名
。它可 以放在类和类方法的定义前面。
为什么要使用 vue-property-decorator
vue-class-component
是官方推出的vue对 typescript
支持的装饰器(库),可以将 Vue 中的组件用类的方式编写。vue-property-decoretor
即vue属性装饰器,这个库完全依赖于 vue-class-component 。在 vue 中使用 typescript,通过装饰器来简化书写。
如何使用 vue-property-decorator
基本用法
<template> <div class="text">测试</div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; @Component export default class Test extends Vue { } </script> <style lang="less" scoped> .text{color:red} </style>
- lang="ts": 表示脚本当前语言是TypeScript;
- @Component: 表示这个类是一个 vue 组件,@Component不能省略,否则会报错;
- Test: 表示组件名
- export default class Login extends Vue: 表示当前组件类是继承vue的
定义变量
data中的数据由原来的data()方法改成直接在对象中定义,data内的属性直接作为实例属性书写,默认都是public公有属性,当变量标记为private时,它就不能在声明它的类的外部访问。
<template> <div class="text">{{uName}}--{{age}}</div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; @Component export default class Test extends Vue { uName='张三' private age=18 } </script> <style lang="less" scoped> .text{color:red} </style>
生命周期钩子函数
<template> <div class="text">{{uName}}--{{age}}</div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; @Component export default class Test extends Vue { uName:string = '张三' private age:number = 18 private created():void { console.log(`Hello,${this.uName}`); } } </script> <style lang="less" scoped> .text{ color:red } </style>
方法
<template> <button @click="sum">{{count}}</button> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; @Component export default class Test extends Vue { count:number=1 private sum(){ this.count++ } } </script> <style lang="less" scoped> .text{ color:red } </style>
@Component 类装饰器
@Component({})可以声明components、filter、directives等未提供装饰器的vue选项,也可以声明computed、watch、路由守卫函数(beforeRouteEnter、beforeRouteLeave)等。
简单的说,就是框架负责为类额外添加一些成员和功能,而开发者负责通过 注解 的方式 将数据传给框架,框架收到 注解 传入的数据后,可以用在类上。
<template> <div> <button @click="sum">{{count}}</button> <ComponentA/> </div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; import ComponentA from '@/component/ComponentA.vue' @Component({ watch:{ count(n){ console.log(n); } }, components:{ ComponentA } }) export default class Test extends Vue { count:number=1 private sum(){ this.count++ } } </script> <style lang="less" scoped> .text{ color:red } </style>
@Prop
父子组件之间的属性传值,子组件接收父组件传参
@Prop接受一个参数可以是类型变量或者对象或者数组.@Prop接受的类型比如Number是JavaScript的类型,之后定义的属性类型则是TypeScript的类型
//父组件 Test.vue <template> <div> <button @click="sum">{{count}}</button> <ComponentA :propA="propA" :propB="propB" :propC="propC"/> </div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; import ComponentA from '@/component/ComponentA.vue' @Component({ watch:{ count(n){ console.log(n); } }, components:{ ComponentA } }) export default class Test extends Vue { count:number=1 private propA:number=1 private propB:boolean=true private propC:string='你好呀' private sum(){ this.count++ } } </script> <style lang="less" scoped> .text{ color:red } </style>
//子组件 ComponentA.vue <template> <div> <div>{{str}}</div> <div>下面是父组件传来的值</div> <div>{{propA}}</div> <div>{{propB}}</div> <div>{{propC}}</div> </div> </template> <script lang="ts"> import { Vue, Component, Prop } from "vue-property-decorator"; @Component export default class ComponentA extends Vue{ str:string='我是子组件' @Prop(Number) propA:number|undefined @Prop([String,Boolean]) propB!:string|boolean @Prop({ default:'default value' }) propC!:string } </script> <!--下面是不用装饰器的写法--> <!--<script> export default { props: { propNum: { type: Number }, propStr: { default: 'default value' }, propArr: { type: [String, Boolean] } }, data(){ return { str:'我是子组件' } }, } </script>-->
需要注意的是:属性的ts类型后面需要加上undefined类型;或者在属性名后面加上!,表示非null 和 非undefined 的断言,否则编译器会给出错误提示。
@PropSync()
与 Prop 的区别是子组件可以对 props 进行更改, 并同步给父组件。
子组件 ComponentA:
<template> <div> <p>{{count}}</p> <button @click="innerCount += 1">increment</button> </div> </template> <script lang="ts"> import { Vue, Component, PropSync } from "vue-property-decorator"; @Component export default class ComponentA extends Vue { @PropSync('count') private innerCount!: number // 注意@PropSync 里的参数不能与定义的实例属性同名, 因为 props 是只读的. } </script>
父组件:注意父组件里绑定 props 时需要加修饰符 .sync
<template> <ComponentA :count.sync="count"/> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; import ComponentA from '@/component/ComponentA.vue' @Component({ components:{ ComponentA } }) export default class Test extends Vue { private count: number = 1 } </script>
@Emit
定义emit事件,参数字符串表示分发的事件名,如果没有,则使用方法名作为分发事件名,会自动转连字符写法;
@Emit会将回调函数的返回值作为第二个参数,如果返回值为一个Promise对象,emit会在Promise-resolved后触发;
//父组件 Test.vue <template> <div> <ComponentA @edit-promise="editHandle" @sum="editHandle" @editHandleEmit="editHandle" @returnValue="editHandle" /> </div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; import ComponentA from "@/component/ComponentA.vue"; @Component({ components: { ComponentA }, }) export default class Test extends Vue { editHandle(count: any) { console.log(count,'===='); } } </script>
//子组件 ComponentA.vue <template> <div> <button @click="editHandle(1)">Click</button> <button @click="returnValue(2)">returnValue</button> <button @click="sum">sum</button> <button @click="editPromise">promise</button> </div> </template> <script lang="ts"> import { Vue, Component, Emit } from "vue-property-decorator"; @Component export default class ComponentA extends Vue { count: number = 0; //@Emit(name: string),里面传递一个字符串,该字符串为要触发的事件名 @Emit("editHandleEmit") private editHandle(n:number) { this.count+=n } @Emit("returnValue") private returnValue(n:number) { return this.count+=n } //@Emit()不传参数,那么它触发的事件名就是它所修饰的函数名 @Emit() sum() { return this.count } //这里@Emit()没有传参数名,editPromise 又是驼峰命名,所以,父组件用的时候要用中划线拼接形式 edit-promise @Emit() editPromise() { return new Promise(resolve => { setTimeout(() => { resolve(20) }, 0) }) } } </script>
@Watch 观察属性装饰器
@Watch使用非常简单,接受第一个参数为要监听的属性名 第二个属性为可选对象
{immediate?: boolean, deep?: boolean}第一个表示监听开始后是否立即调用回调函数,第二个表示监听的属性变化时是否调用回调函数
<template> <div> <button @click="sum">sum</button> </div> </template> <script lang="ts"> import { Vue, Component, Watch } from "vue-property-decorator"; @Component export default class ComponentA extends Vue { count: number = 0; sum() { this.count++ } @Watch('count') countChange1(newVal:number, oldVal:number){ console.log(newVal, oldVal); } //immediate:其值是true或false;immediate:true代表如果在 wacth 里声明了之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行 @Watch('count', { immediate: true }) countChange2(newVal:number, oldVal:number){ console.log(newVal, oldVal); } //deep:其值是true或false;确认是否深入监听。deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器(受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除) @Watch('count', { deep: true }) countChange3(newVal:number, oldVal:number){ console.log(newVal, oldVal); } } </script>
计算属性
对于Vue中的计算属性,我们只需要将该计算属性名定义为一个函数,,在函数前加上get关键字即可,原本Vue中的computed里的每个计算属性都变成了在前缀添加get的函数。
<template> <div> <button @click="sum">sum</button> <div>{{computedMsg}}</div> </div> </template> <script lang="ts"> import { Vue, Component, Watch } from "vue-property-decorator"; @Component export default class ComponentA extends Vue { count: number = 0; sum() { this.count++ } get computedMsg(){ return this.count; } set computedMsg(count: number){ } } </script>
@Provide 和@Inject
- @Provide()、@Inject()提供了父子组件、多层嵌套组件以及兄弟组件数据传递的方法。
- @Provide():父组件中通过Provide传递数据;
- @Inject():子组件中通过Inject获取数据;
<template> <div> <div>{{desc}}</div> <ComponentA/> </div> </template> <script lang="ts"> import { Vue, Component, Provide } from "vue-property-decorator"; import ComponentA from "@/component/ComponentA.vue"; @Component({ components: { ComponentA }, }) export default class Test extends Vue { desc:string="我是父组件" //父组件中通过Provide传递数据,str1、provideStr2是定义的要传递的变量 //如果@Provide()没有传参,则要传给子组件的变量就是@Provide()后面定义的变量名 @Provide() str1:string = '你好呀!'; //如果@Provide()有传参,则要传给子组件的变量就是@Provide()中传入的参数的变量名 @Provide('provideStr2') private str2:boolean = true; } </script>
<template> <div> <div>{{str}}</div> <div>{{str1}}</div> <div>{{str3}}</div> </div> </template> <script lang="ts"> import { Vue, Component, Inject } from "vue-property-decorator"; @Component export default class ComponentA extends Vue { str:string="我是子组件" //子组件中通过Inject获取数据 // str1后面加 “!”表示,str1一定有值 // @Inject()不传参表示接受的变量名和传递的变量名一样 @Inject() private str1!: string; // 如不确定provideStr2是否一定有值,可以加上 “|undefined”,这样就不用加 “!” // @Inject()传参表示接受的变量名和传递的变量名不一样,@Inject()后面定义的变量为接收数据的变量 @Inject('provideStr2') str3: boolean|undefined; } </script>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。