Vue + TypeScript 尝鲜体验

适用 Vue.version < 2.5 && Vue.version >= 2.2

其实我个人一开始很讨厌 TypeScript 这个东西,就是因为讨厌 Java 的啰里巴嗦,突然在我眼前出现了 Javascript,便爱上了这门语言。

但现在的我稍稍又觉得这样的东西其实还行,只使用类型系统也并没有完全限制 Javascript 本身的灵活性,并且他帮助我不会犯一些低级错误,而且还能配合 Visual Studio Code 的提示,我觉得这个还是很不错的,最近忙起来的时候,甚至经常把两个输入框的 value 直接进行比较了,于是就想尝试一下 TypeScript。

官方做法

Vue 2.2 以上之后,官方给 Vue已经添加了很多类型声明,那么我们就来实践一下在单文件 Vue 中使用 TypeScript。

webpack rules 中添加 ts-loader 相关(这里使用 webpack 2)

1
2
3
4
5
6
7
8
{
test: /\.ts$/,
exclude: /node_modules|vue\/src/,
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/]
}
}

表示对 .ts 文件编译时使用 ts-loader 进行读取,appendTsSuffixTo 是为了让 tsc 对 vue 文件能够当成一个 module 进行处理,以解决 moudle not found 的问题(tsc 本身不认识 vue 结尾的文件)

添加 .d.ts文件

1
2
3
4
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}

也是为了让 vscode 在 ts 文件中识别 vue 结尾文件

项目根目录下添加 tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"lib": [
"dom",
"es5",
"es2015.promise"
],
"module": "es2015",
"moduleResolution": "node",
"isolatedModules": false,
"target": "es5"
},
"include": [
"./src/**/*.ts"
]
}

allowSyntheticDefaultImports 是为了能够用 es6 形式的 import,其他就参照 Vue 和官网的弄了个最小化的 json。

万事俱备,让我们 npm run dev 跑起来!

在这里,我们假设使用 Vue 官方的 webpack boilerplate,对 Hello.vue 进行一下改造。

在模板的 msg 下新增一行

1
<h2>Say Hello Times: {{ count }}</h2>

并将 script 部分修改成

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
<script lang="ts">
import Vue, { ComponentOptions } from 'vue'
// Declare the component's type
interface HelloInterface extends Vue {
msg: string,
count: number,
sayHello(): number
}

export {
HelloInterface as interface
}
export default {
data() {
return {
msg: 'Welcome to Your Vue.js App',
count: 0
}
},
methods: {
sayHello() {
this.count++;
return this.count;
}
}
// We need to explicitly annotate the exported options object
// with the Hello type
} as ComponentOptions<HelloInterface>;
</script>

这段代码没有什么太大的问题

接着我们改造一下 App.vue

1
<img src="./assets/logo.png" @click="sayHello">

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script lang="ts">
import Vue, { ComponentOptions } from 'vue';
import { interface as helloInterface, default as Hello } from './components/Hello.vue';
interface App extends Vue {
$refs: {
// 对 helloComponent 进行声明,可以使用 helloComponent 上的方法和属性
helloComponent: helloInterface
}
}
export default {
methods: {
sayHello() {
this.$refs.helloComponent.count++;
this.$refs.helloComponent.sayHello();
}
},
components: {
Hello
}
} as ComponentOptions<App>;
</script>

也就是说,像 refs 这种动态的在运行时才能确定的东西,如果需要在 coding 过程中静态化,则需要在 interface 中对其进行声明,写的 code 稍微有点多,不过可以接受。

注: App.vue 修改成 lang=ts 后,顶层的 main.js 需要换成 main.ts 并修改 webpack 入口点,否则发生 file not found 错误

vue-class-component

官方的另一种推荐做法是 vue-class-component,不过 demo 和 readme 有点小问题,可把我这个 TypeScript 新手给难到啦,提了 pr 希望快快通过。

让我们看看使用 vue-class-component 之后的 Hello.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class Hello extends Vue {
msg: string = 'Welcome to Your Vue.js App'
count: number = 0
sayHello(): number {
this.count++;
return this.count;
}
}
</script>

再让我们看看 App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import Hello from './components/Hello.vue';

@Component({
components: {
Hello
}
})
export default class App extends Vue {
$refs: {
helloComponent: Hello
}

sayHello() {
this.$refs.helloComponent.count++;
this.$refs.helloComponent.sayHello();
}
}
</script>

非常 Cool,非常精炼,暂时没有想到可能会发生的没法解决的因为 vue 或者 vue 组件 和 TypeScript 水土不服的编译错误,而且都有了类型和提示。

总结

尤大佬说在接下来的 Vue 2.5 还会加强一系列的 TypeScript 支持(链接),不知道是怎样的支持呢。
另外,欢迎大家在评论区发表 Vue + TypeScript 的使用场景以及你遇到的错误。

微信扫一扫,分享到朋友圈