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


Posted in Javascript onApril 17, 2018

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

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

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

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

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

实现

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

function Mixins<T>(c: VClass<T>, ...traits: Array<VClass<T>>): VClass<T> {
 return c.extend({
  mixins: traits
 })
}

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

使用

实际使用时:

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 并不是一个有效的 Vue 组件,也不可以直接在 mixins 选项里使用,如果要被以 Vue.extend 方式扩展的自定义组件使用,记住使用 Component 包装一层。

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

Abstract class

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

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`
 }
}

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

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

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'
}

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

总结

以上所述是小编给大家介绍的在 Typescript 中使用可被复用的 Vue Mixin功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery教程 $()包装函数来实现数组元素分页效果
Aug 13 Javascript
IE中getElementsByName()对有些元素无效的解决方案
Sep 28 Javascript
元素绑定click点击事件方法
Jun 08 Javascript
JQuery替换DOM节点的方法
Jun 11 Javascript
简单实现异步编程promise模式
Jul 31 Javascript
jquery选择器简述
Aug 31 Javascript
关于vuex的学习实践笔记
Apr 05 Javascript
Angular4 ElementRef的应用
Feb 26 Javascript
webpack4.x CommonJS模块化浅析
Nov 09 Javascript
JavaScript禁用右键单击优缺点分析
Jan 20 Javascript
详解vue 中 scoped 样式作用域的规则
Sep 14 Javascript
JS setTimeout与setInterval的区别
Apr 20 Javascript
vue iview实现动态路由和权限验证功能
Apr 17 #Javascript
基于VuePress 轻量级静态网站生成器的实现方法
Apr 17 #Javascript
Vue-cropper 图片裁剪的基本原理及思路讲解
Apr 17 #Javascript
js闭包学习心得总结
Apr 17 #Javascript
Vue使用json-server进行后端数据模拟功能
Apr 17 #Javascript
js实现点击按钮复制文本功能
Jul 20 #Javascript
Element-UI Table组件上添加列拖拽效果实现方法
Apr 14 #Javascript
You might like
php Ajax乱码
2008/04/09 PHP
thinkPHP框架实现多表查询的方法
2018/06/14 PHP
php实现多站点共用session实现单点登录的方法详解
2019/09/18 PHP
表单填写时用回车代替TAB的实现方法
2007/10/09 Javascript
基于Jquery的简单&amp;简陋Tabs插件代码
2010/02/09 Javascript
JQuery为textarea添加maxlength属性并且兼容IE
2013/04/25 Javascript
js判断字符是否是汉字的两种方法小结
2014/01/03 Javascript
jsp网页搜索结果中实现选中一行使其高亮
2014/02/17 Javascript
使用jquery animate创建平滑滚动效果(可以是到顶部、到底部或指定地方)
2014/05/27 Javascript
浅谈jQuery中的事件
2015/03/23 Javascript
jquery分割字符串的方法
2015/06/24 Javascript
jQuery 操作input中radio的技巧
2016/07/18 Javascript
html+js+highcharts绘制圆饼图表的简单实例
2016/08/04 Javascript
浅谈jquery的html方法里包含特殊字符的处理
2016/11/30 Javascript
详解webpack进阶之loader篇
2017/08/23 Javascript
node 命令方式启动修改端口的方法
2018/05/12 Javascript
js实现下拉框二级联动
2018/12/04 Javascript
详解keep-alive + vuex 让缓存的页面灵活起来
2019/04/19 Javascript
LayUi数据表格自定义赋值方式
2019/10/26 Javascript
Python存取XML的常见方法实例分析
2017/03/21 Python
python正则实现计算器功能
2017/12/14 Python
详解python OpenCV学习笔记之直方图均衡化
2018/02/08 Python
Python读取excel指定列生成指定sql脚本的方法
2018/11/28 Python
对python中的os.getpid()和os.fork()函数详解
2019/08/08 Python
解决python调用自己文件函数/执行函数找不到包问题
2020/06/01 Python
Python简单实现词云图代码及步骤解析
2020/06/04 Python
使用Keras画神经网络准确性图教程
2020/06/15 Python
python 读取串口数据的示例
2020/11/09 Python
Silk Therapeutics官网:清洁、抗衰老护肤品
2020/08/12 全球购物
linux面试题参考答案(4)
2014/09/21 面试题
金融专业推荐信
2013/11/14 职场文书
模具设计与制造专业推荐信
2014/02/16 职场文书
公务员年终个人总结
2015/02/12 职场文书
个人优缺点总结
2015/02/28 职场文书
Python基础之教你怎么在M1系统上使用pandas
2021/05/08 Python
JavaScript严格模式不支持八进制的问题讲解
2021/11/07 Javascript