在 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 相关文章推荐
js event事件的传递与冒泡处理
Dec 06 Javascript
体验js中splice()的强大(插入、删除或替换数组的元素)
Jan 16 Javascript
JavaScript字符串对象的concat方法实例(用于连接两个或多个字符串)
Oct 16 Javascript
Javascript基础教程之定义和调用函数
Jan 18 Javascript
Bootstrap编写一个兼容主流浏览器的受众门户式风格页面
Jul 01 Javascript
zTree树形插件异步加载方法详解
Jun 14 Javascript
JS实现移动端按首字母检索城市列表附源码下载
Jul 05 Javascript
JS中实现隐藏部分姓名或者电话号码的代码
Jul 17 Javascript
实例详解vue中的$root和$parent
Apr 29 Javascript
countUp.js实现数字滚动效果
Oct 18 Javascript
javascript实现倒计时效果
Feb 17 Javascript
react-router-dom 嵌套路由的实现
May 02 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实现的AES加密类定义与用法示例
2018/01/29 PHP
纯js分页代码(简洁实用)
2013/11/05 Javascript
JavaScript模拟实现继承的方法
2015/03/30 Javascript
JavaScript Math 对象常用方法总结
2016/04/28 Javascript
js生成随机数方法和实例
2017/01/17 Javascript
ThinkPHP+jquery实现“加载更多”功能代码
2017/03/11 Javascript
使用async、enterproxy控制并发数量的方法详解
2018/01/02 Javascript
Vuejs 单文件组件实例详解
2018/02/09 Javascript
vue多页面开发和打包正确处理方法
2018/04/20 Javascript
Vue.js实现的购物车功能详解
2019/01/27 Javascript
浅谈layer弹出层按钮颜色修改方法
2019/09/11 Javascript
微信小程序swiper实现文字纵向轮播提示效果
2020/01/21 Javascript
使用node.JS中的url模块解析URL信息
2020/02/06 Javascript
[02:53]DOTA2英雄基础教程 山岭巨人小小
2013/12/09 DOTA
python中根据字符串调用函数的实现方法
2016/06/12 Python
Python简单实现查找一个字符串中最长不重复子串的方法
2018/03/26 Python
Python定时任务sched模块用法示例
2018/07/16 Python
matplotlib调整子图间距,调整整体空白的方法
2018/08/03 Python
利用Python正则表达式过滤敏感词的方法
2019/01/21 Python
Python使用到第三方库PyMuPDF图片与pdf相互转换
2019/05/03 Python
python 实现一个简单的线性回归案例
2020/12/17 Python
opencv python 对指针仪表读数识别的两种方式
2021/01/14 Python
美国性感内衣店:Yandy
2018/06/12 全球购物
编写类String 的构造函数、析构函数和赋值函数
2012/09/09 面试题
Linux中如何用命令创建目录
2016/12/02 面试题
渡河少年教学反思
2014/02/12 职场文书
企业趣味活动方案
2014/08/21 职场文书
酒店七夕情人节活动策划方案
2014/08/24 职场文书
美术教师个人工作总结
2015/02/06 职场文书
行政助理岗位职责范本
2015/04/11 职场文书
制定企业培训计划的五大要点!
2019/07/10 职场文书
《西游记》读后感(3篇)
2019/09/20 职场文书
解决Jenkins集成SonarQube遇到的报错问题
2021/07/15 Java/Android
python人工智能human learn绘图可创建机器学习模型
2021/11/23 Python
MySQL之MyISAM存储引擎的非聚簇索引详解
2022/03/03 MySQL
InterProcessMutex实现zookeeper分布式锁原理
2022/03/21 Java/Android