19

如何使用TypeScript编写Vue应用

 4 years ago
source link: https://juejin.im/post/5e44cfd16fb9a07c92052403
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

如何使用TypeScript编写Vue应用

都2020了,本小姐姐还没开始在项目中用上ts,趁着在家闲的慌,初尝ts。下面我将展示如何使用Vue CLI 构建一个Vue + TypeScript应用

初始化项目

Vue create ts-vue
复制代码

选择 Manually select features 我这里选择了Babel, TypeScript, Router, Vuex,CSS Pre-processors,Linter / Formatter

WechatIMG84.png

然后根据提示安装,最终配置如下

2.png

安装完后,根据提示操作

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编写

  1. Class-based components
  2. Data, props, computed properties, methods, watchers, and emit
  3. Lifecycle hooks
  4. Mixins

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
    }
  }
}
复制代码

子组件触发父组件的自定义事件并传递数据,在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>
复制代码

Vue & TypeScript 初体验 - 使用Vuex

vuex-module-decorators

vue-class-component

vue-property-decorator

vuex-class


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK