前言
有個項目,需要使用 mixins 來管理一些通用方法。同時該項目使用 Typescript。
問題
編譯器報錯,提示組件中沒有該 mixin 中的內容。具體看一下代碼。
MixinA:
export const MixinA = {
method: {
sayHello() {
// ...
}
}
}
component:
export default Vue.extend({
mixins: [MixinA],
create() {
this.sayHello(); // <- 報錯位置.
}
})
報錯:
Property 'sayHello' does not exist on type 'CombinedVueInstance<Vue, unknown, unknown, unknown, Readonly<Record<never, any>>>'.Vetur(2339)
根據報錯信息,可以看到是 Vetur 這個插件報的錯。
究其原因,還是因為 TypeScript 不夠聰明,沒法知道 Mixin 到底是什么樣的。
解決方案
這里有許許多多種,我收集整理一下。
-
簡單粗暴法 (as 強制轉換) 極度不推薦
跟着上面的內容:
(this as any).sayHello();
報錯消失,但是這樣會讓代碼很臟,組件閱讀性差的同時,還要寫很多 as 轉換,同時,轉換 any 讓 typeScript 的意義盪然無存,還不如不用。
-
繼承 mixins (只適合當個 mixin)
mixinA:
export const MixinA = Vue.extend({ method: { sayHello() { // ... } } })
components:
export default MixinA.extend({ mixins: [MixinA], create() { this.sayHello(); } })
也不報錯了,通過繼承 mixinA 的方式,讓編譯器知道里面有什么東西。
缺點:違背 mixins 的設計初衷,mixins 本身就支持多 mixin 混入。
優點:簡單粗暴。
-
使用 vue-typed-mixins
import Vue from 'vue' import mixins from 'vue-typed-mixins' const mixinsA = Vue.extend({ data() { return { foo: 'test' } } }) const mixinsB = Vue.extend({ data() { return { bar: 123 } } }) const App = mixins(mixinsA, mixinsB).extend({ data() { return { value: true } }, computed: { concat(): string { return `${this.foo} ${this.bar} ${this.value}` } } })
優點:可以混合,多個 mixin
缺點:使用習慣的修改。 還是比較支持的。 -
升級為 Vue3。
Vue 官方在 3 后,有為 mixin 做的處理。(具體沒有了解過,雖然很吐槽這種升級的方式) -
遵循 Vue 和 TS 原則的一種解決方式.
mixinA :
// mixinA.vue 導出默認 Vue.extend({…})
mixinB:
// mixinB.vue 導出默認 Vue.extend({…})
那么使用這兩個的組件可以定義為:
export default (Vue as VueConstructor<Vue & InstanceType<typeof MixinA>& InstanceType<typeof MixinB>>).extend({ mixins: [MixinA, MixinB],
-
自己寫接口
interface mixinState { sayHello: () => {} } export default (Vue as VueConstructor<Vue & mixinState>).extend({ name: 'Home', create() { this.sayHello(); } })
本質上與 第 5 種 是一樣的。
缺點:要寫許多接口。 -
使用 vue-property-decorator 等。
使用類型注解的方式。沒研究,不喜歡這種寫法。