TS05 在Vue中使用TypeScript


使用Vue2.6 + TypeScript3.5.3创建了一个项目,搭建过程总结如下。

(2020.01.07)其中有一些最佳实践可能会随着项目的逐渐迭代进行调整,请自行辨别可行性

1 Vue-CLI支持

Vue-CLI内建了TypeScript工具支持,在新建项目时可以选择使用TypeScript扩展,包括了针对Vue Core的官方类型声明,还包括了Vue Router和Vuex提供了相应的声明文件

使用Vue-CLI会自动创建tsconfig.json文件,基本上使用默认的配置文件就可以满足要求。

2 改造组件

使用TypeScript编写Vue单文件组件有两种方式,一种是通过Vue.extend()方法,另一种是基于类的Vue组件(在使用Vue-CLI创建项目的时候可以选择),我选择使用了后者,可以提供更优雅、更类似于JSX的书写体验。

需要安装vue-class-component用来将Vue组件改写为基于Class的形式,也可以选择使用vue-property-decorator,后者依赖于前者,而且提供了额外的装饰符,让编写更简单。

使用的时候,将原来导出的类型由对象改为了Class,并且使用@Component装饰符,如果有要引入的其他子组件,也放到@Component中。

@Component({
  components: {
    Child
  }
})
export default class HelloVue extends Vue {
  // 组件内容
}

要注意,虽然使用了export default,但是Class的名字还是最好准确定义,这样便于IDE和Lint工具进行追踪、提示。

2.1 组件属性顺序

没有发现Lint和Prettier规则来强制规定组件内的属性顺序,所以约定好一个书写顺序,作为最佳实践

要注意,组件引用、Mixin和Filters都放到了组件外部。总体的顺序分为了三部分:

  1. 数据( Inject → Prop → Data → Computed → Model → Vuex-State → Vuex-Getter → Proivde )
  2. 方法(Vuex-Mutation → Vuex-Action → Methods → Watch)
  3. 钩子函数(生命周期钩子 → 路由钩子)

完整的组件如下,具体写法后面单独列出来(不包含Mixin):

@Component({ components: { Child } })
export default class App extends Vue {
  // 数据 (Inject → Prop→ Computed → Model → Vuex-State → Vuex-Getter → Proivde)
  // 使用祖先组件注入的数据
  @Inject() readonly value1!: string;
  
  // 组件的 Data
  value = 'hello';
  
  // 父组件传入 Prop
  @Prop(Number) readonly value2!: number;
  
  // 计算属性
  get value3(): string {
    return this.value1;
  }
  
   // 定义 组件的 Model 属性
  @Model('change', { type: Boolean, default: false }) checked!: boolean;
  
  // Vuex Store 中定义的 state,作为计算属性定义在组件内
  @State value4!: string;
  
  // Vuex Store 中定义的 getter,作为计算属性定义在组件内
  @Getter value5!: string;
  
  // 为子孙组件提供数据
  @Provide() root = 'Root';
  
  /* ----------------------------------------------- */
  // 方法 (Vuex-Mutation → Vuex-Action → Methods → Watch)
  // Vuex Store 中定义的 Mutation,作为方法定义在组件内
  @Mutation(UPDATE_TITLE_MUTATION) updateTitle!: (payload: { title: string }) => void;
  
  // Vuex Store 中定义的 Action,作为方法定义在组件内
  @Action(UPDATE_TITLE_ACTION) updateTitleSync!: () => void;
  
  // 组件内的 Method
  get foo(): string {
    return this.isCollapse ? 'collapsed-menu' : 'expanded-menu';
  }
  
  // 组件内的 Watch
  @Watch('value1', { immediate: true, deep: true })
  onDataChanged(newVal: string, oldVal: string): void {
    this.foo();
  }
  
  /* ----------------------------------------------- */
  // 钩子函数 (生命周期钩子 → 路由钩子)
  beforeCreated()
  
  created()
  
  beforeMount()
  
  mounted() {}
  
  beforeUpdate() {}
  
  updated(){}
  
  activated(){}
  
  deactivated(){}
  
  beforeDetory(){}
  
  destoryed(){}
  
  errorCaptured(){}
  
  beforeRouteEnter(){}
  
  beforeRouteUpdate(){}
  
  beforeRouteLeave(){}
}

2.2 相关API

(1)Data

直接在Class定义即可(实际上就是Class的新语法,与在Class的constructor中定义相同)

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

@Component
export default class YourComponent extends Vue {
  msg: number = 123;
}

(2)计算属性

计算属性采取使用getter的形式定义,在Class内部可以使用getset关键字,设置某个属性的存值函数和取值函数:

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

@Component
export default class YourComponent extends Vue {
  num: number = 1;
  
  get: value: string() {
    return this.num + 1; 
  }
}

同时定义set实现了对计算属性的赋值。

(3)@Prop

@Prop接受的参数就是原来在Vue中props中传入的参数

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

@Component
export default class YourComponent extends Vue {
  @Prop(Number) readonly propA: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) readonly propC: string | boolean | undefined
}

(4)@PropSync

@PropSyncProp类似,不同之处在于@PropSync会自动生成一个计算属性,计算属性的getter返回传入的Prop,计算属性的setter中会执行Vue中提倡的更新Prop的emit:updatePropName

import { Vue, Component, PropSync } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @PropSync('name', { type: String }) syncedName!: string
}

相当于:

export default {
  props: {
    name: {
      type: String
    }
  },
  computed: {
    syncedName: {
      get() {
        return this.name
      },
      set(value) {
        this.$emit('update:name', value)
      }
    }
  }

使用时需要配合.sync修饰符使用(即在组件上定义对应的更新方法):

<hello-sync :my-prop.sync="syncValue" />
<!-- 相当于 -->

<hello-sync :my-prop="syncValue" @update:name="(name) => syncValue = name" />

(5)定义方法

定义方法与Data类型,直接在Class中定义方法即可:

@Component
export default class HelloChild extends Vue {
  sayHi(): string {
    return 'hello'
  }
}

(6)@Watch

使用@Watch定义侦听器,被装饰的函数就是侦听器执行方法:

@Component
export default class HelloChild extends Vue {
  @Watch('msg', { immediate: true, deep: true })
  onMsgChanged(newVal: string, oldVal: string): void {
    this.oldMsg = oldVal;
  }
}

(7)@Emit

想要触发父组件中定义在组件实例上的方法,需要使用@Emit装饰符。@Emit接受一个参数,是要触发的事件名,如果要出发的事件名和被装饰的方法同名,

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值