TypeScript与Vue组合开发记录点

  • TypeScript使用Vue-property-decorator属性装饰器
  • @Component(options:ComponentOptions = {})用法
  • @Prop(options: (PropOptions | Constructor[] | Constructor) = {})用法
  • @PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})用法
  • @Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})用法
  • @Watch(path: string, options: WatchOptions = {})用法
  • @Emit(event?: string)用法
  • provide与inject用法
  • Vue动态组件切换
  • router-view嵌套路由
  • Vue中的redirect重定向
  • 字符串的includes方法
  • TypeScript类
  • TypeScript中extends继承类
  • TypeScript类中Super关键字
  • TypeScript类中Public公有成员
  • TypeScript类中Private私有成员
  • TypesScript类中protected保护成员
  • TypesScript类中Constructor构造器内部成员
  • TypeScript类中static关键字


TypeScript使用Vue-property-decorator属性装饰器

vue-property-decorator是一个vue属性装饰器,基于官方vue-class-decorator开发。拥有以下关键字:

  • @Prop
  • @PropSync
  • @Provide
  • @Model
  • @Watch
  • @Inject
  • @Emit
  • @Component
  • Mixins

@Component(options:ComponentOptions = {})用法

@Component 装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives 等未提供装饰器的选项。虽然也可以在 @Component 装饰器中声明 computed,watch 等,但并不推荐这么做,因为在访问 this 时,编译器会给出错误提示。即使没有组件也不能省略@Component,否则会报错。

import {Component,Vue} from 'vue-property-decorator';
import {componentA,componentB} from '@/components';
 @Component({
    components:{
        componentA,
        componentB,
    }
})
export default class YourCompoentName extends Vue{
}

@Prop(options: (PropOptions | Constructor[] | Constructor) = {})用法

用于父子组件参数之间的传递。@Prop 装饰器接收一个参数,这个参数可以有三种写法:

import { Vue, Component, Prop } from 'vue-property-decorator'
@Componentexport default class MyComponent extends Vue {
 // 第一种写法
 @Prop(String) propA: string | undefined
 // 第二种写法
 @Prop([String, Number]) propB!: string | number
 // 第三种写法,可指定默认值
 @Prop({
 type: String,
 default: 'abc'
 })
 // 公有变量
 public propC!: string
}

注意

  • 属性的ts类型后面需要加上 undefined 类型;或者在属性名后面加上!,表示 非null 和 非undefined的断言,否则编译器会给出错误提示。
  • 指定默认值必须使用上面例子中的写法,如果直接在属性名后面赋值,会重写这个属性,并且会报错。

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

@PropSync 装饰器与 @prop 用法类似,二者的区别在于:

  • @PropSync 装饰器接收两个参数
import { Vue, Component, PropSync } from 'vue-property-decorator'
@Component({
})
export default class MyComponent extends Vue {
// 与prop第一个参数一致,父子组件间传递的值
 @PropSync('propA', { type: String, default: 'abc' }) 
 // 生成一个新的计算属性,对接收的PropA值进行get和set计算
 syncedPropA!: string
}

注意,@PropSync需要配合父组件的.sync修饰符使用

<comp :foo.sync="bar"></comp>

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

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

  • event: string 事件名。
  • options: Constructor | Constructor[] | PropOptions 与 @Prop 的第一个参数一致。
import { Vue, Component, Model } from 'vue-property-decorator'
@Component({
})
export default class MyInput extends Vue {
//接收一个change事件,通过prop接收value的值,value默认值为123
 @Model('change', { type: String, default: '123' }) value!: string
}

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

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

  • 被监听的属性名
  • {被监听属性名身上的属性,可以包含两个值}
import { Vue, Component, Watch } from 'vue-property-decorator'
@Component({
})
export default class MyInput extends Vue {
// 被监听的属性是 msg
 @Watch('msg')
 // msg上给一个回调函数,新值和旧值
 onMsgChanged(newValue: string, oldValue: string) {}
 // 被监听的属性是arr
 // arr给与两个属性,immediate表示监听开始之后是否立即调用回调函数
 // deep表示监听的对象属性被改变时,是否调用回调函数
 @Watch('arr', { immediate: true, deep: true })
 onArrChanged1(newValue: number[], oldValue: number[]) {}
 @Watch('arr')
 onArrChanged2(newValue: number[], oldValue: number[]) {}
}

