After moving to writing Vue applications with Typescript, after a round of toolchain and dependency baptism, I finally got staggeringly. However, there is a very commonly used function mixin, which seems to have no official solution.
I want to enjoy the flexibility and convenience of mixin, but also want to gain the security guarantees brought by the ts type system and the smooth experience of using IntelliSense during development.
There is a vuejs official organization'vue-class-component'
And recommended'vue-property-decorator'
, none of them were implemented accordingly. I looked through the former issue and there was a feature that I had been waiting for a long time. It was the support of mixin.
It's not a complicated matter, just write one yourself.
Note: vue-class-component 6.2.0 begins to provide mixins method, which is similar to the implementation idea of this article.
accomplish
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 ({ mixins: traits }) }
Declare VClass<T> as a class constructor for T. At the same time, you can get the static method (extend/mixin, etc.) on the Vue constructor through Pick. Only in this way can the real implementation in the following paragraph be supported, and a new subclass constructor is generated by calling the extend method on the Vue subclass constructor.
function Mixins<T>(c: VClass<T>, ...traits: Array<VClass<T>>): VClass<T> { return ({ mixins: traits }) }
As for ABC, this is purely a physical job of type declaration.
use
When used in actual use:
import { Component, Vue } from 'vue-property-decorator' import { Mixins } from '../../util/mixins' @Component class PageMixin extends Vue { title = 'Test Page' redirectTo(path: string) { ('calling reidrectTo', path) this.$({ path }) } } interface IDisposable { dispose(...args: any[]): any } class DisposableMixin extends Vue { _disposables: IDisposable[] created() { ('disposable mixin created'); this._disposables = [] } beforeDestroy() { ('about to clear disposables') this._disposables.map((d) => { () }) 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 (++ >= 3) { return ('/otherpage') } ('count to', ); }, 1000) ({ dispose() { clearInterval(timer) } }) } } count to 1 count to 2 count to 3 calling reidrectTo /otherpage about to clear disposables
Note that the DisposableMixin of Vue directly extends Vue is not a valid Vue component, nor can it be used directly in the mixins option. If you want to be used by custom components that are extended in a way, remember to wrap a layer using Component.
const ExtendedComponent = ({ name: 'ExtendedComponent', mixins: [Component(DisposableMixin)], })
Abstract class
Mixin, which will be used in business systems, is actually more complicated in most cases and provides some basic functions, but some parts need to be left to the successor to implement themselves. It is appropriate to use abstract classes at this time.
abstract class AbstractMusicPlayer extends Vue { abstract audioSrc: string playing = false togglePlay() { = ! } } class MusicPlayerA extends AbstractMusicPlayer { audioSrc = '/audio-a.mp3' } class MusicPlayerB extends AbstractMusicPlayer { staticBase = '/statics' get audioSrc() { return `${}/audio-b.mp3` } }
But abstract classes cannot be instantiated and are not satisfied{ new(): T }
This requirement can only be inherited, not mixed in, and for the same reason, abstract classes cannot be'vue-class-component'
Component function decoration.
At this time, I had to write the implemented functions into Mixin, put the functions to be implemented in the interface, and let the specific classes implement them.
interface IMusicSourceProvider { audioSrc: string } /** * @implements IPlayerImplementation */ class PlayerMixin extends Vue { /** @abstract */ audioSrc: string logSrc() { () } } interface IPlayerImplementation extends IMusicSourceProvider {} class RealPlayer extends Mixins(PlayerMixin) implements IPlayerImplementation { audioSrc = '/audio-c.mp3' }
This way of cheating the compiler is actually quite clumsy. If a specific class inherits PlayerMixin but does not display the declaration to implement IPlayerImplementation, the compiler cannot tell you this error. We can only write comments carefully in the code, and we hope that users will not forget this.
Summarize
The above is what the editor introduced to you to use the reusable Vue Mixin function in Typescript. I hope it will be helpful to everyone. If you have any questions, please leave me a message and the editor will reply to everyone in time. Thank you very much for your support for my website!