Vuejs 项目从 JS 迁移至 TS

前言

TypeScript 是 Javascript 类型的超集,它可以编译成纯 Javascript。

随着时间的推移,项目的不断增大,TypeScript 的优势越来越明显。虽然在代码量上,TypeScript 会多一些,但是其提供的类型检查,代码索引等功能大大增加了项目的可维护性。所以我推荐在 Vue 项目中也使用 Typescript,也许使用的过程对初学者并不是那么友好,但我认为从项目维护的角度出发,是值得去折腾的。

一、为什么要迁移

1.1 TS 的优势

  1. 静态检查
    ts 的静态检查可以让我们在预编译的过程中发现代码中潜藏的问题,并予以修复。

  2. 可维护性提升
    相较于 js 或者 es6 来说,ts 的代码可读性远高于他们。同时,在编写时也有很好的约束,来降低代码所产生的 BUG 概率。

  3. 兼容性
    ts 可以近乎完美的兼容 js ,即使在 ts 编译报错的情况,仍然可以编译为 js 代码运行。这代表着在迁移的时候会方便不少。

  4. 生态
    ts 有着良好的生态,前端的开源库中,大部分都会提供 ts 的声明文件,使得使用者更方便、准确的使用。

1.2 可维护性

一个好好的 js 项目为什么要迁移成 ts 呢,对于一个需要持续维护的前端项目来说,ts 的优势中最核心的就是「可维护性」的提升了。
可维护性主要体现在以下几个方面:

  1. 类型拓展
    javascript 是弱类型语言。Typescript 在类型上做了补充,这使得在写代码的时候,编辑器就可以告诉我们方法调用、定义、参数传递有没有错误。通过类型的约束来提高代码的稳定性。
  2. 类的拓展
    在 ES 当中也有类的概念,和 Typescript 的类不同,Typescript 对类做了更多的拓展,使其在面向对象的研发思想的运用上,更加自然和方便。

二、准备!

在开始之前需要先了解 vue-class-component、vue-property-decorator 和 tslint 。他们仨都是 vue + ts 的好帮手。

2.1 vue-class-component

vue-class-component 项目地址:https://github.com/vuejs/vue-class-component

vue-class-component 是一个让我们以 ts 类的形式来编写 Vue Component 的库。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div>
<button v-on:click="decrement">-</button>
{{ count }}
<button v-on:click="increment">+</button>
</div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

// Define the component in class-style
@Component
export default class Counter extends Vue {
// Class properties will be component data
count = 0

// Methods will be component methods
increment() {
this.count++
}

decrement() {
this.count--
}
}
</script>

有了 vue-class-component 之后,vue 组件的 data、methods 以及生命周期处理更加清晰一目了然。但这样也有一些缺点,比如当某个组件逻辑极为复杂的时候,我们会写出一个代码很长的类。这从代码整洁的角度来说,是不太友好的。

2.2 vue-property-decorator

vue-property-decorator 项目地址:https://github.com/kaorun343/vue-property-decorator

vue-property-decorator 是对 vue-class-component 库的拓展,它需要在 vue-class-component 的基础上进行使用。提供了更加丰富的 decorator(可以理解为 Java 中的注解)来方便 Vue Component 的开发。
例如对属性 Props 的简化:

1
2
3
4
5
6
7
8
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
}

等价于:

1
2
3
4
5
6
7
8
9
10
11
12
13
export default {
props: {
propA: {
type: Number
},
propB: {
default: 'default value'
},
propC: {
type: [String, Boolean]
}
}
}

三、开始!

这里主要以 vue-cli 的项目为例。vue-cli 提供的有 typescript-plugin。这个插件可以用于将现有项目迁移至 typescript。

3.1 引入

网上有很多文章都是说的手动添加 cli-plugin-typescript 依赖,然后按照标准项目结构,一个一个添加,一个一个改。这样有些麻烦,cli-plugin-typescript 提供了自动化的项目更改。仅仅需要执行以下指令:

1
2
3
npm install --save-dev typescript
npm install --save-dev @vue/cli-plugin-typescript
vue add typescript

执行之后会有如下提示:

1
2
3
4
✔  Successfully installed plugin: vue-cli-plugin-typescript

? Use class-style component syntax? (Y/n)

这里,询问我们是否使用 class 形式的组件语法。选择 Y

1
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX) ?

这里,询问我们是否使用 Babel 编译插件,选择 Y

1
? Convert all .js files to .ts?

这里,询问我们是否自动将所有 js 文件转换为 ts 文件,注意,这里的转换,只是转换文件名,代码并不会自动变更。选择 Y

1
? Allow .js files to be compiled? 

这里,询问我们是否支持 js 文件编译。选择 Y
成功后提示如下:

1
2
✔  Successfully invoked generator for plugin: @vue/cli-plugin-typescript
The following files have been updated / added:

这里会列出变更的文件。
插件会帮我们自动生成 vue 相关的声明文件 shims-tsx.d.tsshims-vue.d.ts 还有 tsconfig.json 配置文件,我们可以按需修改这些配置。

3.2 如何修改

完成上一步操作之后,项目的每个组件和 js 文件都会变成 ts 文件。但是里面的代码并没有变化,需要我们手动进行每个文件的修改。
.vue 文件我们需要按照 vue-property-decorator 的方式进行修改。
示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from './components/HelloWorld.vue';

@Component({
components: {
HelloWorld,
},
})
export default class App extends Vue {}
</script>

<style lang="less">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

普通的 ts 文件需要按照 typescript 的写法,补充一些类型的定义就可以了。

注:有的文件可能会被 cli-typescript-plugin 的模板所覆盖,需要手动修改。

四、总结

这种迁移的方式,相比纯手动来说,方便了一点点。但还是需要每一个文件自己改。那么有没有一种可以自动转换的方法呢?我查阅了一些资料,发现这一篇文章提供的思路还不错,是将代码逻辑转化为 AST ,然后再用 ts 去生成。对于大型项目,也许这是一种不错的思路。
参考链接:https://zhuanlan.zhihu.com/p/107595158