@Emit(event?: string)用法

  • @Emit 装饰器接收一个可选参数,该参数是 $Emit 的第一个参数,充当事件名。如果没有提供这个参数, $Emit 会将回调函数名转为事件名;
  • @Emit 会将回调函数的返回值作为第二个参数,如果返回值是一个 Promise 对象, $emit 会在 Promise 对象被标记为 resolved 之后触发;
import { Vue, Component, Emit } from ‘vue-property-decorator’
@Component
export default class MyComponent extends Vue {
count = 0
// 未给Emit传递一个事件名参数
@Emit()
// 传递了一个add-to-count的事件名,与其一起传递的值为n
addToCount(n: number) {
 this.count += n
}
// Emit传递了一个 reset 的事件名参数
@Emit(‘reset’)
resetCount() {
// 当resetCount该方法被触发时,此时的count为0,传递的相关事件名为reset
this.count = 0
}
// 未给Emit传递一个事件名参数
@Emit()
// 当returnValue()方法被触发时,返回一个return-value的事件名,与其一起传递的值为10.
returnValue() {
return 10
}

provide与inject用法

provide与inject不太常用于普通程序编写中,常用在组件库的构建中。其中provide是父向子传递value值,inject是子接收父传递过来的值。
特点是在多级嵌套的组件体系中,不管组件的层级有多深,某级外层组件都可以通过provide属性向任意一级的子组件提供一个值,不管组件的层级有多深,某级子组件都可以通过inject属性接收任意一级父组件提供的依赖。

<!--组件的嵌套关系-->
//使用三级嵌套来演示,康熙:爷爷,雍正:儿子,乾隆:孙子
<kangxi>
  <yongzheng>
    <qianlong></qianlong>
  </yongzheng>
</kangxi>
// 爷爷级组件,康熙
<template>
  <div class="kangxi">
    康熙皇帝
    <slot></slot>
  </div>
</template>

<script>
  export default {
    name: 'kangxi',
    // 通过provide,爷爷对儿子提供‘kangxiToYongzheng’
    // 通过provide,对孙子提供‘kangxiToQianlong’
    provide: {
      kangxiToYongzheng: '康熙皇帝对雍正皇帝说:"你是我儿子"',
      kangxiToQianlong: '康熙皇帝对乾隆皇帝说:"你是我孙子"'
    },
    data () {
      return {}
    }
  }
</script>
// 儿子级组件,雍正
<template>
  <div class="yongzheng">
    雍正皇帝
    <slot></slot>
  </div>
</template>

<script>
  export default {
    name: 'yongzheng',
    // 通过inject,儿子接收父亲提供的‘kangxiToYongzheng’
    inject: ['kangxiToYongzheng'],
    // 同时,通过provide,儿子也给孙子提供‘yongzhengToQianlong’
    provide: {
      yongzhengToQianlong: '雍正皇帝对乾隆皇帝说:"你是我儿子"'
    },
    data () {
      return {}
    },
    created () {
      // 可以通过this方法获取到inject接收的参数
      console.log(this.kangxiToYongzheng, ' 雍正皇帝回答:"是的,爸爸"')
    }
  }
</script>
// 孙子级组件,乾隆
<template>
  <div class="qianlong">
    乾隆皇帝
  </div>
</template>
<script>
  export default {
    name: 'qianlong',
    // 通过inject,孙子接收爷爷的‘kangxiToQianlong’、父亲的‘yongzhengToQianlong’
    inject: ['kangxiToQianlong', 'yongzhengToQianlong'],
    data () {
      return {}
    },
    created () {
      // 可以通过this方法获取inject接收的参数
      console.log(this.kangxiToQianlong, ' 乾隆皇帝回答:"是的,爷爷"')
      console.log(this.yongzhengToQianlong, ' 乾隆皇帝回答:"是的,爸爸"')
    }
  }
</script>

Vue动态组件切换

<div id="app">
    <component :is="who"></component>
    <button @click="handler">按钮</button>
  </div>

当who的值发生变化时,component变为相应的组件,可以写一个methods方法,通过点击handler事件触发this.who值的改变。

router-view嵌套路由

一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的部分片段通常对应于特定的嵌套组件结构。渲染路由匹配到的组件。

// 路由配置文件router.js中设定了相关的路由path路径,以及该路由所映射的组件name名字,
  {
    path: "/",
    name: "Home",
    component: Home
  },
  {
    path: "/about",
    name: "About",
    component: () =>
      import("../views/About.vue")
  }
// 在App.vue中使用router-view,当系统运行起来之后,所有匹配到的路由均会被渲染到此处
  <div class="container">
    <router-view />
  </div>

那么在浏览器地址栏访问根路径 “http://localhost:8080/” 时,页面就会渲染Home组件;访问 “http://localhost:8080/about” 时,页面就会渲染About组件,从而实现了组件的动态渲染。

Vue中的redirect重定向

在router.js路由配置文件中修改相关配置

{
 	// 当浏览器访问项目根路径时,App.vue先渲染Layout组件,
 	// 然后Layout组件重定向渲染children子组件dashboard
    path: '/',
    component: Layout,
    redirect: 'dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index'),
        name: 'dashboard',
        meta: {title: '首页', noCache: true, affix: true}
      }
    ]
  }
