Vue + TypeScript 组合拳入门篇: 二

604 阅读4分钟

续:组件声明

之前说到了 vue 的组件如果换用 TypeScript 环境,各个选项模块声明方法也有所变化,已经说明了 data , methods, computed 选项属性,这里继续列举说明。

另外,补充说明一点,vue 中 data 选项在 JS 中的一般写法为函数形式:data() { return {} }。这种写法的好处是可以避免在复用组件时,发生多组件共用 data 数据库,数据的污染和混淆。每次 return 都会在该组件内生成一个独立空间存储数据,避免混用。之前提到,在 TS 中 data 可以直接使用定义变量的形式声明,我一开始也有疑惑会不会相当于是没有使用 return 形式的 data ,结果查阅一些资料,证明了 TypeScript 中变量形式的声明就相当于是 return{} 函数返回值,不需要担心数据污染问题,书写也很简洁了。

props 写法

该组件在父组件模板中如:

<name-list msg="小李"> 

接受父组件传递的 props:

export default class NameList extends Vue {
@Prop({ default: "" }) readonly msg!: string;
}
  • 在 TypeScript 中,!:表示一定存在,?:表示可能不存在。这两种在语法上叫赋值断言,是 TS 中非常重要的语法。
  • 需要注意的是,@Prop() 的括号内只允许接受一个参数,如果要输入多个参数,需要包裹成一个对象。

prop 可以接受的参数如下:

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

  • PropOptions,可以使用以下选项:type,default,required,validator
  • Constructor[],指定 prop 的可选类型
  • Constructor,例如 String,Number,Boolean 等,指定 prop 的类型

watch 写法

监听方法 watch 的写法就复杂一些,需要注意一下:

  • 属性参数 @Watch(path: string, options: WatchOptions = {})

  • options 包含两个属性 immediate?:boolean 侦听开始之后是否立即调用该回调函数; deep?:boolean 被侦听的对象的属性被改变时,不论其被嵌套多深,调用该回调函数,即深度监听。

  • options 的两个属性不写默认为false

  • watch声明写法,函数和参数分为两部分,函数名必须是 on[key]Change这种形式。

  • @Watch('arr', { immediate: true, deep: true }) onNameChanged(newValue: string[], oldValue: string[]) {} 可以接受旧值和新值并执行回调函数。

如:

export default class NameList extends Vue {
@Watch("name", { immediate: true, deep: true })
private onNameChange(newValue: string, oldValue: string) {
console.log("watch", newValue, oldValue);
} }

生命周期函数写法

export default class HelloWorld extends Vue {
// created生命周期函数
public created(): void {}
// mounted生命周期函数
public mounted(): void {}
}

写法跟在 JS 中一模一样,没什么需要好说的。

顺带提一句,在 TS 中,类中方法的修饰符默认就是 public,声明可以被他人调用。

Ref 写法

  • 属性参数:@Ref(refKey?: string)

@Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息。如果没有提供这个参数,会使用装饰器后面的属性名充当参数。

<HelloWorld ref="addButton"></HelloWorld>
import { Component, Vue, Ref } from 'vue-property-decorator';
import HelloWorld from "@/components/HelloWorld.vue";

@Component({ components: { HelloWorld } })
export default class App extends Vue {
  @Ref('RefComponent') readonly ref!: HTMLDocument;
  getRef() {
    this.$refs.RefComponent.mothed();
    // mothed 为子组件中定义的方法
  }
}

emit 写法

emit 的写法也比较复杂一点。

  • 属性参数:@Emit(event?: string) 参数说明:

  • @Emit 装饰器接收一个可选参数,充当事件名。如果没有提供这个参数,@Emit会将回调函数名的camelCase转为kebab-case,并将其作为事件名

  • @Emit会将回调函数的返回值作为第二个参数,如果返回值是一个Promise对象,$emit会在Promise对象被标记为resolved之后触发。

  • @Emit的回调函数的参数,会在回调函数没有返回值的情况下,被$emit当做第二个参数使用;有返回值,会放在其返回值之后,一起被 $emit当做参数使用

//当前子组件
<div @click="clickMsg"/>

// 相当于 JS 写法的
clickMsg(){
 this.$emit('change-msg','jack');
}

//父组件中
<hello-world :msg="msg" @change-msg="handleChangeMsg"/>
handleChangeMsg(value){ this.msg = value }

// TS 写法父组件一致,只是子组件的区别
import { Component, Prop, Emit } from 'vue-property-decorator';

export default class HelloWorld extends Vue {
@Emit("change-msg")
clickMsg() {
// $emit的第二个参数,也就是给父组件的值
  return "jack";
  }
}

比较绕,写几个例子试验下就清晰了。实际上不适用这种写法,使用传统的 $emit 也是没问题的。

@Model (V-Model)

默认情况下,一个组件上的v-model 会把 value用作 prop,并把 input用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用不同的 value prop 组合来达到不同的目的。使用model选项可以回避这些情况产生的冲突。

下面是Vue官网的例子:

Vue.component('base-checkbox', {
model: { prop: 'checked', event: 'change' },
props: { checked: Boolean },
template: ` <input type="checkbox"
  v-bind:checked="checked"
  v-on:change="$emit('change', $event.target.checked)" > `
})

在这个组件上使用 v-model 的时候:

<base-checkbox v-model="lovingVue"></base-checkbox>

注意:你仍然需要在组件的 props 选项里声明 checked 这个 prop。

使用vue-property-decorator提供的@Model改造上面的例子,在自定义组件中:

import { Component, Prop, Model } from 'vue-property-decorator';

export default class BaseCheckbox extends Vue {
  @Model('change', {type: Boolean}) checked!: boolean;
}

mixin 混入

在 TypeScript 中写 mixin ,mixin 本身是个 .ts 文件,需要注意。然后在文件内部通过 export 来进行类的导出,然后在 vue 文件中引入得到抽离出来的公共部分。

MyMixin.ts 写法:

import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import MyMixins from './MyMixin';

@Component export default class MyMixin extends Vue {
@Prop({ required: true }) structure: AnyObj;
created(): void { // ... }
}

使用 mixins 的文件:

import { Component, Prop, Mixins } from 'vue-property-decorator';
import MyMixins from './MyMixin';

 @Component export default class App extends Mixins(MyMixin) {
 // ... }

需要写两个或更多 mixins 的话,在 Mixins 里面继续添加即可。通过 Mixins 引入可以正常使用 TS 的语法检测系统,用起来也不复杂。

常用的组件声明就这些,先分享这么多,如有更多内容未来补充。