摘要:概念模式就是一些提供能够被一个或者一组子类简单继承功能的类意在重用其功能。示例下面通过一个简单的例子来演示这个模式混入模式的实现不指定特定方法名的时候,将后者所有的方法都添加到前者里优缺点优点有助于减少系统中的重复功能及增加函数复用。
概念
Mixin模式就是一些提供能够被一个或者一组子类简单继承功能的类,意在重用其功能。在面向对象的语言中,我们会通过接口继承的方式来实现功能的复用。但是在javascript中,我们没办法通过接口继承的方式,但是我们可以通过javascript特有的原型链属性,将功能引用复制到原型链上,达到功能的注入。
示例下面通过一个简单的例子来演示这个模式
var Car = function(settings) { this.model = settings.model || "no model provided" this.color = settings.color || "no color provided" } var Mixin = function() {} Mixin.prototype = { driveForward: function() { console.log("drive forward") }, driveBackward: function() { console.log("drive backward") }, driveSideways: function() { console.log("drive sideways") } } //混入模式的实现 function Merge(recClass, giveClass) { if(arguments.length > 2) { for(var i = 2, lenth = arguments.length; i < lenth ; ++ i) { var methodName = arguments[i] recClass.prototype[methodName] = giveClass.prototype[methodName] } }else { for(var methodName in giveClass.prototype) { if(!recClass.prototype[methodName]) { recClass.prototype[methodName] = giveClass.prototype[methodName] } } } } Merge(Car, Mixin, "driveForward", "driveBackward") var myCar = new Car({ model: "BMW", color: "blue" }) myCar.driveForward() //drive forward myCar.driveBackward() //drive backward //不指定特定方法名的时候,将后者所有的方法都添加到前者里 Merge(Car, Mixin) var mySportsCar = new Car({ model: "Porsche", color: "red" }) mySportsCar.driveForward() //drive forward优缺点
优点
有助于减少系统中的重复功能及增加函数复用。当一个应用程序可能需要在各种对象实例中共享行为时,我们可以通过在Mixin中维持这种共享功能并专注于仅实现系统中真正不同的功能,来轻松避免任何重复。
缺点
有些人认为将功能注入对象原型中会导致原型污染和函数起源方面的不确定性。
vue中关于混入的代码目录/src/core/global-api/mixin.js
可以看到vue源码中是通过mergeOptions来合并配置到options上
export function initMixin (Vue: GlobalAPI) { Vue.mixin = function (mixin: Object) { this.options = mergeOptions(this.options, mixin) return this } }
下面我们来看合并配置的相关代码/src/core/instance/init.js
当执行new Vue的时候options._isComponent为false,会走else的分支
export function initMixin (Vue: Class) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { vm.$options = mergeOptions( //合并配置 resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== "production") { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, "beforeCreate") initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, "created") /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
下面来看mergeOptions的实现src/core/util/options.js
mergeField将this.options(parent)和需要混入的对象mixin(child)合并在this.options上面
/** * Merge two option objects into a new one. * Core utility used in both instantiation and inheritance. */ export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { if (process.env.NODE_ENV !== "production") { checkComponents(child) } if (typeof child === "function") { child = child.options } normalizeProps(child, vm) normalizeInject(child, vm) normalizeDirectives(child) const extendsFrom = child.extends if (extendsFrom) { parent = mergeOptions(parent, extendsFrom, vm) } if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm) } } const options = {} let key for (key in parent) { mergeField(key) } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } return options }参考
《JavaScript设计模式》
JS设计模式系列文章JS设计模式之Obeserver(观察者)模式、Publish/Subscribe(发布/订阅)模式
JS设计模式之Factory(工厂)模式
JS设计模式之Singleton(单例)模式
JS设计模式之Facade(外观)模式
JS设计模式之Module(模块)模式、Revealing Module(揭示模块)模式
JS设计模式之Mixin(混入)模式
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103895.html
摘要:使用替换目标在第三篇文章中,我们使用来抽离了注入依赖项的公共逻辑。成果通过作用域插槽,我们有效地避免了第三方组件由于混入而可能造成的命名冲突以及隐式依赖等问题。 04 使用 slot 替换 mixin 目标 在第三篇文章中,我们使用 mixin 来抽离了注入 toggle 依赖项的公共逻辑。在 react 中,类似的需求是通过 HOC 的方式来解决的,但是仔细想想的话,react 在早...
摘要:在中,我们是否也有一些手段或特性来提高组件的复用程度和灵活性呢答案当然是有的,那就是。成果通过实现,我们成功将注入的逻辑抽离了出来,这样每次需要共享组件的状态和方法时,混入该即可。 03 使用 mixin 来增强 Vue 组件 目标 之前一篇文章中,我们虽然将 toggle 组件划分为了 toggle-button、toggle-on 和 toggle-off 三个子组件,且一切运行良...
摘要:前言最近开发的页面以及功能大都以表格为主,接口获取来的数据大都是需要经过处理,比如时间戳需要转换,或者状态码的转义。首先,还是在文件中定义一个状态码对应对象,这里我们将其对应的内容设为段落。 前言 最近开发的页面以及功能大都以表格为主,接口获取来的 JSON 数据大都是需要经过处理,比如时间戳需要转换,或者状态码的转义。对于这样的问题,各大主流框架都提供了类似于过滤的方法,在 Vue ...
阅读 2283·2023-04-25 14:29
阅读 1427·2021-11-22 09:34
阅读 2684·2021-11-22 09:34
阅读 3371·2021-11-11 10:59
阅读 1761·2021-09-26 09:46
阅读 2185·2021-09-22 16:03
阅读 1900·2019-08-30 12:56
阅读 463·2019-08-30 11:12