// Layout.vue文件中使用router-view渲染匹配到的dashboard组件
     <el-main>
        <div class="frame-main">
           <router-view />
        </div>
      </el-main>

字符串的includes方法

string.includes(searchvalue, start),string中从start索引位置处开始检索某searchvalue字符串值,返回是boolean值,如果找到匹配的字符串返回 true,否则返回 false。

TypeScript类

TypeScript中extends继承类

类从基类中继承了属性和方法,不能继承父类的私有成员和构造函数,其他的都可以继承。 这里Dog是一个 派生类,它派生自 Animal基类,通过 extends关键字。 派生类通常被称作子类,基类通常被称作超类。

// 这里Animal是一个基类,里边有一个move方法
class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}
// Dog是从Animal中派生的,继承了基类中的方法,同时还增添了自己的方法bark
class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}
// 通过创建一个 Dog的实例,它能够使用 bark()和 move()
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

TypeScript类中Super关键字

类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。其中 super 关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。

// 父类,其中定义了一个doPrint方法
class PrinterClass { 
   doPrint():void {
      console.log("父类的 doPrint() 方法。") 
   } 
} 
 // 子类,继承自父类,重新定义了一个doPrint方法,在该方法内使用了super关键字直接引用了父类的doPrint函数
class StringPrinter extends PrinterClass { 
   doPrint():void { 
      super.doPrint() // 调用父类的函数
      console.log("子类的 doPrint()方法。")
   } 
}
// 最后打印输出
// 父类的 doPrint() 方法。
// 子类的 doPrint()方法

TypeScript类中Public公有成员

类中,Public是访问控制修饰符,访问控制符用来保护对类、变量、方法和构造方法的访问。public(默认)表示公有,可以在任何地方被访问。

class Encapsulate { 
   str1:string = "hello" 
   private str2:string = "world" 
   public str3:string = "!"
}
 
var obj = new Encapsulate() 
console.log(obj.str1)     // 可访问 
console.log(obj.str2)   // 编译错误, str2 是私有的
console.log(obj.str3)    //可访问

TypeScript类中Private私有成员

类中,Private是访问控制修饰符,访问控制符用来保护对类、变量、方法和构造方法的访问。private表示 私有,只能被其定义所在的类内部访问。

class Encapsulate { 
   str1:string = "hello" 
   private str2:string = "world" 
   public str3:string = "!"
}
 
var obj = new Encapsulate() 
console.log(obj.str1)     // 可访问 
console.log(obj.str2)   // 编译错误, str2 是私有的
console.log(obj.str3)    //可访问

TypesScript类中protected保护成员

类中,Protected是访问控制修饰符,访问控制符用来保护对类、变量、方法和构造方法的访问。protected表示成员受保护,可以被其自身以及其子类访问。

TypesScript类中Constructor构造器内部成员

在实例化类的时候,构造器会被调用,如果构造器需要参数,则在实例化的时候传入参数,构造器会接收到。

class Man {
// 构造器,其中的name前加了public,则name变成了可以被任何地方访问
  constructor(public name: string) {}
}
const man = new Man('lei') // 实例化时构造器将被执行,传入字符串参数

在子类中调用父类的构造器需要使用关键字super

class Man {
  public name: string
  constructor(name: string) {
    this.name = name
  }
}
class WoMen extends Man {
  constructor() {
  // 因为父类构造器有形参,则super关键字后需要传入实参
    super('lei')
  }
}

TypeScript类中static关键字

static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。

// 定义一个类
class StaticMem {  
// static静态关键字
   static num:number; 
   static disp():void { 
      console.log("num 值为 "+ StaticMem.num) 
   } 
} 
// 直接通过类名调用,不需要使用new StaticMem赋予实例化
StaticMem.num = 12     // 初始化静态变量
StaticMem.disp()       // 调用静态方法
// 最后打印输出为
// num 值为 12