在 Typescript中如何使用可被复用的 Vue Mixin功能

小编给大家分享一下在 Typescript中如何使用可被复用的 Vue Mixin功能,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

转到用 Typescript 写 Vue 应用以后,经过一轮工具链和依赖的洗礼,总算蹒跚地能走起来了,不过有一个很常用的功能 mixin,似乎还没有官方的解决方案。

既想享受 mixin 的灵活和方便,又想收获 ts 的类型系统带来的安全保障和开发时使用 IntelliSense 的顺滑体验。

vuejs 官方组织里有一个 'vue-class-component' 以及连带推荐的 'vue-property-decorator',都没有相应实现。翻了下前者的 issue,有一条挂了好些时间的待做 feature 就是 mixin 的支持。

也不是什么复杂的事,自己写一个吧。

后注:vue-class-component 6.2.0 开始提供 mixins 方法,和本文的实现思路相似。

实现

importVue,{VueConstructor}from'vue'
exporttypeVClass<T>={
new():T
}&Pick<VueConstructor,keyofVueConstructor>
/**
*mixinsforclassstylevuecomponent
*/
functionMixins<A>(c:VClass<A>):VClass<A>
functionMixins<A,B>(c:VClass<A>,c1:VClass<B>):VClass<A&B>
functionMixins<A,B,C>(c:VClass<A>,c1:VClass<B>,c2:VClass<C>):VClass<A&B&C>
functionMixins<T>(c:VClass<T>,...traits:Array<VClass<T>>):VClass<T>{
returnc.extend({
mixins:traits
})
}

声明 VClass<T> 可作为 T 的类构造器。同时通过 Pick 拿到 Vue 的构造器上的静态方法(extend/mixin 之类),如此才能够支持下面这段中的真正实现,通过调用一个 Vue 的子类构造器上的 extend 方法生成新的子类构造器。

functionMixins<T>(c:VClass<T>,...traits:Array<VClass<T>>):VClass<T>{
returnc.extend({
mixins:traits
})
}

至于 ABC 这个纯粹是类型声明的体力活了。

使用

实际使用时:

import{Component,Vue}from'vue-property-decorator'
import{Mixins}from'../../util/mixins'
@Component
classPageMixinextendsVue{
title='TestPage'
redirectTo(path:string){
console.log('callingreidrectTo',path)
this.$router.push({path})
}
}
interfaceIDisposable{
dispose(...args:any[]):any
}
classDisposableMixinextendsVue{
_disposables:IDisposable[]
created(){
console.log('disposablemixincreated');
this._disposables=[]
}
beforeDestroy(){
console.log('abouttocleardisposables')
this._disposables.map((d)=>{
d.dispose()
})
deletethis._disposables
}
registerDisposable(d:IDisposable){
this._disposables.push(d)
}
}
@Component({
template:`
<div>
<h2>{{title}}</h2>
<p>Counted:{{counter}}</p>
</div>
`
})
exportdefaultclassTimerPageextendsMixins(PageMixin,DisposableMixin){
counter=0
mounted(){
consttimer=setInterval(()=>{
if(this.counter++>=3){
returnthis.redirectTo('/otherpage')
}
console.log('countto',this.counter);
},1000)

this.registerDisposable({
dispose(){
clearInterval(timer)
}
})
}
}
countto1
countto2
countto3
callingreidrectTo/otherpage
abouttocleardisposables

注意到直接 extends Vue 的 DisposableMixin 并不是一个有效的 Vue 组件,也不可以直接在 mixins 选项里使用,如果要被以 Vue.extend 方式扩展的自定义组件使用,记住使用 Component 包装一层。

constExtendedComponent=Vue.extend({
name:'ExtendedComponent',
mixins:[Component(DisposableMixin)],
})

Abstract class

在业务系统中会使用到的 Mixin 其实多数情况下会更复杂,提供一些基础功能,但有些部分需要留给继承者自行实现,这个时候使用抽象类就很合适。

abstractclassAbstractMusicPlayerextendsVue{
abstractaudioSrc:string
playing=false
togglePlay(){
this.playing=!this.playing
}
}
classMusicPlayerAextendsAbstractMusicPlayer{
audioSrc='/audio-a.mp3'
}
classMusicPlayerBextendsAbstractMusicPlayer{
staticBase='/statics'
getaudioSrc(){
return`${this.staticBase}/audio-b.mp3`
}
}

但抽象类是无法被实例化的,并不满足 { new(): T } 这个要求,因此只能被继承,而不能被混入,由于同样的原因,抽象类也无法被 'vue-class-component' 的 Component 函数装饰。

这时候只好将实现了的功能写入 Mixin 中,待实现的功能放到接口里,让具体类来实现。

interfaceIMusicSourceProvider{
audioSrc:string
}
/**
*@implementsIPlayerImplementation
*/
classPlayerMixinextendsVue{
/**@abstract*/
audioSrc:string
logSrc(){
console.log(this.audioSrc)
}
}
interfaceIPlayerImplementationextendsIMusicSourceProvider{}
classRealPlayerextendsMixins(PlayerMixin)implementsIPlayerImplementation{
audioSrc='/audio-c.mp3'
}

这种欺骗编译器的方式其实还是比较拙劣的,如果一个具体类继承了 PlayerMixin,却没有显示声明实现 IPlayerImplementation ,编译器无法告诉你这个错误。我们只能在代码里小心翼翼写上注释,期待使用者不要忘了这件事。

看完了这篇文章,相信你对“在 Typescript中如何使用可被复用的 Vue Mixin功能”有了一定的了解,如果想了解更多相关知识,欢迎关注恰卡编程网行业资讯频道,感谢各位的阅读!

发布于 2021-07-09 21:18:15
分享
海报
162
上一篇:如何使用express+multer实现node中的图片上传功能 下一篇:vue项目中怎么导入swiper插件
目录

    推荐阅读

    忘记密码?

    图形验证码