本文已参与「新人创作礼」活动,一起开启掘金创作之路。
生成项目
vue create ts-vue
或
vue ui // 可视化生成
功能选择时把typescript勾上,安装完成后后npm run serve就可以了
整体demo
- 注意在<script lang="ts">中需要有lang="ts"
- 官方demo中引入的Vue,component是从每个不同的文件中引入的,其实可以从vue自带的'vue-property-decorator'中引入,
import { Component, Prop, Vue, Watch, ... } from 'vue-property-decorator';
vue-property-decorator提供如下七个装饰器和一个Mixins混合以及Vue类(来自官方GitHub)
- @Prop
- @PropSync
- @Model
- @Watch
- @Provide
- @Inject
- @ProvideReactive
- @InjectReactive
- @Emit
- @Ref
- @Component 由vue-class-component组件提供
- Mixins the helper function named
mixinsdefined at vue-class-component
基础结构
typescript中的结构和普通vue中很相似,template中写dom、script写js、style写css,在script中采用了基于类的vue组件(毕竟是vue文件,用一般的写法也可以支持)
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>{{ val }}</h2>
<button @click="change"></button>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
//data,methods,watch,computed的属性和函数都可以使用 private public readonly 来创建
}
</script>
<style scoped lang="scss">
</style>
基础功能
data
typescript和vue中,创建一个数据并不需要放在data中,只需要
...
export default class HelloWorld extends Vue {
val: string = '我是被创建的数据'
val2: number = 999
...
}
...
methods
方法直接创建在类中
...
export default class HelloWorld extends Vue {
val: string = '我是被创建的数据'
change () {
return xxx
}
getVal () {
return this.val
}
...
}
...
computed
函数前边加get即可
...
export default class HelloWorld extends Vue {
get computedData () { // 也可以写成 public get computedData
return xxx
}
...
}
...
watch
watch监听需要额外引入import { Watch } from 'vue-property-decorator',在@Watch的括号()中,写入要监听的属性名,如果需要给watch添加deep等属性,则需要@Watch('val',{deep: true})
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
...
export default class HelloWorld extends Vue {
val: string = '我是val'
@Watch('val')
changeVal(data: any) {
console.log(data) // 我是val
}
...
}
...
引入组件
引入组件的位置需要在类上的@Component装饰器中引入(vuex中的内容也是在这里)
import HelloWorld from './HelloWorld.vue'
@Component({
components: { // 引入需要在这里引入
HelloWorld
},
})
export default class HelloWorld extends Vue {
...
}
...
ref/refs
typescript中,$refs中的内容需要注册后使用
有两种方式注册,一种写成属性,一种是声明
<template>
<hello-world ref="hw" />
</template>
import HelloWorld from './HelloWorld.vue'
@Component({
components: {
HelloWorld
},
})
export default class HelloWorld extends Vue {
$refs!: { // 如果不注册,下面的hw.xxx会报错(hw取得是dom)
hw: HelloWorld // 或者hw: HTMLFormElement
}
// @Ref() hw!: HelloWorld 第二种注册方式
change() {
console.log(this.$refs.hw.xxx)
}
...
}
...
父子相传
父传子
父传子取值注册的时候和vue有所不同
有两种不同的注册方式
- 官方demo中的prop注册方式,创建一个Vue.extend,在其中的props中接收传的值,然后再创建class类时继承创建的那个对象(AppProps)
const AppProps = Vue.extend({
props: {
propMessage: String
}
})
export default class App extends AppProps {
...
}
- 正常创建class,继承Vue,在里边@props来接传过来的属性(推荐,比较方便)
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class HelloWorld extends Vue {
@Prop() msg!: string
@Prop() private names!: string
}
子传父
子传父时需要使用@Emit来传
// 父组件
<template>
...
<About @emitVal="propMsg"/>
</template>
...
// 子组件
<template>
...
<button @click="propMsg">点击给父组件发送数据</button>
...
</template>
<script lang="ts" type="text/tsx">
import { Component, Emit, Vue } from "vue-property-decorator";
@Component
export default class About extends Vue {
@Emit("emitVal") send(data: string) {} // data为要传输的形参(send为属性名,可以任意更改的)
...
propMsg() {
this.send('要传输的数据');
}
}
</script>
Mixin混入
mixin引入的文件为ts文件,可以通过import { mixins } from 'vue-class-component'引入mixins,也可以通过import { Mixins } from 'vue-property-decorator';引入
// mixin.ts
import { Vue, Component } from 'vue-property-decorator'
@Component
export default class helloMixin extends Vue {
mixinValue = 'Hello'
}
// xxx.vue
...
import { Component, Vue, Mixins } from 'vue-property-decorator';
import MyMixin from "@/mixins/mixin.ts"
@Component()
export default class HelloWorld extends Mixins(MyMixin) {
created() {
console.log(this.mixinValue)
}
}
...
vuex
可以通过this.$store.state等正常使用,如果想通过mapGetter等方式使用,需要引入 vuex-class
npm install --save vuex-class
引入vuex-class,然后再class中@注册一下,就可以直接使用了
import {
State,
Mutation
} from 'vuex-class'
@Component()
export default class HelloWorld extends Vue {
@State storeVal:any
@Mutation setStoreVal:any
public change () {
console.log(this.storeVal)
this.setStoreVal('要传输的值')
}
}
// store中的内容
...
export default new Vuex.Store({
state: {
storeVal: 'store中的内容'
},
mutations: {
setStoreVal(state: any, val: string) {
state.storeVal = val
}
}
...
})
父子v-model
v-model在typescript中在用法上截取model时有些区别,需要调用@Model来接取
// 父组件
...
<content-dom v-model="val" />
...
// 子组件
...
import { Component, Model, Vue, Emit } from 'vue-property-decorator';
@Component
export default class contentDom extends Vue {
@Model('change', { type: [Number,String] }) readonly value!:[Number,String]
// 在子组件中,付级传下来的只读参数映射为value,允许为Number或String组件(这里的Number为vue中的prop写法,首字母需大写),通过emit change向父级改变值
@Emit('change') sendValue(data: string|number) {}
public setValue() {
this.sendValue('值值值');
// this.$emit('change','值值值') 这种写法同样生效
}
}
PropSync
和父子级的v-model用处和用法都基本相同
// 父组件
...
<content-dom :value.sync="val" />
...
// 子组件
...
import { Component, PropSync, Vue } from 'vue-property-decorator';
@Component
export default class contentDom extends Vue {
@PropSync('value', { type: [Number,String] }) syncValue!: string|number
// 在子组件中,付级传下来的只读参数映射为syncValue,通过 update:value 向父级改变值
public setValue() {
this.$emit('update:value', '值值值')
}
}