关于vue-property-decorator的基础使用实践

目录
  • 基本使用
    • 基础模板
    • data数据定义
    • 生命周期钩子
    • method方法
    • 计算属性
    • 其他选项
  • 装饰器函数
    • @Component
    • @Prop
    • @PropSync
    • @Emit
    • @Ref
    • @Watch
    • @Model
    • 其他

vue-property-decorator帮助我们让vue支持TypeScript的写法,这个库是基于 vue-class-component库封装实现的。

注:以下环境为 vue2.x + typescript

基本使用

基础模板

和原来的vue单文件组件写法对比,template和css区域写法不变,只是script部分的写法有变化。

<!--HelloWorld.vue-->
<template>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
}
</script>
<style scoped>
</style>
  • lang="ts"表示当前支持语言为Typescript
  • @Component表示当前类为vue组件
  • export default class HelloWorld extends Vue表示导出当前继承vue的类

data数据定义

export default class HelloWorld extends Vue {
    msg: string = "";
}

data中数据属性在类中声明为类属性即可

生命周期钩子

export default class HelloWorld extends Vue {
    created(): void {
    }
}

所有生命周期钩子也可以直接声明为类原型方法,但不能在实例本身上调用他们

method方法

export default class HelloWorld extends Vue {
    initData(): void {
    }
}

method里面的方法在类中直接声明为类原型方法即可

计算属性

计算属性声明为类属性 getter/setter

<template>
  <div class="about">
    <input type="text" v-model="name">
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class AboutView extends Vue {
  firsrName = "Hello";
  lastName = "Kity";
  // getter
  get name() {
    return this.firsrName + " " + this.lastName;
  }
  // setter
  set name(value) {
    const splitted = value.split(' ');
    this.firsrName = splitted[0];
    this.lastName = splitted[1] || "";
  }
}
</script>

其他选项

对于其他选项,将他们传递给装饰器函数

装饰器函数

@Component

@Component可以接收一个对象,注册子组件

import { Component, Vue, Ref } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue';
@Component({
  components: {
    HelloWorld,
  },
})
export default class HomeView extends Vue {
}

如果我们使用Vue Router时,希望类组件解析他们提供的钩子,这种情况下,可以使用 Component.registerHooks注册这些钩子

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <input type="text" v-model="name">
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
// 注册路由钩子
Component.registerHooks([
  "beforeRouteEnter",
  "beforeRouteLeave"
])
@Component
export default class AboutView extends Vue {
  // 注册钩子之后,类组件将他们实现为类原型方法
  beforeRouteEnter(to: any, from: any, next: any) {
    console.log("beforeRouteEnter");
    next();
  }
  beforeRouteLeave(to: any, from: any, next: any) {
    console.log("beforeRouteLeave");
    next();
  }
}
</script>

建议将注册代码写在单独的文件中,因为我们必须在任何组件定义之前注册他们。import将钩子注册的语句放在主文件的顶部来确保执行顺序

// class-component-hooks.ts
import { Component } from 'vue-property-decorator'
// Register the router hooks with their names
Component.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave'
])
// main.ts
import './class-component-hooks'
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
  render: h => h(App)
}).$mount('#app')

@Prop

@Prop(options: (PropOptions | Constructor[] | Constructor) = {})
  • Constructor,指定 prop 的类型,例如 String,Number,Boolean等
  • Constructor[],指定 prop的可选类型
  • PropOptions,指定 type,default,required,validator等选项

属性的 ts 类型后面需要设置初始类型 undefined,或者在属性名后面加上!,表示非null和非undefined的断言,否则编译器给出错误提示

父组件

// Test.vue
<template>
  <div class="test">
    <test-children :name="myname" :age="age" :sex="sex" />
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TestChildren from "@/components/TestChildren.vue";
@Component({
  components: {
    TestChildren
  }
})
export default class Test extends Vue {
  private myname = "Kitty";
  private age = 18;
  sex = "female";
}
</script>

子组件

// TestChildren.vue
<template>
  <div class="test-children">
    <p>myname: {{ name }}</p>
    <p>age: {{ age }}</p>
    <p>sex: {{ sex }}</p>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
