前言
都2020了,本小姐姐还没开始在项目中用上ts,趁着在家闲的慌,初尝ts。下面我将展示如何使用Vue CLI 构建一个Vue + TypeScript应用
初始化项目
Vue create ts-vue
选择 Manually select features
我这里选择了Babel, TypeScript, Router, Vuex,CSS Pre-processors,Linter / Formatter
然后根据提示安装,最终配置如下
安装完后,根据提示操作
cd ts-vue
npm run serve
在浏览器打开地址 "http://localhost:8080/",可以看到项目已经跑起来啦
项目结构
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ └── views
│ ├── About.vue
│ └── Home.vue
│ ├── App.vue
│ ├── main.ts
│ ├── router.ts
│ ├── shims-tsx.d.ts
│ ├── shims-vue.d.ts
│ ├── store.ts
├── eslintrc.js
├── babel.config.js
├── package-lock.json
├── package.json
├── README.md
└── tsconfig.json
shims-tsx.d.ts:允许在vue项目中使用.tsx文件
shims-vue.d.ts:允许导入和使用.vue单个文件组件,因为typescript默认并不支持导入vue文件
本文主要从以下五个方面介绍如何使用TypeScript编写
- Class-based components
- Data, props, computed properties, methods, watchers, and emit
- Lifecycle hooks
- Mixins
- Vuex
1.Class-based components
// ts
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class HelloWorld extends Vue {
}
</script>
与ts等效的js代码是:
<script>
export default {
name: 'HelloWorld'
}
</script>
为了使用TypeScript,需要在script标签上添加 lang = ts
vue-property-decorator是一个第三方包,它使用官方的vue-class-component软件包,并在其之上添加更多装饰器。我们还可以使用name属性来命名组件,但是将其用作类名就足够了
vue-property-decorator
@component({
name: 'HelloWorld'
})
2.导入组件 在一个组件里面引入其他组件的写法如下:
<template>
<div class="main">
<User />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import User from '@/components/User.vue'
@Component({
components: {
User
}
})
export default class HelloWorld extends Vue {
}
</script>
与ts等效的js代码是:
<template>
<div class="main">
<User />
</div>
</template>
<script>
import User from '@/components/User.vue'
export default {
name: 'HelloWorld',
components: {
User
}
})
</script>
2. Data, props, computed properties, methods, watchers
使用data
@Component
export default class HelloWorld extends Vue {
private msg: string = "welcome to my app"
private list: Array<object> = [
{
name: 'Melody',
age: '20'
},
{
name: 'James',
age: '20'
}
]
}
与ts等效的js代码
export default {
data() {
return {
msg: "welcome to my app",
list: [
{
name: 'Melody',
age: '20'
},
{
name: 'James',
age: '20'
}
]
}
}
使用props
可以添加 required, default, type 为props指定验证要求,同样也可以使用 readonly 禁止操作props
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class HelloWorld extends Vue {
@Prop() readonly msg!: string
@Prop({default: 'Joy Melody'}) readonly name: string
@Prop({required: true}) readonly age: number,
@Prop(String) readonly address: string
@Prop({required: false, type: String, default: 'Developer'}) readonly job: string
}
</script>
与ts等效的js代码如下
export default {
props: {
msg,
name: {
default: 'Joy Melody'
},
age: {
required: true,
},
address: {
type: String
},
job: {
required: false,
type: string,
default: 'Developer'
}
}
}
Computed 属性
计算属性一般用于编写简单的模版逻辑
export default class HelloWorld extends Vue {
get fullName(): string {
return this.first+ ' '+ this.last
}
}
与ts等效的js代码如下
export default {
fullName() {
return this.first + ' ' + this.last
}
}
当需要写一个稍微复杂点的涉及到setter和getter的 computed属性时,在ts中写法如下
export default class HelloWorld extends Vue {
get fullName(): string {
return this.first+ ' '+ this.last
}
set fullName(newValue: string) {
let names = newValue.split(' ')
this.first = names[0]
this.last = names[names.length - 1]
}
}
与ts等效的js写法如下
fullName: {
get: function () {
return this.first + ' ' + this.last
},
set: function (newValue) {
let names = newValue.split(' ')
this.first = names[0]
this.last = names[names.length - 1]
}
}
Watch
@Watch(path: string, options: WatchOptions = {})
- @Watch 装饰器接收两个参数:path: string 被侦听的属性名
- options?: WatchOptions={} options可以包含两个属性
- immediate?:boolean 侦听开始之后是否立即调用该回调函数
- deep?:boolean 被侦听的对象的属性被改变时,是否调用该回调函数
@Watch('child')
onChildChanged (val: string, oldVal: string) {
if (val !== oldVal) {
window.console.log(val)
}
}
与ts等效的js代码如下
watch: {
'child': {
handler: 'onChildChanged',
immediate: false,
deep: false
}
},
method: {
onChildChanged(val, oldVal) {
if (val !== oldVal) {
window.console.log(val)
}
}
}
也可以写成: @Watch('child', { immediate: true, deep: true }), 等价于:
watch: {
'child': {
handler: 'onChildChanged',
immediate: true,
deep: true
}
},
method: {
onChildChanged(val, oldVal) {
if (val !== oldVal) {
window.console.log(val)
}
}
}
Methods
export default class HelloWorld extends Vue {
public clickMe(): void {
console.log('clicked')
console.log(this.addNum(4, 2))
}
public addNum(num1: number, num2: number): number {
return num1 + num2
}
}
与ts等效的js代码如下
export default {
methods: {
clickMe() {
console.log('clicked')
console.log(this.addNum(4, 2))
}
addNum(num1, num2) {
return num1 + num2
}
}
}
Emit
子组件触发父组件的自定义事件并传递数据,在TypeScript中使用@Emit 装饰器
import { Vue, Component, Emit } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
count = 0
@Emit()
addToCount(n: number) {
this.count += n
}
@Emit('reset')
resetCount() {
this.count = 0
}
@Emit()
returnValue() {
return 10
}
@Emit()
onInputChange(e) {
return e.target.value
}
@Emit()
promise() {
return new Promise(resolve => {
setTimeout(() => {
resolve(20)
}, 0)
})
}
}
与ts等效的js代码如下
export default {
data() {
return {
count: 0
}
},
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count', n)
},
resetCount() {
this.count = 0
this.$emit('reset')
},
returnValue() {
this.$emit('return-value', 10)
},
onInputChange(e) {
this.$emit('on-input-change', e.target.value, e)
},
promise() {
const promise = new Promise(resolve => {
setTimeout(() => {
resolve(20)
}, 0)
})
promise.then(value => {
this.$emit('promise', value)
})
}
}
}
3.Lifecycle hooks
由于生命周期挂钩是自动调用的,因此它们既不带参数也不返回任何数据,所以,不需要访问修饰符 输入参数或者返回类型
export default class HelloWorld extends Vue {
mounted() {
//do something
}
beforeUpdate() {
// do something
}
}
与ts等效的js代码
export default {
mounted() {
//do something
}
beforeUpdate() {
// do something
}
}
4.Mixins
假设当前已经有一个mixins/ProjectMixin文件 如何在其他组件里面使用方式如下
<template>
<div class="project-detail">
{{ projectDetail }}
</div>
</template>
<script lang="ts">
import { Component, Vue, Mixins } from 'vue-property-decorator'
import ProjectMixin from '@/mixins/ProjectMixin'
@Component
export default class Project extends Mixins(ProjectMixin) {
get projectDetail(): string {
return this.projName + ' ' + 'HS'
}
}
</script>
与ts等效的js代码如下
<template>
<div class="project-detail">
{{ projectDetail }}
</div>
</template>
<script>
import ProjectMixin from '@/mixins/ProjectMixin'
export default {
mixins: [ ProjectMixin ],
computed: {
projectDetail() {
return this.projName + ' ' + 'HS'
}
}
}
</script>
5.vuex
阅读本节前,先了解什么是vuex命名空间
首先安装
npm install vuex-module-decorators -D
npm install vuex-class -D
如果想通过名字空间的形式来使用module, 需在@Module装饰器中添加额外的参数. 例如, 以下示例代码中添加一个namespaced为user的module
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
@Module({ namespaced: true, name: 'user' })
class User extends VuexModule {
public name: string = ''
@Mutation
public setName(newName: string): void {
this.name = newName
}
@Action
public updateName(newName: string): void {
this.context.commit('setName', newName)
}
}
export default User
注意
@Module装饰器的属性字段name值, 必须与new store({modules: {}})中注册module name名称保持一致. 与ts等效的js代码
export default {
namespaced: true,
state: {
name: ''
},
mutations: {
setName(state, newName) {
state.name = newName
}
},
actions: {
updateName(context, newName) {
context.commit('setName', newName)
}
}
}
在组件里面使用vuex
要使用Vuex,可以使用vuex-class
库。该库提供了装饰器,可以在Vue组件中绑定State,Getter,Mutation和Action。由于已经使用了命名空间的Vuex模块,因此我们首先从vuex-class导入命名空间,然后传递模块名称以访问该模块
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { namespace } from 'vuex-class'
const user = namespace('user')
@Component
export default class User extends Vue {
@user.State
public name!: string
@user.Getter
public nameUpperCase!: string
@user.Action
public updateName!: (newName: string) => void
}
</script>
与ts等效的js代码如下
<template>
<div class="details">
<div class="username">User: {{ nameUpperCase }}</div>
<input :value="name" @keydown="updateName($event.target.value)" />
</div>
</template>
<script>
import { mapState, mapGetters, mapActions} from 'vuex'
export default {
computed: {
...mapState('user', ['name']),
...mapGetters('user', ['nameUpperCase'])
}
methods: {
...mapActions('user', ['updateName'])
}
}
</script>