在 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 相关文章推荐
javascript hashtable实现代码
Oct 13 Javascript
关于IE BUG与字符串截取substr的解决办法
Apr 10 Javascript
jQuery实现行文字链接提示效果的方法
Mar 10 Javascript
jquery弹出遮掩层效果【附实例代码】
Apr 28 Javascript
详解在vue-cli中使用路由
Sep 25 Javascript
20个最常见的jQuery面试问题及答案
May 23 jQuery
Vue源码解读之Component组件注册的实现
Aug 24 Javascript
详解node登录接口之密码错误限制次数(含代码)
Oct 25 Javascript
webpack常用构建优化策略小结
Nov 21 Javascript
通过实例解析JavaScript for in及for of区别
Jun 15 Javascript
js实现从右往左匀速显示图片(无缝轮播)
Jun 29 Javascript
React forwardRef的使用方法及注意点
Jun 13 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
ucenter中词语过滤原理分析
2016/07/13 PHP
Yii2 批量插入、更新数据实例
2017/03/15 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
CI框架(CodeIgniter)操作redis的方法详解
2018/01/25 PHP
jQuery live( type, fn ) 委派事件实现
2009/10/11 Javascript
Aptana调试javascript图解教程
2009/11/30 Javascript
jQuery 处理表单元素的代码
2010/02/15 Javascript
JS声明变量背后的编译原理剖析
2012/12/28 Javascript
当鼠标移动到图片上时跟随鼠标显示放大的图片效果
2013/06/06 Javascript
jquery配合css简单实现返回顶部效果
2013/09/30 Javascript
javascript创建和存储cookie示例
2014/01/07 Javascript
Jquery插件分享之气泡形提示控件grumble.js
2014/05/20 Javascript
easyui简介_动力节点Java学院整理
2017/07/14 Javascript
js canvas实现适用于移动端的百分比仪表盘dashboard
2017/07/18 Javascript
jQuery阻止事件冒泡实例分析
2018/07/03 jQuery
微信小程序发布新版本时自动提示用户更新的方法
2019/06/07 Javascript
解决elementui表格操作列自适应列宽
2020/12/28 Javascript
简述Python中的面向对象编程的概念
2015/04/27 Python
Python判断直线和矩形是否相交的方法
2015/07/14 Python
django使用xlwt导出excel文件实例代码
2018/02/06 Python
Python批量提取PDF文件中文本的脚本
2018/03/14 Python
python处理csv中的空值方法
2018/06/22 Python
django2+uwsgi+nginx上线部署到服务器Ubuntu16.04
2018/06/26 Python
使用selenium和pyquery爬取京东商品列表过程解析
2019/08/15 Python
Python3 JSON编码解码方法详解
2019/09/06 Python
python通过SSH登陆linux并操作的实现
2019/10/10 Python
容易被忽略的Python内置类型
2020/09/03 Python
美国地毯购买网站:Rugs USA
2019/02/23 全球购物
eBay加拿大站:eBay.ca
2019/06/20 全球购物
关于安全的演讲稿
2014/05/09 职场文书
2014年银行柜员工作总结
2014/11/12 职场文书
2014-2015学年工作总结
2014/11/27 职场文书
2015年学校保卫部工作总结
2015/05/11 职场文书
CSS 新特性 contain控制页面的重绘与重排问题
2021/04/30 HTML / CSS
MySQL中日期型单行函数代码详解
2021/06/21 MySQL
Python捕获、播放和保存摄像头视频并提高视频清晰度和对比度
2022/04/14 Python