在 Typescript 中使用可被復(fù)用的 Vue Mixin功能
轉(zhuǎn)到用 Typescript 寫(xiě) Vue 應(yīng)用以后,經(jīng)過(guò)一輪工具鏈和依賴的洗禮,總算蹣跚地能走起來(lái)了,不過(guò)有一個(gè)很常用的功能 mixin,似乎還沒(méi)有官方的解決方案。
既想享受 mixin 的靈活和方便,又想收獲 ts 的類型系統(tǒng)帶來(lái)的安全保障和開(kāi)發(fā)時(shí)使用 IntelliSense 的順滑體驗(yàn)。
vuejs 官方組織里有一個(gè) 'vue-class-component' 以及連帶推薦的 'vue-property-decorator',都沒(méi)有相應(yīng)實(shí)現(xiàn)。翻了下前者的 issue,有一條掛了好些時(shí)間的待做 feature 就是 mixin 的支持。
也不是什么復(fù)雜的事,自己寫(xiě)一個(gè)吧。
后注:vue-class-component 6.2.0 開(kāi)始提供 mixins 方法,和本文的實(shí)現(xiàn)思路相似。
實(shí)現(xiàn)
import Vue, { VueConstructor } from 'vue'
export type VClass<T> = {
new(): T
} & Pick<VueConstructor, keyof VueConstructor>
/**
* mixins for class style vue component
*/
function Mixins<A>(c: VClass<A>): VClass<A>
function Mixins<A, B>(c: VClass<A>, c1: VClass<B>): VClass<A&B>
function Mixins<A, B, C>(c: VClass<A>, c1: VClass<B>, c2: VClass<C>): VClass<A&B&C>
function Mixins<T>(c: VClass<T>, ...traits: Array<VClass<T>>): VClass<T> {
return c.extend({
mixins: traits
})
}
聲明 VClass<T> 可作為 T 的類構(gòu)造器。同時(shí)通過(guò) Pick 拿到 Vue 的構(gòu)造器上的靜態(tài)方法(extend/mixin 之類),如此才能夠支持下面這段中的真正實(shí)現(xiàn),通過(guò)調(diào)用一個(gè) Vue 的子類構(gòu)造器上的 extend 方法生成新的子類構(gòu)造器。
function Mixins<T>(c: VClass<T>, ...traits: Array<VClass<T>>): VClass<T> {
return c.extend({
mixins: traits
})
}
至于 ABC 這個(gè)純粹是類型聲明的體力活了。
使用
實(shí)際使用時(shí):
import { Component, Vue } from 'vue-property-decorator'
import { Mixins } from '../../util/mixins'
@Component
class PageMixin extends Vue {
title = 'Test Page'
redirectTo(path: string) {
console.log('calling reidrectTo', path)
this.$router.push({ path })
}
}
interface IDisposable {
dispose(...args: any[]): any
}
class DisposableMixin extends Vue {
_disposables: IDisposable[]
created() {
console.log('disposable mixin created');
this._disposables = []
}
beforeDestroy() {
console.log('about to clear disposables')
this._disposables.map((d) => {
d.dispose()
})
delete this._disposables
}
registerDisposable(d: IDisposable) {
this._disposables.push(d)
}
}
@Component({
template: `
<div>
<h1>{{ title }}</h1>
<p>Counted: {{ counter }}</p>
</div>
`
})
export default class TimerPage extends Mixins(PageMixin, DisposableMixin) {
counter = 0
mounted() {
const timer = setInterval(() => {
if (this.counter++ >= 3) {
return this.redirectTo('/otherpage')
}
console.log('count to', this.counter);
}, 1000)
this.registerDisposable({
dispose() {
clearInterval(timer)
}
})
}
}
count to 1
count to 2
count to 3
calling reidrectTo /otherpage
about to clear disposables
注意到直接 extends Vue 的 DisposableMixin 并不是一個(gè)有效的 Vue 組件,也不可以直接在 mixins 選項(xiàng)里使用,如果要被以 Vue.extend 方式擴(kuò)展的自定義組件使用,記住使用 Component 包裝一層。
const ExtendedComponent = Vue.extend({
name: 'ExtendedComponent',
mixins: [Component(DisposableMixin)],
})
Abstract class
在業(yè)務(wù)系統(tǒng)中會(huì)使用到的 Mixin 其實(shí)多數(shù)情況下會(huì)更復(fù)雜,提供一些基礎(chǔ)功能,但有些部分需要留給繼承者自行實(shí)現(xiàn),這個(gè)時(shí)候使用抽象類就很合適。
abstract class AbstractMusicPlayer extends Vue {
abstract audioSrc: string
playing = false
togglePlay() {
this.playing = !this.playing
}
}
class MusicPlayerA extends AbstractMusicPlayer {
audioSrc = '/audio-a.mp3'
}
class MusicPlayerB extends AbstractMusicPlayer {
staticBase = '/statics'
get audioSrc() {
return `${this.staticBase}/audio-b.mp3`
}
}
但抽象類是無(wú)法被實(shí)例化的,并不滿足 { new(): T } 這個(gè)要求,因此只能被繼承,而不能被混入,由于同樣的原因,抽象類也無(wú)法被 'vue-class-component' 的 Component 函數(shù)裝飾。
這時(shí)候只好將實(shí)現(xiàn)了的功能寫(xiě)入 Mixin 中,待實(shí)現(xiàn)的功能放到接口里,讓具體類來(lái)實(shí)現(xiàn)。
interface IMusicSourceProvider {
audioSrc: string
}
/**
* @implements IPlayerImplementation
*/
class PlayerMixin extends Vue {
/** @abstract */
audioSrc: string
logSrc() {
console.log(this.audioSrc)
}
}
interface IPlayerImplementation extends IMusicSourceProvider {}
class RealPlayer extends Mixins(PlayerMixin) implements IPlayerImplementation {
audioSrc = '/audio-c.mp3'
}
這種欺騙編譯器的方式其實(shí)還是比較拙劣的,如果一個(gè)具體類繼承了 PlayerMixin,卻沒(méi)有顯示聲明實(shí)現(xiàn) IPlayerImplementation ,編譯器無(wú)法告訴你這個(gè)錯(cuò)誤。我們只能在代碼里小心翼翼寫(xiě)上注釋,期待使用者不要忘了這件事。
總結(jié)
以上所述是小編給大家介紹的在 Typescript 中使用可被復(fù)用的 Vue Mixin功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- 深入了解Vue.js 混入(mixins)
- 淺談Vue3 Composition API如何替換Vue Mixins
- Vue之Mixins(混入)的使用方法
- 深入淺析Vue中mixin和extend的區(qū)別和使用場(chǎng)景
- 使用vue中的混入mixin優(yōu)化表單驗(yàn)證插件問(wèn)題
- 記一次Vue.js混入mixin的使用(分權(quán)限管理頁(yè)面)
- Vue之mixin全局的用法詳解
- 詳解vue項(xiàng)目中如何引入全局sass/less變量、function、mixin
- Vue中的混入的使用(vue mixins)
- Vue 中mixin 的用法詳解
- 詳解Vue的mixin策略
相關(guān)文章
Vue中消息橫向滾動(dòng)時(shí)setInterval清不掉的問(wèn)題及解決方法
最近在做項(xiàng)目時(shí),需要進(jìn)行兩個(gè)組件聯(lián)動(dòng),一個(gè)輪詢獲取到消息,然后將其傳遞給另外一個(gè)組件進(jìn)行橫向滾動(dòng)展示,結(jié)果滾動(dòng)的速度越來(lái)越快。接下來(lái)通過(guò)本文給大家分享Vue中消息橫向滾動(dòng)時(shí)setInterval清不掉的問(wèn)題及解決方法,感興趣的朋友一起看看吧2019-08-08
Electron+Vue3+Vite搭建桌面應(yīng)用的示例代碼
本文主要介紹了Electron+Vue3+Vite搭建桌面應(yīng)用的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
vue.js中created()與activated()的個(gè)人使用解讀
這篇文章主要介紹了vue.js中created()與activated()的個(gè)人使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
vue實(shí)現(xiàn)計(jì)數(shù)器簡(jiǎn)單制作
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)計(jì)數(shù)器簡(jiǎn)單制作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
vue-cli創(chuàng)建項(xiàng)目時(shí)由esLint校驗(yàn)導(dǎo)致報(bào)錯(cuò)或警告的問(wèn)題及解決
這篇文章主要介紹了vue-cli創(chuàng)建項(xiàng)目時(shí)由esLint校驗(yàn)導(dǎo)致報(bào)錯(cuò)或警告的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Vue實(shí)現(xiàn)讓頁(yè)面加載時(shí)請(qǐng)求后臺(tái)接口數(shù)據(jù)
這篇文章主要介紹了Vue實(shí)現(xiàn)讓頁(yè)面加載時(shí)請(qǐng)求后臺(tái)接口數(shù)據(jù)2022-08-08

