作者:高天阳
邮箱:13683265113@163.com
更改历史
* 2019-07-24 高天阳 初始化文档
Vue项目中应用Typescript
Vue CLI在2.5版本之后内置了TypeScript的支持,并且@vue/cli3提供了TypeScript插件,因此搭建支持TypeScript的vue工程非常方便
创建工程
npm install -g @vue/cli
node环境要求在8及以上 window系统不支持通过命令行 需要下载安装包进行升级
vue create project-name
添加Typescript 插件
为工程添加TypeScript插件,进入工程目录
vue add typescript
// 执行该指令后 会在项目目录中修改、或添加ts文件
添加成功后,我们来看下工程结构,插件已将.js文件修改成了.ts
注意:typescript 不适合在已有项目上运行,因为其特殊的语法
vue-cli-plugin-typescript插件除了添加了typescript相关依赖之外,我们需要关注下vue-class-component和vue-property-decorator, 这两者是VUE的装饰器,vue-property-decorator依赖vue-class-component,class-style模式下开发时可使代码更加简明、可读, 接下来我们会举例介绍怎样更高效、优雅的书写Vue代码
使用Ts开发Vue
完整的Vue页面代码
注意:scrpit中需要指明lang=ts,否则编辑器将不能识别语言类型而报错。
<template>
<div class="content-wrapper" >
</div>
</template>
<script lang = "ts" >
import { Component, Vue } from "vue-property-decorator";
@Component({})
export default class Foo extends Vue {
}
</script>
<style scoped >
</style>
声明响应式属性 data
export default class App extends Vue {
private name: string = 'kaelyn'; // 声明响应式属性
}
这样的写法等同于之前的
export default {
name: 'App',
data() {
return {
name: 'kaelyn'
}
}
}
计算属性
import { Component, Vue } from 'vue-property-decorator';
@Component({})
export default class App extends Vue {
private number: number = 0;
get age(): string { // 计算属性的get
return `I am ${this.number} years old`;
}
set age(value) { // 计算属性的set
this.number = Number(value);
}
}
这样的写法等于之前的:
computed: {
age: {
get: function () {
return `I am ${this.number} years old`;
},
set: function (value) {
this.number = Number(value);
}
}
}
添加vue-router
执行 npm i vue-router
命令添加VueRoter,src下创建router目录、router下创建index.ts文件:
import Vue from 'vue'
import Router from 'vue-router'
import User from '../views/User.vue'
Vue.use(Router);
const routes = [
{
path: '/',
component: User
}
];
const route = new Router({
mode: 'history',
routes
});
/* 前置导航守卫 */
route.beforeEach((to, from, next) => {
// do something before next route
next()
});
/* 后置导航守卫 */
route.afterEach((to, from) => {
// do something after route
});
export default route
main.ts中加入路由:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false;
new Vue({
render: h => h(App),
router
}).$mount('#app');
3.2 创建User.vue
src目录下创建views目录,views下创建User.vue:
<template>
<div>
name: <input title="name" v-model="name"/><br/>
age: <input title="age" type="number" v-model="age"/><br/>
<button type="button" @click="onShowHelloClick">Show Hello</button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class User extends Vue {
showHello = false;
name: string = '';
age: number = 0;
onShowHelloClick() {
this.showHello = !this.showHello
}
}
</script>
<style scoped>
</style>
上述写法等价于
export default {
name: 'user',
data () {
return {
showHello: false,
name: '',
age: 0
}
},
methods: {
onShowHelloClick () {
this.showHello = !this.showHello
}
}
}
3.3 components、props、watch和计算属性
src/components下创建HelloWorld.vue:
<template>
<div>
{{hello}}
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue, Watch} from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
@Prop({
type: String,
required: false,
default: 'world'
})
name!: string;
@Prop([String,Number])
age!: number;
helloName: string = this.name;
helloAge: number = this.getAgeSync(this.age);
get hello (): string {
return `name is: ${this.name}, age is: ${this.age}`
}
@Watch('helloName')
onNameChange(newVal: string, oldVal: string) {
// do something
}
getAgeSync(age: number) :number {
setTimeout(() => {}, 2000);
return 10;
}
}
</script>
<style scoped>
</style>
@Prop修饰器声明class属性为vue组件的prop;@Watch声明函数为vue的观察器;函数前添加关键字get,表明该函数为vue的计算属性。以上代码等价于
<script>
export default {
name: 'HelloWorld',
props: {
name: {
type: string, required: false, default: 'world'
},
age: {
type: [string, number]
}
},
data () {
return {
helloName: '',
helloAge: 0
}
},
computed: {
hello () {
return `name is: ${this.name}, age is: ${this.age}`
}
},
watch: {
helloName (newVal, oldVal) {
// do something
}
},
mounted () {
this.helloName = this.name
this.helloAge = this.getAgeSync(this.age)
},
methods: {
getAgeSync(age) {
setTimeout(() => {}, 2000)
return 10
}
}
}
</script>
真是没有对比就没有伤害呀,不管是从语义还是代码量来讲,基于TS的声名式开发值得入坑(ps:这种声明式的语法与Java中的AT标签很相似)。
修改User.vue,使用HelloWorld组件:
<template>
<div>
name: <input title="name" v-model="name"/><br/>
age: <input title="age" type="number" v-model="age"/><br/>
<hello-world v-if="showHello" :name="name" :age="age"/>
<button type="button" @click="onShowHelloClick">Show Hello</button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from "../components/HelloWorld.vue";
@Component({
components: {
HelloWorld
}
})
export default class User extends Vue {
showHello = false;
name: string = '';
age: number = 0;
onShowHelloClick() {
this.showHello = !this.showHello
}
}
</script>
<style scoped>
</style>
@Component修饰器中的components参数声明了当前组件引入的外部组件。
参考资料