LOADING...
Mixin设计思想和应用
公众文章 js常见问题 js杂项
作者:TANGJIE 查看:43 发布时间:2020-05-30
简介: 最近看weui的源码,看到less的mixin部分,突然想要仔细了解一下这个东西

Mixin设计思想和应用

1.基本概念

Mixin 就是 混入的意思,主要是为了解决多重继承 带来复杂继承链的问题,或者说是多重继承实现的一种技巧

2.基本代码示例

以 廖雪峰的官方网站中的 Animal 类层次设计为例,有下面4个动物:

  • Dog - 狗狗
  • Bat - 蝙蝠
  • Parrot - 鹦鹉
  • Ostrich - 鸵鸟

此类设计中有多种设计方法,比如根据哺乳动物和鸟类归类 或者 按照“能跑”和“能飞”来归类,其设计类如下:

//按哺乳动物和鸟类归类 class Animal{ public callname():void{ console.log('我是动物') } } class Mammal extends Animal{ public skil_m():void{ console.log('我出生后要吃奶') } } class Bird extends Animal{ public skil_b():void{ console.log('我长大后能飞') } } class Dog extends Mammal{ public skill_d():void{ console.log('我是单身狗') } } class Bat extends Mammal{ public skil_mb():void{ console.log('我是蝙蝠') } } class Parrot extends Bird{ public skil_p():void{ console.log('我是鹦鹉') } } class Ostrich extends Bird{ public skil_o():void{ console.log('我是鸵鸟') } }
class Animal{ public callname():void{ console.log('我是动物') } } class Run extends Animal{ public skil_r():void{ console.log('我是地上跑的') } } class Fly extends Animal{ public skil_f():void{ console.log('是天上飞的') } } class Dog extends Run{ public skill_dr():void{ console.log('我是能跑的单身狗') } } class Bat extends Fly{ public skil_mf():void{ console.log('我是蝙蝠,可能不会飞') } } class Parrot extends Fly{ public skil_pr():void{ console.log('我是鹦鹉') } } class Ostrich extends Run{ public skil_or():void{ console.log('我是鸵鸟,不会飞智慧跑') } }

如果再多其他几种分类,如宠物类”和“非宠物类”,这结构会越来越庞大,这种类设计完全是不可取的,正确的方法,我们应该采用多重继承来完成。

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。`

3.mixin在less的表现

在 Less 中, 可以定义一种叫 “mixins” 的东西 — 和编程语言中的函数有一点的相似。
在 Less 中, 它被用来灵活的组件化 CSS 中可重复使用的 class。 Mixin 允许你把一个 class 的所有属性嵌入到另外的一个 class 中 —— 就像它的一个属性一样, 仅仅通过简单的 include class 的名字。 它仅仅是一个变量,但是拥有一个 class 的属性。 任何的 CSS class 和 id 规则都可以混在里面。

copy官网示例:

.bordered { border-top: dotted 1px black; border-bottom: solid 2px black; }

如果希望在其它规则集中使用这些属性呢?没问题,我们只需像下面这样输入所需属性的类(class)名称即可,如下所示:

#menu a { color: #111; .bordered(); } .post a { color: red; .bordered(); }

.bordered 类所包含的属性就将同时出现在 #menu a 和 .post a 中了。(注意,你也可以使用 #ids 作为 mixin 使用。)

4. mixin在vue的表现

在vue中混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

例子我就不举明了,vue官网有详细的教程,vue官网;

4.1 简单说明

先创建混入对象

export const mixinstest = { data(){ return { testMix: '混入对象的data' } }, created(){ console.log('这是混入对象的created') }, methods:{ mixinsFun(){ console.log('调用混入对象的methods的函数') } }, computed:{ testMix2(){ return this.testMix+':这是混入对象计算结果。' } }, watch: { testMix(newVal,oldVal){ console.log('混入对象的watch') } } }

再在组件内引入并引用混入

<template> <div> <div>{{testMix}}</div> <div>{{testMix}}</div> <input type="text" v-model="testMix"> <div>{{testMix2}}</div> </div> </template> <script> import {mixinstest} from '../../mixins/index' export default { mixins: [mixinstest], data (){ return { testMix:'这是组件的数据' } }, created(){ console.log('这是组件的created') }, methods: { mixinsFun(){ console.log('调用组件的methods的函数') } }, computed:{ testMix2(){ return this.testMix+':这是组件计算结果' } }, watch: { testMix(newVal,oldVal){ console.log('组件的watch') } } } </script>

【注意:】

  • a、当在组件中和混入中有相同的‘testMix’这个数据时,显示组件中’testMix’对应的数据,当只用混入中有’testMix‘函数时,显示混入中’testMix’对应的数据。
  • b、在组件中和混入中有相同的函数mixinsFun()时,在组件中调用时,调用的是组件中的mixinsFun()函数,当只用混入中有mixinsFun()函数时,在组件中调用mixinsFun()是调用混入中的。
  • c、在组件中和混入中有相同的computed函数testMix2()时,在组件中调用时,调用的是组件中的testMix2()函数,当只用混入中有computed函数testMix2()时,在组件中调用testMix2()是调用混入中的。
  • d、在组件中和混入中有相同的created()函数时,先执行混入中的created,再执行组件中的created。其他生命周期也应该是一样。
  • e、在组件中和混入中有相同的watch()函数testMix时,先执行混入中watch的testMix,再执行组件中watch的testMix。

5. react放弃mixins的设计模式

  • a、mixins 引入了不清晰的依赖关系
  • b、mixins 导致命名空间的冲突
  • c、Mixins 导致滚雪球般的复杂度

6.在小程序中实现 Mixins 方案

虽然mixin设计有其缺点,但是在小规模项目中,优点很明确,尤其是小程序,毕竟小程序你在怎么做,其规模不会很大,mixin就能解决在小程序开发中很多痛点

const originPage = Page; const originProperties = ['data', 'properties', 'options']; const originMethods = ['onLoad', 'onReady', 'onShow', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap']; function merge (mixins, options) { mixins.forEach((mixin) => { if (Object.prototype.toString.call(mixin) !== '[object Object]') { throw new Error('mixin 类型必须为对象!') } for (let [key, value] of Object.entries(mixin)) { if (originProperties.includes(key)) { options[key] = { ...value, ...options[key] } } else if (originMethods.includes(key)) { const originFunc = options[key]; options[key] = function (...args) { value.call(this, ...args); return originFunc && originFunc.call(this, ...args) } } else { options = { ...mixin, ...options } } } }); return options } Page = (options) => { const mixins = options.mixins; if (Array.isArray(mixins)) { delete options.mixins; options = merge(mixins, options) } originPage(options) };

文章评论

历史评论

暂无评论,赶紧来抢沙发吧!