@Component
export default class TestChildren extends Vue {
  @Prop(String)
  readonly name!: string;
  @Prop({ default: 22, type: Number })
  private age!: number;
  @Prop([String, Boolean])
  sex!: string | boolean;
}
</script>

@PropSync

@PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})

@PropSync装饰器接收两个参数:

  • propName:string,表示父组件传递过来的属性名
  • options:Constructor | Constructor[] | PropOptions与@Prop的第一个参数一样

@PropSync会生成一个新的计算属性,所以@PropSync里面的参数名不能与定义的实例属性同名,因为prop是只读的

@PropSync与@Prop的区别是使用@PropSync,子组件可以对 peops 进行更改,并同步到父组件。

使用 @PropSync需要在父组件绑定props时使用 .sync修饰符

父组件

<template>
  <div class="test">
    <p>gender: {{ gender }}</p>
    <test-children :gender.sync="gender" />
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TestChildren from "@/components/TestChildren.vue";
@Component({
  components: {
    TestChildren
  }
})
export default class Test extends Vue {
  gender = "喝啤酒";
}
</script>

子组件

<template>
  <div class="test-children">
    <p>myGender: {{ myGender }}</p>
    <button @click="updateMyGender">更换myGender</button>
  </div>
</template>
<script lang="ts">
import { Component, Vue, PropSync } from "vue-property-decorator";
@Component
export default class TestChildren extends Vue {
  @PropSync("gender", { type: String })
  myGender!: string;
  updateMyGender() {
    this.myGender = "吃香蕉";
  }
}
</script>

@Emit

@Emit(event?: string)
  • @Emit装饰器接收一个可选参数,该参数是$emit的第一个参数,作为事件名。如果第一个参数为空,@Emit修饰的事件名作为第一个参数,$emit会将回调函数的camelCase转化为kebab-case
  • @Emit会将回调函数的返回值作为 $emit的第二个参数。如果返回值是一个Promise对象,$emit会在Promise对象状态变为resolved之后被触发
  • @Emit回调函数的参数,会放在返回值之后,作为$emit参数

父组件

<template>
  <div class="test">
    <p>name:{{ name }}</p>
    <test-children @change-name="changeName" />
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TestChildren from "@/components/TestChildren.vue";
@Component({
  components: {
    TestChildren
  }
})
export default class Test extends Vue {
  name = "";
  changeName(val: string): void {
    this.name = val;
  }
}
</script>

子组件

<template>
  <div class="test-children">
    <input type="text" v-model="value">
    <button @click="changeName">修改父组件的name</button>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Emit } from "vue-property-decorator";
@Component
export default class TestChildren extends Vue {
  value = "";
  @Emit()
  changeName(): string {
    return this.value;
  }
}
</script>
// 上例@Emit相当于
changeName() {
    this.$emit("changeName", this.value);
}
@Emit()
changeName(arg: string): string {
    return this.value;
}
// 相当于
changeName(arg) {
    this.$emit("changeName", this.value, arg);
}
@Emit("change-name")
change(arg: string): string {
    return this.value;
}
// 相当于
change(arg) {
    this.$emit("changeName", this.value, arg);
}
@Emit()
changeName(): Promise<number> {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(20)
        }, 2000)
    })
}
// 相当于
changeName() {
    const promise = new Promise((resolve) => {
        setTimeout(() => {
            resolve(20)
        }, 2000)
    })
    promise.then(val => {
        this.$emit("changeName", this.val)
    })
}

@Ref

@Ref(refKey?: string)

@Ref接收一个可选的参数,表示元素或子组件的ref引用,如果不传参数,则使用装饰器后面的属性名作为参数

<template>
    <HelloWorld ref="helloComp"/>
</template>
<script lang="ts">
import { Component, Vue, Ref } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue';
@Component({
  components: {
    HelloWorld,
  },
})
export default class HomeView extends Vue {
  @Ref("helloComp") readonly helloWorld!: HelloWorld;
  mounted(): void {
    console.log(this.helloWorld);
  }
}
</script>
<template>
    <HelloWorld ref="helloWorld">
</template>
<script lang="ts">
...
export default class HomeView extends Vue {
  @Ref() readonly helloWorld!: HelloWorld;
}
</script>

@Watch

@Watch(path: string, options: WatchOptions = {})

@Watch接收两个参数:

  • path: string表示被侦听的属性名称
  • options包含immediate?: boolean和 deep: boolean属性
