乐趣区

使用typescript构建Vue应用

使用 typescript 构建 Vue 应用

一、Vue 项目初始化 - 引入 typescript

使用 typescript 构建 Vue 应用和使用 js 一样,都是通过 vue-cli 去初始化并创建一个 vue 项目,只不过使用 typescript 构建的时候 要在脚手架问卷操作的时候勾选上 typescript 选项

二、typescript Vue 项目比较

使用 typescript 构建的 Vue 项目发生了一些变化:
main.js 变成了 main.ts,但是main.ts 中的内容和 main.js 的内容是一模一样的
router.js 变成了 router.ts,但是router.ts 中的内容和 router.js 中的内容也是一模一样的
store.js 变成了 store.ts,但是store.ts 中的内容和 store.ts 中的内容也是一模一样的

因为 typescript 是 javascript 的超集,所以ts 完全兼容 js

新增了一个 shims-vue.d.ts 声明文件,这个文件的作用就是 让 typescript 能够识别.vue 文件,在引入 ”*.vue” 文件的时候,会将其标识为一个 Vue 组件,才能对.vue 文件进行类型校验,其具体内容如下:

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

新增了一个 shims-tsx.d.ts 文件,其作用就是 为了能够解析.tsx 文件,对.tsx 文件进行类型校验,其具体内容如下:

import Vue, {VNode} from 'vue'
declare global {
  namespace JSX {interface Element extends VNode {}
    interface ElementClass extends Vue {}
    interface IntrinsicElements {[elem: string]: any
    }
  }
}

当然还会新增一个 ts.config 文件,这个是 typescript 的配置文件,具体内容这里不作解释,请参考 tsconfig 文件详解

三、.vue 文件内容格式与写法

使用 typescript 来写 Vue 应用,最主要的就是.vue 文件,.vue 文件写法上与 js 有些不同并且 新增了一些装饰器,接着一步一步分析。

虽然.vue 文件的格式和写法上有了不同,但这不同 只是 <script></script> 部分发生了变化,<template></template> 和 <style></style> 和原来是一样的,一个最简单.vue 文件仍然可以使用如下写法:
// HelloWorld.vue

<template>
    <div>
        hello world
    </div>
</template>

在写 <script></script> 部分,第一点不同就是,<script> 标签上要加上 lang 语言属性,表示其中的内容为 ts,如:

<script lang="ts">
</script>

默认 export 上的不同,使用 js 的时候,我们是直接通过 export default {}导出一个 Vue 组件对象即可,但是使用 ts 的时候,我们 必须导出一个类 class,类名为组件名,同时这个类必须继承 Vue,如:

// import Vue from "vue";
import {Component, Vue} from 'vue-property-decorator'; // 引入 Vue 及一些装饰器
@Component
export default class App extends Vue {// 继承 Vue 并导出 Vue 组件}

以上就是 <sciprt> 内容最基本的写法,上面继承的 Vue 不是直接从 ”vue” 模块中引入,而是从 ”vue-property-decorator” 即 vue 属性装饰器模块中引入,当然 也可以通过 vue 模块引入 import Vue from “vue”,但是我们写.vue 文件的时候 通常要引入一些装饰器 ,同时 这个装饰器类 vue-property-decorator 也提供了 Vue,故可以直接从 vue-property-decorator 装饰器类上直接引入

组件中 data 属性的写法,由于我们在.vue 文件中声明了一个 class,这个 class 就是 Vue 组件,我们可以 直接在这个 class 中声明属性 即可,这些声明的属性就是之前使用 js 写时的 data 属性中的数据,如:

export default class App extends Vue {
  public lists = [ // 这里就是之前 Vue 组件的 data 属性
    "Vue.js", "React.js", "Angular.js"
  ]
}

组件中的 computed 计算属性的写法,同样我们可以 在 class 中声明 get 和 set 方法即可变成对应的 computed 属性,如:

export default class App extends Vue {
  public lists = [ // 这里就是之前 Vue 组件的 data 属性
    "Vue.js", "React.js", "Angular.js"
  ]
  public get count() { // 通过 get 和 set 实现计算属性
    return this.lists.length;
  }
}

组件中方法的声明更简单,直接在 class 中声明方法即可,如:

export default class App extends Vue {public say() {console.log("say");
    }
}

@Component 装饰器的使用,@Component 装饰器就是用来 标识当前这个类是一个 Vue 组件 ,@Component 装饰器还可以 传递对象 作为参数,这个传递的对象就是 Vue 组件实例,所以 所有之前用 js 写法的时候,Vue 组件支持的所有选项都可以传入,如:

@Component({
    // 这里可以配置 Vue 组件支持的各种选项
    components: {HelloWorld},
    data() {
        return {a:1}
    },
    methods: {say(){console.log("say");
        }
    }
})
export default class App extends Vue {}

@Component 内容使用的是 Vue.extend() 方法,用于扩展 Vue Component 并生成 Vue 组件,这里需要注意的就是,Vue 最终会将 class 中定义的属性和 @Component 中定义的属性进行合并,如果二者中定义了同名的属性,那么class 中的优先级更高,即 class 中定义的会覆盖掉 @Component 中定义的同名属性,但是data 除外@Component 中定义的同名的 data 数据会覆盖掉 class 中定义的同名 data 属性,如:

@Component({data() {bar: 1},
    methods: {say(){console.log("say::@Component");
        }
    }
})
export default class App extends Vue {
    public bar: number = 2; // 这里的 bar 会被 @Component 中定义的 bar 覆盖掉
    public say() { // 这里的 say()方法会覆盖掉 @Component 中定义的 say 方法
        console.log("say::class");
    }
}

还有一点要注意的是,@Component 装饰器千万不要漏写,必须在组件类 class 前修饰

props 属性的写法,使用 ts 写 Vue 组件的时候,如果要在组件上定义 props 属性,那么 必须通过 @Prop()装饰器 ,其实就是在定义组件 data 属性的时候用 @Prop() 装饰器进行修饰,如果没有通过 @Propp()进行修饰,那么定义的属性就是组件的 data 属性 可以给 @Prop()传递一个配置对象,可以定义传递属性的 default、required、type 等属性,如:

import {Component, Vue, Prop} from 'vue-property-decorator';
@Component // 不要忘了用 @Component 修饰组件 class 哟
export default class HelloWorld extends Vue {@Prop() public bar!: string; // 这里定义的是 props 属性
    public foo = "foo"; // 这里定义的是 data 属性
    @Prop({type: String, default:"foo", required: true}) public foo!: string;
}
</script>

需要注意的是,public foo!: string,这里声明的 string 类型是不起作用的 ,要限定父组件传入的 foo 属性的数据类型, 必须在 @Prop()装饰器内限定数据类型,否则无效

@Emit()装饰器的使用,在子组件发射自定义事件的时候通常会通过 this.$emit(“say”)的方式,但是 typescript 提供了一个 @Emit()装饰器,用于 修饰一个方法 ,当这个方法被执行的时候, 就会在方法执行完成后发射一个同方法名的事件出去 ,当然,这是在没有给 @Emit() 传递参数的情况下,如果给 @Emit()传递了参数,那么就会发射指定参数的事件,如:

export default class HelloWorld extends Vue {@Emit()  // 在 say()方法执行完成后会发射一个同方法名的 say 事件
    public say() {}
    @Emit("speak")  // 这里给 @Emit()传递了参数,则会发射 speak 事件
    public say() {}
}

@Watch()装饰器的使用,其拥有监听组件中数据的变化,就相当于 $watch,需要给其传递一个字符串参数,表示其监听的是哪个数据的变化,如:

@Watch("foo") // 监听 this.foo 的变化
public add(newValue: number, oldValue: number) {console.log(`newValue is ${newValue}, oldValue is ${oldValue}`);
}

四、vuex 中的变化

vuex 中的数据是存放在 state 属性上的,如果要限定 state 中数据的属性和类型,那么我们 必须在创建 store 对象的时候定义一个接口限定一下数据类型,如:

interface IState {lists: string[]
}
export default new Vuex.Store<IState>({
    state: {lists: ["vue"] // 定义了 state 的数据结构,必须要有 lists 属性,并且属性值为 string[]}
});

获取 vuex 中的数据时候,还是可以通过 this.$store.state.lists 获取到,但是我们也可以通过装饰器获取到,要使用vuex 的装饰器,我们需要安装vuex-class,如:

import {State, Mutation, Action} from "vuex-class";
export default class App extends Vue {
    // 将从 vuex 中获取到的 lists 数据保存到组件的 lists 属性上
    @State("lists") public lists!: string[];
    @Mutation("say") // 在 say()方法前修饰,当 say()方法执行的时候就会提交一个 say mutation
    public say() {console.log("say1");
    }
    @Action("speak") // 在 speak()方法前修饰,当 speak()方法执行的时候就会提交一个 speak action
    public speak(){}
    public mounted () {this.say();
      this.speak();}
}
退出移动版