使用 TypeScript 来写 Vue

  Vue 和 TypeScript 的结合使我们在编写的代码的时候能更及时的避免各种错误,要想更优雅的去编写 Vue 组件,我们可以使用官方维护的 vue-class-component,这个库给我们提供了各种装饰器去结合 TypeScript 来编写我们的 Vue 组件,使用它的好处是我们能通过基于类的 API 去声明组件。
  而 vue-property-decorator 是一个基于 vue-class-component 的库,它提供更多结合 Vue 特性的装饰器,加快开发效率。


安装

  • 直接使用 vue-cli3 来手动选择特性创建项目
# 1. 如果没有安装 Vue CLI 就先安装
npm install --global @vue/cli
    
# 2. 创建一个新工程,并选择 "Manually select features (手动选择特性)" 选项
vue create my-project-name
  • 分别安装
$ npm i -S vue-class-component 
$ npm i -S vue-property-decorator

一个基本的 vue 组件模板

<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'
    }
  }
}

计算属性 computed

<template>
  <div id="app">
     <button @click="age = number + 1">+</button>
     <p>{{age}}</p>
     <button @click="age = number - 1">-</button>
  </div>
</template>

<script>
	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);
  	  }
	}
</script>

vue cpounted
当点击 button 的时候会执行set age(value)去改变 number 的值,同时计算出新的 age 值,这样的写法等于之前的:

computed: {
  age: {
    get: function () {
      return `I am ${this.number} years old`;
    },
    set: function (value) {
      this.number = Number(value);
    }
  }
}

分享个小技巧,如果想要传参给 computed,可以令计算属性返回一个函数:

get foo() {
  // ...
  return (params: any) => {
  	let returnValue;
    // ...
    return returnValue; 
  }
}

侦听属性 watch

import { Component, Vue, Watch } from 'vue-property-decorator';
@Component({})
export default class App extends Vue {
  private number: number = 0;
  @Watch('number')
  changeAge(newValue: number, oldValue: number)  {
    console.log(`newValue: ${newValue}, oldValue: ${oldValue}`);
  }
}

这样的写法等同于之前的:

watch: {
  changeAge: function (newValue, oldValue) {
    console.log(`newValue: ${newValue}, oldValue: ${oldValue}`);          	
  }
}

生命周期

声明周期还是原来的写法:

// 生命周期
beforeCreate() {
  console.log('before create');
}
created() {
  console.log('created');}
beforeMount() {
  console.log('before mount');
}
mounted() {
  console.log('mounted');
}

组件注册与传递 Prop

父组件:

<!-- App.vue -->
<template>
  <div id="app">
    <Son msg="msg from parent"/>
  </div>
</template>
<script lang="ts">
	import { Component, Vue } from 'vue-property-decorator';
	import Son from './components/Son.vue';
	@Component({
 	  components: { Son }
    })
    export default class App extends Vue { }
</script>

子组件:

<!-- Son.vue -->
<template>
  <div class="son">
    <h1>{{ msg }}</h1>
  </div>
</template>

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

	@Component
	export default class Son extends Vue {
  		@Prop() private msg!: string;
	}
</script>

父子组件通信 Emit

父组件:

<!-- App.vue -->
<template>
  <div id="app">
    <Son v-on:methodFromParent="methodFromParent"/>
  </div>
</template>
<script lang="ts">
	import { Component, Vue } from 'vue-property-decorator';
	import Son from './components/Son.vue';
	@Component({
 	  components: { Son }
    })
    export default class App extends Vue { 
      methodFromParent(val: string) {
        console.log('data from sub:', val);
      }
    }
</script>

子组件:

// Son.vue
mounted() {
  this.$emit('methodFromParent', 'hello my parent');
}

这样子组件挂载好之后父组件就回被触发事件打印出data from sub: hello my parent
也可以使用 Vue Property Decorator 官方提供的 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()
  promise() {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}

这种写法相对于之前的:

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)
    },
    promise() {
      const promise = new Promise(resolve => {
        setTimeout(() => {
          resolve(20)
        }, 0)
      })

      promise.then(value => {
        this.$emit('promise', value)
      })
    }
  }
}

混入对象 Mixins

在一个 ts 文件中定义 Mixins:

// mixins.ts
import { Vue, Component } from 'vue-property-decorator';

declare module 'vue/types/vue' {
  interface Vue {
    methodFromMixins(value: number | string): void;  // 记得声明一下,要不然会报错 Property 'methodFromMixins' does not exist on type 'App'.
  }
}

@Component
export default class Mixins extends Vue {
  public methodFromMixins(value: number | string): void {
    console.log('method from mixins', value);
  }
}

在想使用 Mixins 的组件中:

// App.vue
import { Component, Vue } from 'vue-property-decorator';
import mixins from "./common/mixins";
@Component({
  mixins: [mixins]
})
export default class App extends Vue { 
  created() {
    this.methodFromMixins('hello');	// method from mixins hello
  }
}
Logo

前往低代码交流专区

更多推荐