目录↑

vue-cli3+typescript初体验

By blockXun

生成项目

vue create ts-vue
或
vue ui // 可视化生成

功能选择时把typescript勾上,安装完成后后npm run serve就可以了

整体demo

官方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)

基础结构

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有所不同

有两种不同的注册方式

  1. 官方demo中的prop注册方式,创建一个Vue.extend,在其中的props中接收传的值,然后再创建class类时继承创建的那个对象(AppProps)

    const AppProps = Vue.extend({
     props: {
         propMessage: String
     }
    })
    export default class App extends AppProps {
     ...
    }
  2. 正常创建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混入

官方demo

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', '值值值')
    }
}