基于事件驱动架构的jQuery插件?

30

是否有任何事件驱动架构的jQuery插件?

步骤1:订阅

alt text
订阅者订阅位于中间的事件处理程序,并传递回调方法以及他们要监听的事件名称......

例如,这两个绿色的订阅者将会监听p0事件。而蓝色的订阅者将会监听p1事件。


步骤2:p0事件由另一个组件触发到事件处理程序

alt text

  1. p0事件被触发到事件处理程序
  2. 事件处理程序通知它的订阅者该事件的发生,调用他们在步骤1:订阅中指定的回调方法。

请注意,因为蓝色的订阅者没有监听p0事件,所以他不会得到通知。


步骤3:p1事件被组件触发到事件处理程序

alt text

p1事件被另一个组件触发

与之前类似,只是现在蓝色的订阅者通过它的回调方法接收到了该事件,而其他两个绿色的订阅者则没有接收到该事件。

图片来自leeand00的Flickr

我似乎找不到这样的插件,但我的猜想是它们在JavaScript/jquery中称之为其他名称。

此模式有一个名称吗?因为它不仅仅是基本的发布/订阅模式,我认为它必须有其他名称。


请告诉我,如果我的话不清楚的话。 - leeand00
7个回答

48

你可能不需要一个插件来完成这个任务。首先,DOM本身完全是事件驱动的。你可以使用事件委托在根节点上监听所有事件(这是jQuery live使用的一种技术)。为了处理那些可能与DOM无关的自定义事件,你可以使用一个普通的JavaScript对象来完成这项工作。我写了一篇博客文章,介绍如何使用MooTools创建一个中央事件分发器,只需一行代码。

var EventBus = new Class({Implements: Events});

在jQuery中做这个也同样容易。使用一个普通的JavaScript对象作为所有事件的中央代理。任何客户端对象都可以在此对象上发布和订阅事件。请参见这个相关的问题

var EventManager = {
    subscribe: function(event, fn) {
        $(this).bind(event, fn);
    },
    unsubscribe: function(event, fn) {
        $(this).unbind(event, fn);
    },
    publish: function(event) {
        $(this).trigger(event);
    }
};

// Your code can publish and subscribe to events as:
EventManager.subscribe("tabClicked", function() {
    // do something
});

EventManager.publish("tabClicked");

EventManager.unsubscribe("tabClicked");

或者如果你不介意暴露jQuery,那么只需使用一个空对象并直接在jQuery包装的对象上调用bindtrigger即可。

var EventManager = {};

$(EventManager).bind("tabClicked", function() {
    // do something
});

$(EventManager).trigger("tabClicked");

$(EventManager).unbind("tabClicked");

这些包装器的作用只是为了隐藏底层的jQuery库,以便在需要时可以替换实现。

基本上这就是发布/订阅观察者模式,一些很好的例子包括Cocoa的NSNotificationCenter类、Ray Ryan在GWT社区中流行的EventBus模式以及其他几种。


该解决方案 https://gist.github.com/786386 相似,但支持通过令牌取消订阅。该解决方案 https://dev59.com/IXA85IYBdhLWcg3wCfHm#2969692 更为先进,还支持取消订阅(通过传递函数的引用)。 - koppor
@koppor:你的第二个链接指向了这个答案。你肯定是想引用其他内容吧? - Aaronaught
我不记得我可能想要引用的其他URL。也许,我想指向https://github.com/cujojs/msgs或https://github.com/sissbruecker/js-event-bus? - koppor
@koppor - jQuery现在正式支持包装纯JavaScript对象。我认为它已经做了一段时间了,但我的答案有点老旧。我记得当我添加这个答案时,并非如此,虽然bind适用于纯JavaScript对象,但是unbind不适用。但现在它可以了,所以我更新了我的答案。 - Anurag

5

