YUI3组件框架之plugin

plugin相关源码分析:

plugin功能包括如下几个模块, 简单分析如下:

pluginhost-base

维护对象 this._plugins = {};

并提供方法: plug、unplug、hasplug、_destroyPlugins、_initPlugins

plug: 初始化插件实例,并与host进行关联   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (Plugin && !L.isFunction(Plugin)) {
    config = Plugin.cfg;
    Plugin = Plugin.fn;
}
 
// Plugin should be fn by now
if (Plugin && Plugin.NS) {
    ns = Plugin.NS;
 
    config = config || {};
    config.host = this;
 
    if (this.hasPlugin(ns)) {
        // Update config
        if (this[ns].setAttrs) {
            this[ns].setAttrs(config);
        }
    } else {
        // Create new instance
        this[ns] = new Plugin(config);
        this._plugins[ns] = Plugin;
    }
}

_initPlugins: 调用模块 pluginhost-config 中的_initConfigPlugins(config);

  • 根据this._classes(原型链上的constructor对象)上的静态属性 _PLUG and _UNPLUG, 来初始化plugin
  • 通过widget初始化时的config配置, 来初始化plugin
  • 在YUI Widget体系中base-core模块用来初始化插件, 初始化所有ext和ATTRS后调用  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
PluginHost.prototype.\_initConfigPlugins = function(config) {
 
    // Class Configuration
    var classes = (this._getClasses) ? this._getClasses() : [this.constructor],
        plug = [],
        unplug = {},
        constructor, i, classPlug, classUnplug, pluginClassName;
    // TODO: Room for optimization. Can we apply statically/unplug in same pass?
    for (i = classes.length - 1; i >= 0; i--) {
        constructor = classes[i];
 
        classUnplug = constructor._UNPLUG;
        if (classUnplug) {
            // subclasses over-write
            Y.mix(unplug, classUnplug, true);
        }
 
        classPlug = constructor._PLUG;
        if (classPlug) {
            // subclasses over-write
            Y.mix(plug, classPlug, true);
        }
    }
 
    for (pluginClassName in plug) {
        if (plug.hasOwnProperty(pluginClassName)) {
            if (!unplug[pluginClassName]) {
                this.plug(plug[pluginClassName]);
            }
        }
    }
 
    // User Configuration
    if (config && config.plugins) {
        this.plug(config.plugins);
    }
};  

pluginhost-config

  • _initConfigPlugins
  • 静态方法: plug、unplug
  • 提供给Y.Base引用

base-pluginhost

1
2
3
4
5
mixin Base and PluginHost, 即:
 
Y.mix(Base, PluginHost, false, null, 1);
Base.plug = PluginHost.plug;
Base.unplug = PluginHost.unplug;  

plugin

继承Y.Base . 提供AOP的一系列方法, 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
doBefore: function(strMethod, fn, context) {
    var host = this.get("host"), handle;
    if (strMethod in host) { // method
        handle = this.beforeHostMethod(strMethod, fn, context);
    } else if (host.on) { // event
        handle = this.onHostEvent(strMethod, fn, context);
    }
    return handle;
},
doAfter: function() {
    ...
},
onHostEvent: function() {
    var handle = this.get("host").on(type, fn, context || this);
    this._handles.push(handle);
    return handle;
},
afterHostEvent: function() {
    ...
},
beforeHostMethod: function() {
    ...
},
afterHostMethod: function() {
    ...
} 

如何写一个插件

1、 任何简单对象即可成为一个简单插件, 如:

1
2
3
4
5
6
7
function NodeDrag(config) {
    var host = config.host;
    drag(host);
}
function drag(host) {
    ...
}

2、YUI的Plugin基类提供了基于事件的AOP机制支持,可以通过继承它在不影响原有代码逻辑前提下,通过对代码执行过程的控制,达到改变原有代码逻辑或者增加插件功能的效果, 如:

1
2
3
4
5
6
7
8
function NodeDrag() {
    NodeDrag.superclass.constructor.apply(this, arguments);
}
Y.extend(NodeDrag, Y.Plugin.Base, {
    drag: function() {
        ...
    }
});

使用插件的几种方式

给一个node节点添加一个插件

1、使用Base的静态方法, 实际调用的是pluginhost-config里面的plug方法

1
Y.Base.plug(Y.one('#foo'), NodeDrag, config);

2、node对象和继承了Y.Base对象的实例都可以通过实例直接调用plug方法使用插件

1
Y.one('#foo').plug(NodeDrag, config);

3、具体widget

1
2
3
4
5
6
7
8
9
10
function Widget() {}
Y.extend(Widget, Y.Base, {
    initializer: function() {
        ...
    }
});
var w = new Widget({plugins: NodeDrag});
//var w = new Widget({plugins: [NodeDrag, ..]});
//var w = new Widget({plugins: {fn: NodeDrag, cfg: {} });
//w.plug(NodeDrag, cfg);

  

在一些情况,为了让调用者不必过多的关心实现细节的时候,也将插件的初始化放到具体widget的实现中, 如:

1
2
3
4
5
6
7
8
9
10
11
12
Widget.ATTRS = {
    drag: {
        value: false
    }
};
Y.extend(Widget, Y.Base, {
    initializer: function() {
        if(this.get('drag')) {
            this.plug(NodeDrag, cfg);
        }
    }
});

插件的一些优势和适合的场景

  • 插件一般是基于host开发的扩展功能, 用以对host功能的增强,如一些动画效果、也类如dota游戏中一些英雄所具有的各种技能等
  • 插件机制能对复杂功能进行更好的抽象, 减少代码逻辑的耦合
    • 如,在代码中由于加入一个功能,会涉及到很多代码片段加入if else 判断逻辑, 那么可以考虑将这个功能作为一个plugin增强, 使用AOP的方式与原来代码逻辑关联起来
  • 通过host进行统一的入口管理

 

posted @   mininice  阅读(354)  评论(0编辑  收藏  举报
编辑推荐:
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
阅读排行:
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· 一个基于 .NET 开源免费的异地组网和内网穿透工具
· 《HelloGitHub》第 108 期
· Windows桌面应用自动更新解决方案SharpUpdater5发布
· 我的家庭实验室服务器集群硬件清单
点击右上角即可分享
微信分享提示