@Watch("value")
    valueWatch(newV: string, oldV: string) {
    console.log(newV, oldV);
}
@Watch("name", { immediate: true, deep: true })
    nameWatch(newV: string, oldV: string) {
    console.log(newV, oldV);
}

@Model

@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})

@Model允许我们在组件上自定义v-model指令,接收两个参数:

event事件名

options和 Prop接收的参数类型一样

父组件

<template>
  <div class="test">
    <p>name:{{ name }}</p>
    <test-children v-model="name" />
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TestChildren from "@/components/TestChildren.vue";
@Component({
  components: {
    TestChildren
  }
})
export default class Test extends Vue {
  name = "";
}
</script>

子组件

<template>
  <div class="test-children">
    <input type="text" :value="value" @input="inputHandle($event)">
  </div>
</template>
<script lang="ts">
import { Component, Vue, Model, Emit } from "vue-property-decorator";
@Component
export default class TestChildren extends Vue {
  @Model("update", { type: String })
  readonly value!: string;
  @Emit("update")
  inputHandle(e: any): void {
    return e.target.value;
  }
}
</script>

解释

export default class TestChildren extends Vue {
  @Model("update", { type: String })
  readonly value!: string;
}
// 相当于
export default {
    model: {
        prop: 'value',
        event: 'update'
    },
    props: {
        value: {
            type: String
        }
    }
}

其他

