在 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 相关文章推荐
去除链接虚线全面分析总结
Aug 15 Javascript
经常用的图片在容器中的水平垂直居中实例
Jun 10 Javascript
JavaScript中获取未知对象属性的代码
Apr 27 Javascript
jQuery1.6 使用方法一
Nov 23 Javascript
button没写type=button会导致点击时提交
Mar 06 Javascript
JavaScript实现同步于本地时间的动态时间显示方法
Feb 02 Javascript
浅谈Node.js 沙箱环境
May 15 Javascript
js的各种数据类型判断的介绍
Jan 19 Javascript
layui table数据修改的回显方法
Sep 04 Javascript
JS合并两个数组的3种方法详解
Oct 24 Javascript
简单了解JS打开url的方法
Feb 21 Javascript
js+css3实现简单时钟特效
Sep 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
PHP IDE phpstorm 常用快捷键
2015/05/18 PHP
PHP弹出对话框技巧详细解读
2015/09/26 PHP
Symfony2实现从数据库获取数据的方法小结
2016/03/18 PHP
php实现的错误处理封装类实例
2017/06/20 PHP
浅谈PHP无限极分类原理
2019/03/14 PHP
Laravel框架实现多个视图共享相同数据的方法详解
2019/07/09 PHP
记录几个javascript有关的小细节
2007/04/02 Javascript
JavaScript 实现类的多种方法实例
2013/05/01 Javascript
表单提交前触发函数返回true表单才会提交
2014/03/11 Javascript
js实现按钮加背景图片常用方法
2014/11/01 Javascript
基于Jquery实现焦点图淡出淡入效果
2015/11/30 Javascript
JQuery日期插件datepicker的使用方法
2016/03/03 Javascript
JavaScript实现简单Tip提示框效果
2016/04/20 Javascript
IScroll5 中文API参数说明和调用方法
2016/05/21 Javascript
JavaScript中Form表单技术汇总(推荐)
2016/06/26 Javascript
JS正则替换掉小括号及内容的方法
2016/11/29 Javascript
jQuery使用ajax方法解析返回的json数据功能示例
2017/01/10 Javascript
原生js实现吸顶效果
2017/03/13 Javascript
使用cropper.js裁剪头像的实例代码
2017/09/29 Javascript
angular.js实现购物车功能
2017/10/23 Javascript
ES6中let 和 const 的新特性
2018/09/03 Javascript
详解如何在vue项目中使用layui框架及采坑
2019/05/05 Javascript
js实现小时钟效果
2020/03/25 Javascript
Python实战购物车项目的实现参考
2019/02/20 Python
python使用Plotly绘图工具绘制水平条形图
2020/03/25 Python
Pyqt5如何让QMessageBox按钮显示中文示例代码
2019/04/11 Python
Python利用matplotlib做图中图及次坐标轴的实例
2019/07/08 Python
Django上线部署之IIS的配置方法
2019/08/22 Python
微软澳洲官方网站:Microsoft Australia
2017/01/10 全球购物
Johnston & Murphy官网: 约翰斯顿·墨菲牛津总统鞋
2018/01/09 全球购物
德国运动营养和健身网上商店:Myprotein.de
2018/07/18 全球购物
Cinque网上商店:德国服装品牌
2019/03/17 全球购物
Vans澳大利亚官网:购买鞋子、服装及配件
2019/09/05 全球购物
Moda Operandi官网:美国奢侈品电商,海淘秀场T台同款
2020/05/26 全球购物
PostgreSQL聚合函数介绍以及分组和排序
2022/04/12 PostgreSQL
在ubuntu下安装go开发环境的全过程
2022/08/05 Golang