前端框架中 “类mixin” 模式的思考
Posted lovellll
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端框架中 “类mixin” 模式的思考相关的知识,希望对你有一定的参考价值。
“类 mixin” 指的是 Vue 中的 mixin,Regular 中的 implement
使用 Mixin 的目的
首先我们需要知道为什么会有 mixin
的存在?
-
为了扩展对象(组件)的功能
扩展对象功能的需求是很容易的理解的。比如业务开发时会碰到跨模块传递消息的需求,常用的方法是使用 “发布-订阅模式” 来创建一个全局的
``` // global/eventEmitter class EventEmitter { on(eventName, handle) { } emit(eventName) { } } // ComponentA import EventEmitter from ‘global/eventEmitter‘; // 全局的 EventEmitter 对象 class ComponentA { constructor() { EventEmitter.on(‘event‘, () => {}); } } // ComponentB import EventEmitter from ‘global/eventEmitter‘; class ComponentB { constructor() { EventEmitter.emit(‘event‘); } } ```EventEmitter
。不使用mixin
时的使用方法如下我们需要在不同的组件中引入
EventEmitter
来使用它如果使用
``` // eventEmitter mixin import EventEmitter from ‘global/eventEmitter‘; // 全局的 EventEmitter 对象 const eventEmitterMixin = { on: (eventName, handle) => {EventEmitter.on(eventName, handle)}, fire: () => {EventEmitter.emit(eventName)} } // Root Component Component.mixin(eventEmitterMixin); // ComponentA class ComponentA { constructor() { this.on(‘event‘, () => {}); } } // ComponentB class ComponentB { constructor() { this.emit(‘event‘); } } ```mixin
的话,我们可以这样做这样通过
mixin
来扩展了组件的功能,是每个组件都可以方便的使用EventEmitter
的功能 - 为了复用代码
软件开发中的
DRY
原则还是有必要遵守的。过多的重复代码会导致维护上的麻烦,通过mixin
,我们可以在不同的对象上使用同一份代码来完成相同的功能,减轻我们维护的压力。
复用 VS 扩展
其实这两个没有可比性,但在我们决定是否需要将一个对象通过 mixin
的方式混入到其他对象时就应该考虑这个问题。
原则上 复用 > 扩展。
如果一个 mixin
只是为了扩展单个对象的功能,而扩展的功能并不能复用到其他的对象时,就不应该使用 mixin
,而是直接写在那个对象上更好。
如果扩展的功能可以被复用的话,那么可以考虑使用 mixin
。
mixin 的缺点
mixins-considered-harmful 这篇文章已经列举了一些问题,我简单的列举下
-
mixin
会引入隐性的依赖关系 -
mixin
会导致命名冲突 -
mixin
会增加项目的复杂性
如何正确的使用 Mixin
虽然mixin
有一些缺点,但正确的使用还是可以方便我们的开发没有不合理的设计模式,只有不合理的使用设计模式
首先我们需要解决混入的 mixin
可能会造成隐性的依赖关系,而形成这种依赖关系多半是 mixin
中的扩展功能依赖了被扩展对象想内部数据,例如:
// 一个数据库查看组件
class DatabaseView {
state = {
database: [{name: ‘db1‘, id: 1, tables: [{name: ‘table1‘, id: 1}]}]
}
}
DatabaseView.mixin(viewTabelDetails);
// 把查看表详情弹窗作为 mixin 混入
const mixin = {
viewTabelDetails: (dbId, tableId) => {
const db = this.state.database.filter(db => db.id === dbId)[0];
if (db) {
const table = db.tables.filter(table => table.id === tableId)[0];
if (table) {
Model.show(table);
}
}
}
}
上面的例子中,mixin
依赖的 DarabaseView
中的 database
数据。这就导致了如果页面中其他的组件也需要一个查看表详情弹窗的功能,那么这个组件也必须有类似的 database
数据,形成了一个隐性的依赖关系。
避免的方式就是 mixin
对象中的功能不要与被扩展对象发生依赖,而在组件开发中这个依赖多半就是使用了被扩展对象的 state
产生的
至于命名冲突和增加项目的复杂性可以通过其他的方式解决,相比于上面的问题还算简单
总结
-
mixin
只是用来扩展对象功能的,而且这个扩展功能是可以被复用的,否则你应该直接写在对象里面 - 扩展功能的同时,
mixin
提供的函数不应该依赖被扩展对象的内部数据。因为如果依赖的被扩展对象的内部数据,会使这个mixin
只能被包含特定数据对象的对象复用,影响mixin
的复用
个人主观观点
其实我觉得在前端的开发中,我们应该避免使用 mixin
去扩展组件的功能
例如我们完全可以使用函数调用(显性的)去调用 mixin
对象中的方法,而不是一股脑的将 mixin
对象混合到组件中
每次修改使用了 implement
组件都会特别痛苦(这样导入的方法 WebStorm 根本不识别),极大了增加维护的工作量。同时由于有的 mixin
对象依赖的被扩展对象的内部数据,导致想复用的话还得有相同的数据结构(那还复用个锤子啊)
所以,不要用 mixin
!!!
更多文章可以查看本人的博客 https://lleohao.github.io
来源:https://segmentfault.com/a/1190000017554489
以上是关于前端框架中 “类mixin” 模式的思考的主要内容,如果未能解决你的问题,请参考以下文章