尽管不是jQuery插件,Twitter发布了一个名为Flight的JavaScript框架,允许您创建基于组件的体系结构,并通过事件进行通信。

Flight是来自Twitter的轻量级、基于组件的JavaScript框架。与其他基于MVC模式的JavaScript框架不同,Flight直接将行为映射到DOM节点。

Flight对请求路由或决定使用哪个模板库都无关紧要。Flight强制实行严格的职责分离。Flight中的组件不会直接相互交互。

它们将其操作作为事件广播,那些订阅这些事件的组件可以根据它们采取行动。要使用Flight,您需要ES5-shim和jQuery以及一个AMD加载器。

Flight - 来自Twitter的轻量级、基于组件的JavaScript框架


2

可以使用一个虚拟的jQuery节点作为调度器来轻松完成此操作:

var someModule = (function ($) {

    var dispatcher = $("<div>");

    function init () {
        _doSomething();
    }

    /**
        @private
    */
    function _doSomething () {
        dispatcher.triggerHandler("SOME_CUSTOM_EVENT", [{someEventProperty: 1337}]);
    }

    return {
        dispatcher: dispatcher,
        init: init
    }

}(jQuery));



var someOtherModule = (function ($) {

    function init () {
        someModule.dispatcher.bind("SOME_CUSTOM_EVENT", _handleSomeEvent)
    }

    /**
        @private
    */
    function _handleSomeEvent (e, extra) {
        console.log(extra.someEventProperty) //1337
    }

    return {
        init: init
    }

}(jQuery));

$(function () {
    someOtherModule.init();
    someModule.init();
})

+1我喜欢依赖已知的DOM技术而不改变DOM的想法。 - gion_13

2
这能作为轻量级的消息传递框架吗?
function MyConstructor() {
    this.MessageQueues = {};

    this.PostMessage = function (Subject) {
        var Queue = this.MessageQueues[Subject];
        if (Queue) return function() {
                                        var i = Queue.length - 1;
                                        do Queue[i]();
                                        while (i--);
                                    }
        }

    this.Listen = function (Subject, Listener) {
        var Queue = this.MessageQueues[Subject] || [];
        (this.MessageQueues[Subject] = Queue).push(Listener);
    }
}

那么你可以这样做:
var myInstance = new MyConstructor();
myInstance.Listen("some message", callback());
myInstance.Listen("some other message", anotherCallback());
myInstance.Listen("some message", yesAnotherCallback());

之后:

myInstance.PostMessage("some message");

将会调度队列。

2

太好了!谢谢老大!我已经很长时间在寻找这种JS功能了。 - leeand00
实际上,让我感到惊讶的是JQuery需要插件来完成这个功能。通常我使用Prototype,那里默认的方式就是这样做。 - vartec
等等,也许我接受这个答案有点草率了。我仔细看了一下,这两个库似乎都没有一个中央组件来触发事件并通知订阅者。 - leeand00
文档对象是“中央组件”。 - vartec

2
最近的一个进展是msgs.js,它是“基于消息的JavaScript编程。受Spring Integration启发”。它还支持通过WebSockets通信。
msgs.js将《企业集成模式》一书中定义的词汇和模式应用到JavaScript中,将面向消息的编程扩展到浏览器和/或服务器端JavaScript。最初开发的消息传递模式用于集成松散耦合的不同系统,同样适用于单个应用程序过程中松散耦合的模块。
测试环境: - Node.js (0.6, 0.8) - Chrome (stable) - Firefox (stable, ESR, 应该可以在早期版本中工作) - IE (6-10) - Safari (5、6、iOS 4-6,应该可以在早期版本中工作) - Opera (11、12,应该可以在早期版本中工作)

1

我使用了OpenAjax Hub的发布/订阅服务。它不是一个jQuery插件,而是一个独立的JavaScript模块。你可以从SourceForge下载和使用参考实现。我喜欢层次化的主题命名和使用通配符符号订阅多个主题的支持。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接