此文已由作者張漢銳授權網易雲社區發布。
歡迎訪問網易雲社區,了解更多網易技術產品運營經驗。
前言:對於我們而言,typescript 更像一個工具
官方指南
從 vue2.5 之后,vue 對 ts 有更好的支持。根據官方文檔,vue 結合 typescript ,有兩種書寫方式:
Vue.extend
import Vue from 'vue'
const Component = Vue.extend({ // type inference enabled
})
Class-style Vue Components
import Vue from 'vue'
import Component from 'vue-class-component'
// The @Component decorator indicates the class is a Vue component
@Component({ // All component options are allowed in here
template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue { // Initial data can be declared as instance properties
message: string = 'Hello!'
// Component methods can be declared as instance methods
onClick (): void { window.alert(this.message)
}
}
理想情況下,Vue.extend 的書寫方式,是學習成本最低的。在現有寫法的基礎上,幾乎 0 成本的遷移
// 現在常見的寫法export default { // your code }
但「理想豐滿,現實骨感」,問題出在:
Vue.exend 在和 vuex 和 mixins 結合使用的時候,無法發揮 ts 的作用,vuex 和 mixins 會在項目中大量使用,這個問題不能忽視。
Vue.extend + vuex + mixins 問題的介紹
Vue.extend + vuex 的問題
由於 vuex 使用 mapState, mapActions 等方法的時候,通過字符串形式做映射,這個過程中,丟失了類型信息。下面的 gif 可以看到,整個過程中:
無法做代碼提示
無法對對應的 actions 和 state 做類型聲明,使得類型檢查生效
無法使用重構
顯然,如果只有一部分的方法和屬性得到了代碼提示和類型檢查,就是失去了使用 typescript 意義。
在 Vue.extend + vuex 寫法下,這個問題暫時沒有解決方案。
Vue.extend + mixins 的問題
同樣的問題,在 mixin 中定義的方法,不會被 typescript 識別到,下面 gif 可以看到,不僅僅「代碼提示」「類型檢查」「代碼重構」沒有工作,甚至因識別不到 test 而報錯
Class-Style Components
那么就剩下 Class-Style Components 方案。當然,這個方案需要做額外的工作才能夠讓「vue 全家桶 + ts」良好的工作。
原理:將屬性直接掛載在 class 上,使得 typescript 能夠良好的進行「代碼提示」和「類型檢查」。然后再通過裝飾器將屬性轉成 vue 上的屬性。
例如 @Prop, @Watch, @Action 等裝飾器,將屬性做相應的轉換成 props, watch, mapActions 里面的值,具體后面例子展示。
vue-class-component
這里庫提供最基礎的 vue 裝飾器:@Component 。其他的 vue 裝飾器庫,都在這個庫的基礎上做擴展和修改。看看官網的例子:
import Vue from 'vue'import Component from 'vue-class-component'// @Component 會將 MyComponent 中的屬性,轉換成 vue 對應的屬性@Component({ // Vue 所有的屬性都可以在這里聲明,一般用到的比較少
template: '<button @click="onClick">Click!</button>'})
export default class MyComponent extends Vue { // @Component 將 message 轉成成 data
message: string = 'Hello!'
// @Component 會將這里的 getter 屬性,轉換成 computed
get name(){ return 'anders'
} // @Component 識別到 created 是聲明周期關鍵字,不做處理
created(){} // @Component 識別到 onClick 不是關鍵字,將它轉成 methods
onClick (): void { window.alert(this.message)
}
}
vue-property-decorator
這個庫提供了:
@Emit
@Inject
@Model
@Prop
@Provide
@Watch
其中常用的: @Prop,@Watch,@Emit。 看例子:
import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'const s = Symbol('baz')
@Component
export class MyComponent extends Vue {
@Emit()
addToCount(n: number){ this.count += n }
@Emit('reset')
resetCount(){ this.count = 0 }
@Prop()
propA: number
@Prop({ default: 'default value' })
propB: string
@Prop([String, Boolean])
propC: string | boolean
@Watch('child')
onChildChanged(val: string, oldVal: string) { }
@Watch('person', { immediate: true, deep: true })
onPersonChanged(val: Person, oldVal: Person) { }
}
上面的使用就相當於:
const s = Symbol('baz')
export const MyComponent = Vue.extend({
name: 'MyComponent',
props: {
checked: Boolean,
propA: Number,
propB: {
type: String, default: 'default value'
},
propC: [String, Boolean],
},
methods: {
addToCount(n){ this.count += n this.$emit("add-to-count", n)
},
resetCount(){ this.count = 0
this.$emit("reset")
},
onChildChanged(val, oldVal) { },
onPersonChanged(val, oldVal) { }
},
watch: { 'child': {
handler: 'onChildChanged',
immediate: false,
deep: false
}, 'person': {
handler: 'onPersonChanged',
immediate: true,
deep: true
}
}
})
更加全面的用法參考文檔:vue-property-decorator
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】 iOS安裝包瘦身(上篇)