以下装饰器后面使用到会及时补充,如果有不清楚的可以查看文档

  • @ModelSync
  • @Provide
  • @Inject
  • @ProvideReactive
  • @InjectReactive
  • @VModel
  • Mixins

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • vue项目中使用ts(typescript)入门教程

    目录 1.引入Typescript 2.配置文件webpack配置 3.让项目识别.ts 4.vue组件的编写 data()中定义数据 props传值 完整代码案例 最近项目需要将原vue项目结合ts的使用进行改造,这个后面应该是中大型项目的发展趋势,看到一篇不错的入门教程,结合它并进行了一点拓展记录之.本文从安装到vue组件编写进行了说明,适合入门. 1.引入Typescript npm install vue-class-component vue-property-decorator --

  • 详解vue-property-decorator使用手册

    一,安装 npm i -s vue-property-decorator 二,用法 1,@Component(options:ComponentOptions = {}) @Component 装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives 等未提供装饰器的选项 虽然也可以在 @Component 装饰器中声明 computed,watch 等,但并不推荐这么做,因为在访问 this 时,编译器会给出错误提示 import { Vu

  • vue-property-decorator用法详解

    vue-property-decorator 这个组件完全依赖于vue-class-component.它具备以下几个属性: @Component (完全继承于vue-class-component) @Emit @Inject @Provice @Prop @Watch @Model Mixins (在vue-class-component中定义); 使用 当我们在vue单文件中使用TypeScript时,引入vue-property-decorator之后,script中的标签就变为这样:

  • Vue新搭档TypeScript快速入门实践记录

    目录 1. 使用官方脚手架构建 2. 项目目录解析 3. TypeScript极速入门 3.1 基本类型和扩展类型 3.2 泛型:Generics 3.3 自定义类型:Interface vs Type alias 3.4 实现与继承:implements vs extends 3.5 声明文件与命名空间:declare 和 namespace 3.6 访问修饰符:private.public.protected 3.7 可选参数 ( ?: )和非空断言操作符(!.) 4. Vue组件的Ts写法

  • Vue axios设置访问基础路径方法

    看过axios的官方文档后配置变得简单: 在main.js 做如下配置: import axios from 'axios' axios.defaults.baseURL = 'http://10.202.42.24:8080/sf-cloud-web' Vue.prototype.axios = axios 最后一行是将axios配置到Vue原型中,使用方法为: this.axios.get('/test/1').then(function (response) {}) 可能会遇到下面的报错:

  • Vue 2.0入门基础知识之内部指令详解

    1.Vue.js介绍 当前前端三大主流框架:Angular.React.Vue.React前段时间由于许可证风波,使得Vue的热度蹭蹭地上升.另外,Vue友好的API文档更是一大特色.Vue.js是一个非常轻量级的工具,与其说是一个MVVM框架,不如说是一个js库.Vue.js具有响应式编程和组件化的特点.响应式编程,即保持状态和视图的同步,状态也可以说是数据吧:而其组件化的理念与React则一样,即"一切都是组件,组件化思想方便于模块化的开发,是前端领域的一大趋势. 2.内部指令 2-1.v-

  • Vue项目组件化工程开发实践方案

    我们暂时给提取出来的脚手架取名叫vde-cli,通过vde-cli脚手架生成的组件库工程目录结构如下: 核心功能 组件库 工程的packages文件夹就是用来存放组件库里面的各种组件了,这里不需要通过手动创建文件的方式创建组件,直接通过一条创建组件的命令完成.每个组件都有一个单独的组件文件夹,组件文件夹下都至少包含"index.vue","example.vue","readme.md"这三个文件,这几个文件都是通过创建组件传递的参数加指定的模板

  • vue移动端项目缓存问题实践记录

    最近在做一个vue移动端项目,被缓存问题搞得头都大了,积累了一些经验,特此记录总结下,权当是最近项目问题的一个回顾吧! 先描述下问题场景:A页面->B页面->C页面.假设A页面是列表页面,B页面是列表详情页面,C页面是操作改变B页面的一些东西,进行提交类似的操作.A页面进入B页面,应该根据不同的列表item显示不一样的详情,从B进入C,也应该根据item的标识比如ID展示不一样的内容,在C页面操作后,返回B页面,B页面数据发生变化.这个时候会有两种情况: C页面操作数据后返回B页面,B页面对应

  • Vue全家桶入门基础教程

    1. Vue概述 Vue(读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用. 渐进式: 声明式渲染→组件系统→客户端路由→集中式状态管理→项目构建 可以使用其中的一个或者多个 优点: 易用:熟悉HTML,CSS.JavaScript知识后,可快速上手Vue 灵活:在一个库和一套完整框架之间自如伸缩 高效:20kB运行大小,超快虚拟DOM 2. Vue的基本使用 2.1 传统开发模式对比 /

  • Vue学习-VueRouter路由基础

    目录 一.VueRouter 1.说明 2.选中路由的渲染: 3.基本工作原理 二.实战 1.创建一个带router的vue项目 2.打开项目中的src/router/index.js文件 3.在浏览器中打开项目 4.新建路由 一.VueRouter 1.说明 用 Vue.js + Vue Router 创建单页应用,感觉很自然:使用 Vue.js ,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (

  • 前端架构vue动态组件使用基础教程

    目录 1.基本使用 2.配合 keep-alive使用 1.基本使用 新建组件 Article.vue <template> <div> <p>黄州东南三十里为沙湖,亦曰螺师店.予买田其间,因往相田得疾.</p> <p>闻麻桥人庞安常善医而聋.遂往求疗.</p> <p>安常虽聋,而颖悟绝人,以纸画字,书不数字,辄深了人意.</p> <p>余戏之曰:"余以手为口,君以眼为耳,皆一时异人也.&

  • vue mixins代码复用的项目实践

    目录 导语: 场景: 1. 代码里有很多当前组件需要的纯函数,methods过多 2. 举个例子你有一个组件需要抛出两个数据,直接的v-model不适用.需要采用$emit方法 3. 同理,可以通过这个方式复用很多data数据,避免模板化的声明 总结: 导语: 两年前来到新公司,开始使用vue开发,代码复用程度比较低.到后期大量的开发经验,以及看了一些设计模式类的书籍.才开始慢慢总结一些代码复用的经验.分享出来, PS: Vue版本2.6 场景: 1. 代码里有很多当前组件需要的纯函数,meth

  • React实现类似于Vue中的插槽的项目实践

    目录 搭建项目 实现方式1 实现方式2 最终效果展示 最近刚开始接触React,感觉React比Vue更灵活一些,但是感觉代码确实维护的时候可读性也没有Vue好(可能是因为我太菜了),Vue中很多都是自己的API, 看到这个api就知道想要实现的是什么功能,但是react 需要自己去读一下代码或者有好的代码注释习惯更好. 比如 Vue 中有一个插槽的概念,可以任意放置内容,那么灵活的 React要怎么实现这个功能呢,这篇文章主要就是记录一下“React实现类似于Vue中的插槽的效果” 搭建项目

随机推荐