简介
使用vue-cli3.x
脚手架搭建基于TypeScript
的项目。
使用对象的写法与以前类似,这里不再累述
本文介绍的是使用类(class
)的写法
vue-cli2.x
升级成vue-cli3.x
参考:vue-cli2升级成vue-cli3
Typescript
基础知识参考:TypeScript笔记
修饰器相关知识参考:修饰器Decorator笔记
搭建
vue create project-name
# 或者图形化搭建 vue ui
搭建的时候选择typescript
扩展库
vue-class-component
- 官方出品
- 提供了
Vue
、Component
等 - 用类的方式编写组件。这种编写方式可以让
.vue
文件的js
域结构更扁平,并使vue
组件可以使用继承、混入等高级特性 - 开发一般不用
vue-class-component
,直接使用vue-property-decorator
就行
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({
props: {
child: String
},
watch:{
},
components: {
}
})
export default class ComponentA extends Vue {
// initial data
nameString = `ComponentA`
// computed
get computedMsg () {
return 'computed ' + this.nameString
}
// lifecycle hook
mounted () {
this.greet()
}
// method
greet () {
console.log('greeting: ' + this.nameString)
}
}
vue-property-decorator
- 在
vue-class-component
的基础上扩展出来的 - 社区出品
- 深度依赖了
vue-class-component
,拓展出了更多操作符:@Prop
、@Emit
、@Inject
、@Model
、@Provide
、@Watch
Vue
、Component
也可以通过vue-property-decorator
引入- 开发时正常引入
vue-property-decorator
就行
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class App extends Vue {
name: string = 'Simon Zhang'
// computed
get MyName(): string {
return `My name is ${this.name}`
}
// methods
sayHello(): void {
alert(`Hello ${this.name}`)
}
mounted() {
this.sayHello()
}
}
vuex-class
常见写法
组件声明
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
// ...
}
import { Component, Vue } from 'vue-property-decorator'
// Component组件装饰器,用于增加一些全局的配置,比如过滤器filters
@Component({
components: {
// components 里面放一些全局的组件,引入子组件后写在这,跟vue中的components一样
}
})
export default class Test extends Vue {
// ...
}
data对象
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
private str: string = 'hello'
private name: string
constructor() {
super()
this.name = '喵巨人'
}
}
计算属性
直接通过get
加函数名的形式
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
private radius: number = 3
private get area() {
return Math.PI * this.radius * this.radius
}
}
生命周期函数
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
public created(): void {
console.log('created')
}
}
自定义方法
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
private handleClick(): void {
console.log(this.name)
}
}
监听属性
import { Component, Vue, Watch } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
@Watch('name', { immediate: true, deep: true })
public onChildChanged(val: string, oldVal: string) {
console.log('watch new name=' + val)
}
}
Prop声明
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
@Prop() private msg: string
@Prop({ default: 'default' }) private propB: string
@Prop([String, Boolean]) private PropC: string | boolean
@Prop(Object) private list!: any; //!表示这个值必须传,不然报错,强制性。。
// 感叹号是非 null 和非 undefined 的类型断言,所以上面的写法就是对 list 这个属性进行非空断言
@Prop({
type: Boolean, // 父组件传递给子组件的数据类型
required: true, // 是否必填
default: false // 默认值,如果传入的是Object,则要 default: () => ({}) 参数为函数
}) collapsed: boolean
}
Emit事件
import { Component, Vue, Emit } from 'vue-property-decorator'
@Component
export default class Test extends Vue {
private count = 0
@Emit()
private addToCount(n: number) {
this.count += n
}
@Emit('reset')
private resetCount() {
this.count = 0
}
@Emit()
private returnValue() {
return 10
}
}
相当于
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)
}
}
}
父子组件传值
方法一:不使用 @Emit()
-
父组件通过自定义属性将属性值传给子组件
-
子组件引入
Prop
修饰器,通过@Prop()
接收 -
子组件通过
$emit
通知父组件修改 -
父组件绑定事件处理函数
父组件的html
代码
<sub-com :count="count" @add="handleAdd"></sub-com>
父组件的typescript
代码
import { Component, Vue, Emit } from 'vue-property-decorator'
import subCom from 'components/subcom.vue'
@Component({
components: {
subCom
}
})
export default class Test extends Vue {
private count: number = 0
private handleAdd(offset: number) {
this.count += offset
}
}
子组件的typescript
代码
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component
export default class Subcom extends Vue {
@Prop() private count!: number
private countAdd() {
this.$emit('add', 1)
}
}
方法二:使用 @Emit()
-
父组件通过自定义属性将属性值传给子组件
-
子组件引入
Prop
修饰器,通过@Prop()
接收 -
子组件引入
Emit
修饰器,通过向父组件传递事件 -
父组件绑定事件处理函数
父组件的html
代码
<sub-com :count="count" @add="handleAdd"></sub-com>
父组件的typescript
代码
import { Component, Vue, Emit } from 'vue-property-decorator'
import subCom from 'components/subcom.vue'
@Component({
components: {
subCom
}
})
export default class Test extends Vue {
private count: number = 0
private handleAdd(offset: number) {
this.count += offset
}
}
子组件的typescript
代码
import { Component, Vue, Prop, Emit } from 'vue-property-decorator'
@Component
export default class Subcom extends Vue {
@Prop() private count!: number
@Emit('add')
private countAdd() {
return 1
}
}
过滤器
- 在
src/
目录下新建filters/
目录 - 在
src/filters/
目录下新建index.ts
文件
export function formatTime(value: number): string {
if (!value) {
return ''
}
const date: Date = new Date()
date.setTime(Number(value) * 1000)
let year: number = date.getFullYear()
let month: number | string = date.getMonth() + 1
let day: number | string = date.getDate()
let hour: number | string = date.getHours()
let minute: number | string = date.getMinutes()
let second: number | string = date.getSeconds()
month = ('00' + month).slice(-2)
day = ('00' + day).slice(-2)
hour = ('00' + hour).slice(-2)
minute = ('00' + minute).slice(-2)
second = ('00' + second).slice(-2)
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}
- 在组件局部使用:通过
filters
注入局部过滤器函数
<div>{{ time | formatTime }}</div>
import { Component, Vue } from 'vue-property-decorator'
import { formatTime } from '@/filters'
@Component({
filters: {
formatTime
}
})
export default class Test extends Vue {
private time: number = 1573712728
}
- 全局注册:修改
src/main.ts
文件
import * as filters from './filters' // 全局filter
// register global utility filters.
Object.keys(filters).forEach(key => {
Vue.filter(key, (filters as any)[key])
})
指令
- 在
src/
目录下新建directives/
目录 - 在
src/directives/
目录下新建index.ts
文件
export const focus = {
// 当被绑定的元素插入到DOM中时
inserted: function (el: HTMLElement) {
// 聚焦元素
el.focus()
}
}
- 在组件局部使用
<input v-focus v-model="modelData">
import { Component, Vue } from 'vue-property-decorator'
import { focus } from '@/directives'
@Component({
directives: {
focus
}
})
export default class Test extends Vue {
private modelData: string
}
混入
混合公用的属性或函数在template
中使用时可以定义为private
,
如果在生命周期中使用需要定义为public
,然后还需在shims-vue.d.ts
文件中将混合公用的属性或函数声明下(或者直接告诉它这个肯定有(this as any).consoleStr()
)。
- 在
src/
目录下新建mixins/
目录 - 在
src/mixins/
目录下新建test.ts
文件
// 定义混合属性或函数
import { Vue, Component } from 'vue-property-decorator'
@Component
export default class MixinTest extends Vue {
private message: string = 'hello word'
public consoleStr() {
this.$notify('测试混合函数');
}
}
- 在组件文件中使用
<div>{{ message }}</div>
<van-button type="primary" @click="consoleStr">Notify</van-button>
import { Component, Vue } from 'vue-property-decorator'
import MixinTest from '../mixins/test'
@Component({
mixins: [MixinTest]
})
export default class Test extends Vue {
public created() {
(this as any).consoleStr()
}
}
常见问题
获取dom
// 加上 as HTMLDivElement
let docEl = document.documentElement as HTMLDivElement
获取refs
在其后面加上as HTMLDivElement
let layoutList: any = this.$refs.layout as HTMLDivElement
// 或着
let fieldCalculate: any = (this as any).$refs.fieldCalculate
Property ‘deviceId’ has no initializer and is not definitely assigned in the constructor
属性deviceId
还没有初始化方法(initializer
)而且在构造器(constructor
)中也没有明确指出
在Typescript 2.7 release
版本出来后,设置了一个新的编译器选项strictPropertyInitialization
。
当本选项strictPropertyInitialization:true
的时候,编译器会确保一个类中所有的属性都已经初始化,如果没有,那么在属性构建的过程中就会抛出错误
解决办法:
修改tsconfig.json
文件,将其中的strictPropertyInitialization
值修改成false
{
"compilerOptions": {
// ...
"strictPropertyInitialization": false
// ...
},
// ...
}
发表评论