Javascript 为什么要使用发布/订阅模式(在 JS/jQuery 中)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13512949/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 12:15:40  来源:igfitidea点击:

Why would one use the Publish/Subscribe pattern (in JS/jQuery)?

javascriptjquerydesign-patternspublish-subscribe

提问by Maccath

So, a colleague introduced me to the publish/subscribe pattern (in JS/jQuery), but I'm having a hard time getting to grips with whyone would use this pattern over 'normal' JavaScript/jQuery.

所以,一位同事向我介绍了发布/订阅模式(在 JS/jQuery 中),但我很难理解为什么人们会在“普通”JavaScript/jQuery 上使用这种模式。

For example, previously I had the following code...

例如,以前我有以下代码...

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    var orders = $(this).parents('form:first').find('div.order');
    if (orders.length > 2) {
        orders.last().remove();
    }
});

And I could see the merit of doing this instead, for example...

我可以看到这样做的好处,例如......

removeOrder = function(orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    removeOrder($(this).parents('form:first').find('div.order'));
});

Because it introduces the ability to re-use the removeOrderfunctionality for different events etc.

因为它引入了removeOrder对不同事件等重用功能的能力。

But why would you decide to implement the publish/subscribe pattern and go to the following lengths, if it does the same thing? (FYI, I used jQuery tiny pub/sub)

但是,如果它做同样的事情,你为什么要决定实现发布/订阅模式并转到以下长度?(仅供参考,我使用了jQuery tiny pub/sub

removeOrder = function(e, orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

I've read about the pattern for sure, but I just can't imagine why this would ever be necessary. The tutorials I've seen that explain howto implement this pattern only cover just as basic examples as my own.

我肯定已经阅读过有关该模式的内容,但我无法想象为什么这会是必要的。我看过的解释如何实现这种模式的教程只涵盖了与我自己的示例一样的基本示例。

I imagine that the pub/sub's usefulness would make itself apparent in a more complex application, but I can't imagine one. I'm afraid that I am completely missing the point; but I'd like to know the point if there is one!

我想 pub/sub 的用处会在更复杂的应用程序中体现出来,但我无法想象。恐怕我完全没有抓住重点;但我想知道这一点,如果有的话!

Could you explain succinctlywhy and in what situations this pattern is advantageous? Is it worth using the pub/sub pattern for code snippets like my examples above?

您能否简要解释一下为什么以及在什么情况下这种模式是有利的?对于像我上面的例子那样的代码片段使用 pub/sub 模式是否值得?

回答by Minko Gechev

It's all about loose coupling and single responsibility, which goes hand to hand with MV* (MVC/MVP/MVVM) patterns in JavaScript which are very modern in the last few years.

这完全是关于松散耦合和单一职责,这与过去几年非常现代的 JavaScript 中的 MV*(MVC/MVP/MVVM)模式密切相关。

Loose couplingis an Object-oriented principle in which each component of the system knows its responsibility and doesn't care about the other components (or at least tries to not care about them as much as possible). Loose coupling is a good thing because you can easily reuse the different modules. You're not coupled with the interfaces of other modules. Using publish/subscribe you're only coupled with the publish/subscribe interface which is not a big deal – just two methods. So if you decide to reuse a module in a different project you can just copy and paste it and it'll probably work or at least you won't need much effort to make it work.

松耦合是一种面向对象的原则,其中系统的每个组件都知道自己的职责并且不关心其他组件(或者至少尽量不关心它们)。松耦合是一件好事,因为您可以轻松地重用不同的模块。您没有与其他模块的接口耦合。使用发布/订阅,您只需要与发布/订阅接口相结合,这没什么大不了的——只有两种方法。因此,如果您决定在不同的项目中重用一个模块,您只需复制并粘贴它,它可能会起作用,或者至少您不需要太多努力就可以使它起作用。

When talking about loose coupling we should mention the separation of concerns. If you're building an application using an MV* architectural pattern you always have a Model(s) and a View(s). The Model is the business part of the application. You can reuse it in different applications, so it's not a good idea to couple it with the View of a single application, where you want to show it, because usually in the different applications you have different views. So it's a good idea to use publish/subscribe for the Model-View communication. When your Model changes it publishes an event, the View catches it and updates itself. You don't have any overhead from the publish/subscribe, it helps you for the decoupling. In the same manner you can keep your application logic in the Controller for example (MVVM, MVP it's not exactly a Controller) and keep the View as simple as possible. When your View changes (or the user clicks on something, for example) it just publishes a new event, the Controller catches it and decides what to do. If you are familiar with the MVCpattern or with MVVMin Microsoft technologies (WPF/Silverlight) you can think of the publish/subscribe like the Observer pattern. This approach is used in frameworks like Backbone.js, Knockout.js (MVVM).

在谈论松散耦合时,我们应该提到关注点分离. 如果您正在使用 MV* 架构模式构建应用程序,您总是有一个模型和一个视图。模型是应用程序的业务部分。您可以在不同的应用程序中重用它,因此将它与单个应用程序的 View 结合起来不是一个好主意,您希望在其中显示它,因为通常在不同的应用程序中您有不同的视图。因此,使用发布/订阅进行模型-视图通信是一个好主意。当您的模型更改它发布一个事件时,视图会捕获它并更新自身。您没有发布/订阅的任何开销,它可以帮助您解耦。以同样的方式,您可以将应用程序逻辑保留在控制器中(例如 MVVM、MVP,它不完全是控制器)并尽可能保持视图简单。当你的 View 改变(或者用户点击某些东西,例如)它只是发布一个新事件,Controller 捕获它并决定做什么。如果你熟悉MVC模式或Microsoft 技术中的MVVM(WPF/Silverlight),您可以将发布/订阅视为观察者模式。这种方法用于 Backbone.js、Knockout.js (MVVM) 等框架中。

Here is an example:

下面是一个例子:

//Model
function Book(name, isbn) {
    this.name = name;
    this.isbn = isbn;
}

function BookCollection(books) {
    this.books = books;
}

BookCollection.prototype.addBook = function (book) {
    this.books.push(book);
    $.publish('book-added', book);
    return book;
}

BookCollection.prototype.removeBook = function (book) {
   var removed;
   if (typeof book === 'number') {
       removed = this.books.splice(book, 1);
   }
   for (var i = 0; i < this.books.length; i += 1) {
      if (this.books[i] === book) {
          removed = this.books.splice(i, 1);
      }
   }
   $.publish('book-removed', removed);
   return removed;
}

//View
var BookListView = (function () {

   function removeBook(book) {
      $('#' + book.isbn).remove();
   }

   function addBook(book) {
      $('#bookList').append('<div id="' + book.isbn + '">' + book.name + '</div>');
   }

   return {
      init: function () {
         $.subscribe('book-removed', removeBook);
         $.subscribe('book-aded', addBook);
      }
   }
}());

Another example. If you don't like the MV* approach you can use something a little different (there's an intersection between the one I'll describe next and the last mentioned). Just structure your application in different modules. For example look at Twitter.

另一个例子。如果您不喜欢 MV* 方法,您可以使用一些不同的方法(我接下来要描述的方法和最后提到的方法之间存在交集)。只需在不同的模块中构建您的应用程序。例如看看推特。

Twitter Modules

推特模块

If you look at the interface you simply have different boxes. You can think of each box as a different module. For example you can post a tweet. This action requires the update of a few modules. Firstly it has to update your profile data (upper left box) but it also has to update your timeline. Of course, you can keep references to both modules and update them separately using their public interface but it's easier (and better) to just publish an event. This will make the modification of your application easier because of looser coupling. If you develop new module which depends on new tweets you can just subscribe to the “publish-tweet” event and handle it. This approach is very useful and can make your application very decoupled. You can reuse your modules very easily.

如果您查看界面,您只会看到不同的框。您可以将每个框视为不同的模块。例如,您可以发布一条推文。此操作需要更新几个模块。首先它必须更新您的个人资料数据(左上框),但它也必须更新您的时间线。当然,您可以保留对两个模块的引用,并使用它们的公共接口分别更新它们,但发布事件更容易(也更好)。由于松散耦合,这将使您的应用程序的修改更容易。如果您开发依赖于新推文的新模块,您只需订阅“publish-tweet”事件并处理它。这种方法非常有用,可以使您的应用程序非常解耦。您可以非常轻松地重用您的模块。

Here is a basic example of the last approach (this is not original twitter code it's just a sample by me):

这是最后一种方法的基本示例(这不是原始 twitter 代码,它只是我的一个示例):

var Twitter.Timeline = (function () {
   var tweets = [];
   function publishTweet(tweet) {
      tweets.push(tweet);
      //publishing the tweet
   };
   return {
      init: function () {
         $.subscribe('tweet-posted', function (data) {
             publishTweet(data);
         });
      }
   };
}());


var Twitter.TweetPoster = (function () {
   return {
       init: function () {
           $('#postTweet').bind('click', function () {
               var tweet = $('#tweetInput').val();
               $.publish('tweet-posted', tweet);
           });
       }
   };
}());

For this approach there's an excellent talk by Nicholas Zakas. For the MV* approach the best articles and books I know of are published by Addy Osmani.

对于这种方法,Nicholas Zakas 进行了精彩的演讲。对于 MV* 方法,我所知道的最好的文章和书籍由Addy Osmani出版。

Drawbacks: You have to be careful about the excessive use of publish/subscribe. If you've got hundreds of events it can become very confusing to manage all of them. You may also have collisions if you're not using namespacing (or not using it in the right way). An advanced implementation of Mediator which looks much like an publish/subscribe can be found here https://github.com/aHymansified/Mediator.js. It has namespacing and features like event “bubbling” which, of course, can be interrupted. Another drawback of publish/subscribe is the hard unit testing, it may become difficult to isolate the different functions in the modules and test them independently.

缺点:您必须小心过度使用发布/订阅。如果您有数百个事件,管理所有事件可能会变得非常混乱。如果您没有使用命名空间(或没有以正确的方式使用它),您也可能会发生冲突。可以在https://github.com/aHymansified/Mediator.js 中找到看起来很像发布/订阅的 Mediator 的高级实现。它具有命名空间和事件“冒泡”等功能,当然可以被中断。发布/订阅的另一个缺点是很难进行单元测试,可能很难隔离模块中的不同功能并独立测试它们。

回答by Anders Arpi

The main goal is to reduce coupling between the code. It's a somewhat event-based way of thinking, but the "events" aren't tied to a specific object.

主要目标是减少代码之间的耦合。这是一种有点基于事件的思维方式,但“事件”并不与特定对象相关联。

I'll write out a big example below in some pseudo code that looks a bit like JavaScript.

我将在下面用一些看起来有点像 JavaScript 的伪代码写出一个大例子。

Let's say we have a class Radio and a class Relay:

假设我们有一个 Radio 类和一个 Relay 类:

class Relay {
    function RelaySignal(signal) {
        //do something we don't care about right now
    }
}

class Radio {
    function ReceiveSignal(signal) {
        //how do I send this signal to other relays?
    }
}

Whenever radio receives a signal, we want a number of relays to relay the message in some way. The number and types of relays can differ. We could do it like this:

每当无线电接收到信号时,我们都希望有多个中继以某种方式中继消息。继电器的数量和类型可以不同。我们可以这样做:

class Radio {
    var relayList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function ReceiveSignal(signal) {
        for(relay in relayList) {
            relay.Relay(signal);
        }
    }

}

This works fine. But now imagine we want a different component to also take part of the signals that the Radio class receives, namely Speakers:

这工作正常。但是现在假设我们想要一个不同的组件也接收 Radio 类接收到的信号的一部分,即 Speakers:

(sorry if the analogies aren't top notch...)

(对不起,如果类比不是一流的......)

class Speakers {
    function PlaySignal(signal) {
        //do something with the signal to create sounds
    }
}

We could repeat the pattern again:

我们可以再次重复这个模式:

class Radio {
    var relayList = [];
    var speakerList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function AddSpeaker(speaker) {
        speakerList.add(speaker)
    }

    function ReceiveSignal(signal) {

        for(relay in relayList) {
            relay.Relay(signal);
        }

        for(speaker in speakerList) {
            speaker.PlaySignal(signal);
        }

    }

}

We could make this even better by creating an interface, like "SignalListener", so that we only need one list in the Radio class, and always can call the same function on whatever object we have that wants to listen to the signal. But that still creates a coupling between whatever interface/base class/etc we decide on and the Radio class. Basically whenever you change one of the Radio, Signal or Relay class you have to think about how it could possibly affect the other two classes.

我们可以通过创建一个像“SignalListener”这样的接口来使这变得更好,这样我们只需要 Radio 类中的一个列表,并且总是可以在我们拥有的任何想要收听信号的对象上调用相同的函数。但这仍然会在我们决定的任何接口/基类/等与 Radio 类之间产生耦合。基本上,每当您更改 Radio、Signal 或 Relay 类之一时,您都必须考虑它可能如何影响其他两个类。

Now let's try something different. Let's create a fourth class named RadioMast:

现在让我们尝试一些不同的东西。让我们创建一个名为 RadioMast 的第四个类:

class RadioMast {

    var receivers = [];

    //this is the "subscribe"
    function RegisterReceivers(signaltype, receiverMethod) {
        //if no list for this type of signal exits, create it
        if(receivers[signaltype] == null) {
            receivers[signaltype] = [];
        }
        //add a subscriber to this signal type
        receivers[signaltype].add(receiverMethod);
    }

    //this is the "publish"
    function Broadcast(signaltype, signal) {
        //loop through all receivers for this type of signal
        //and call them with the signal
        for(receiverMethod in receivers[signaltype]) {
            receiverMethod(signal);
        }
    }
}

Now we have a patternthat we are aware of and we can use it for any number and types of classes as long as they:

现在我们有了一个我们知道的模式,我们可以将它用于任意数量和类型的类,只要它们:

  • are aware of the RadioMast (the class handling all the message passing)
  • are aware of the method signature for sending/receiving messages
  • 知道 RadioMast(处理所有消息传递的类)
  • 知道发送/接收消息的方法签名

So we change the Radio class to its final, simple form:

因此,我们将 Radio 类更改为其最终的简单形式:

class Radio {
    function ReceiveSignal(signal) {
        RadioMast.Broadcast("specialradiosignal", signal);
    }
}

And we add the speakers and the relay to the RadioMast's receiver list for this type of signal:

我们将扬声器和中继器添加到 RadioMast 的接收器列表中以接收此类信号:

RadioMast.RegisterReceivers("specialradiosignal", speakers.PlaySignal);
RadioMast.RegisterReceivers("specialradiosignal", relay.RelaySignal);

Now the Speakers and Relay class has zero knowledge of anything except that they have a method that can receive a signal, and the Radio class, being the publisher, is aware of the RadioMast that it publishes signals to. This is the point of using a message-passing system like publish/subscribe.

现在 Speakers 和 Relay 类对任何东西都零知识,除了它们有一个可以接收信号的方法,而 Radio 类作为发布者,知道它向其发布信号的 RadioMast。这是使用诸如发布/订阅之类的消息传递系统的要点。

回答by Trevedhek

The other answers have done a great job in showing how the pattern works. I wanted to address the implied question "what is wrong with the old way?" as I've been working with this pattern recently, and I find it involves a shift in my thinking.

其他答案在展示该模式的工作原理方面做得很好。我想解决隐含的问题“旧方式有什么问题?”因为我最近一直在使用这种模式,我发现它涉及到我思想的转变。

Imagine we have subscribed to an economic bulletin. The bulletin publishes a headline: "Lower the Dow Jones by 200 points". That would be an odd and somewhat irresponsible message to send. If however, it published: "Enron filed for chapter 11 bankrupcy protection this morning", then this is a more useful message. Note that the message may causethe Dow Jones to fall 200 points, but that is another matter.

想象一下,我们订阅了一份经济公报。该公告发布了一个标题:“将道琼斯指数下调 200 点”。这将是一个奇怪且有点不负责任的信息。然而,如果它发表了:“安然今天早上申请了第 11 章破产保护”,那么这是一个更有用的信息。请注意,该消息可能会导致道琼斯指数下跌 200 点,但那是另一回事。

There is a difference between sending a command, and advising of something that has just happened. With this in mind, take your original version of the pub/sub pattern, ignoring the handler for now:

发送命令和通知刚刚发生的事情是有区别的。考虑到这一点,采用原始版本的 pub/sub 模式,暂时忽略处理程序:

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

There is already an implied strong coupling here, between the user-action (a click) and the system-response (an order being removed). Effeectively in your example, the action is giving a command. Consider this version:

这里已经存在隐含的强耦合,在用户操作(点击)和系统响应(订单被删除)之间。有效地在您的示例中,该操作是发出命令。考虑这个版本:

$.subscribe('iquery/action/remove-order-requested', handleRemoveOrderRequest);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order-requested', $(this).parents('form:first').find('div.order'));
});

Now the handler is responding to something of interest that has happened, but is under no obligation to remove an order. In fact, the handler can do all sorts of things not directly related to removing an order, but still maybe relevant to the calling action. For example:

现在处理程序正在对发生的感兴趣的事情做出响应,但没有义务删除订单。事实上,处理程序可以做各种与删除订单没有直接关系的事情,但仍然可能与调用操作相关。例如:

handleRemoveOrderRequest = function(e, orders) {
    logAction(e, "remove order requested");
    if( !isUserLoggedIn()) {
        adviseUser("You need to be logged in to remove orders");
    } else if (isOkToRemoveOrders(orders)) {
        orders.last().remove();
        adviseUser("Your last order has been removed");
        logAction(e, "order removed OK");
    } else {
        adviseUser("Your order was not removed");
        logAction(e, "order not removed");
    }
    remindUserToFloss();
    increaseProgrammerBrowniePoints();
    //etc...
}

The distinction between a command and a notification is a useful distinction to make with this pattern, IMO.

命令和通知之间的区别是使用这种模式做出的有用区分,IMO。

回答by Esailija

So that you don't have to hardcode method / function calls, you just publish the event without caring who listens. This makes the publisher independent from subscriber, reducing dependency (or coupling, whatever term you prefer) between 2 different parts of the application.

这样您就不必对方法/函数调用进行硬编码,您只需发布事件而无需关心谁在听。这使得发布者独立于订阅者,减少了应用程序的 2 个不同部分之间的依赖性(或耦合,无论您喜欢什么术语)。

Here are some disadvantages of coupling as mentioned by wikipedia

以下是维基百科提到的耦合的一些缺点

Tightly coupled systems tend to exhibit the following developmental characteristics, which are often seen as disadvantages:

  1. A change in one module usually forces a ripple effect of changes in other modules.
  2. Assembly of modules might require more effort and/or time due to the increased inter-module dependency.
  3. A particular module might be harder to reuse and/or test because dependent modules must be included.

紧耦合系统往往表现出以下发展特征,这些特征通常被视为缺点:

  1. 一个模块的变化通常会导致其他模块变化的连锁反应。
  2. 由于模块间依赖性的增加,模块的组装可能需要更多的努力和/或时间。
  3. 特定模块可能更难重用和/或测试,因为必须包含相关模块。

Consider something like an object encapsulating business data. It has hard coded method call to update the page whenever the age is set:

考虑诸如封装业务数据的对象之类的东西。它具有硬编码的方法调用以在设置年龄时更新页面:

var person = {
    name: "John",
    age: 23,

    setAge: function( age ) {
        this.age = age;
        showAge( age );
    }
};

//Different module

function showAge( age ) {
    $("#age").text( age );
}

Now I cannot test the person object without also including the showAgefunction. Also, if I need to show the age in some other GUI module as well, I need to hardcode that method call in .setAge, and now there is dependencies for 2 unrelated modules in the person object. It's also just hard to maintain when you see those calls being made and they are not even in the same file.

现在我不能在不包含showAge函数的情况下测试 person 对象。此外,如果我还需要在其他一些 GUI 模块中显示年龄,我需要对该方法调用进行硬编码, .setAge现在 person 对象中存在 2 个不相关模块的依赖关系。当您看到这些调用发生并且它们甚至不在同一个文件中时,也很难维护。

Note that inside the same module, you can of course have direct method calls. But business data and superficial gui behavior should not reside in the same module by any reasonable standards.

请注意,在同一个模块中,您当然可以有直接的方法调用。但是,按照任何合理的标准,业务数据和表面的 gui 行为不应该驻留在同一个模块中。

回答by user2756335

PubSub implementation is commonly seen in where there is -

PubSub 实现常见于有 -

  1. There is a portlet like implementation where there are multiple portlets that communicate with the help of an event bus. This helps in creating in aync architecture.
  2. In a system marred by tight coupling, pubsub is a mechanism which helps in communicating between various modules.
  1. 有一个类似于 portlet 的实现,其中有多个 portlet 在事件总线的帮助下进行通信。这有助于在 aync 架构中创建。
  2. 在一个被紧密耦合破坏的系统中,pubsub 是一种有助于在不同模块之间进行通信的机制。

Example code -

示例代码 -

var pubSub = {};
(function(q) {

  var messages = [];

  q.subscribe = function(message, fn) {
    if (!messages[message]) {
      messages[message] = [];
    }
    messages[message].push(fn);
  }

  q.publish = function(message) {
    /* fetch all the subscribers and execute*/
    if (!messages[message]) {
      return false;
    } else {
      for (var message in messages) {
        for (var idx = 0; idx < messages[message].length; idx++) {
          if (messages[message][idx])
            messages[message][idx]();
        }
      }
    }
  }
})(pubSub);

pubSub.subscribe("event-A", function() {
  console.log('this is A');
});

pubSub.subscribe("event-A", function() {
  console.log('booyeah A');
});

pubSub.publish("event-A"); //executes the methods.

回答by Peheje

The paper "The Many Faces of Publish/Subscribe"is a good read and one thing that they emphasize is the decoubling in three "dimensions". Here is my crude summary but please see the paper instead.

论文“发布/订阅的多面性”是一本很好的读物,他们强调的一件事是三个“维度”的解耦。这是我的粗略总结,但请参阅论文。

  1. Space decoubling.The interacting parties do not need to know eachother. The publisher do not know who is listening, how many or what they are doing with the event. The subscribers do not know who is producing these events or how many producers there are etc.
  2. Time decoubling.The interacting parties do not need to be active at the same time during the interaction. E.g. a subscriber might be disconnected while a publisher is publishing some events, it can react to it when it becomes online.
  3. Synchronization decoupling.Publishers are not blocked while producing events and subscribers can be asynchronously notified through callbacks whenever an event they have subcribed to arrives.
  1. 空间解耦。交互方不需要彼此认识。发布者不知道谁在听,有多少人在听,或者他们正在对事件做什么。订阅者不知道谁在生产这些事件或有多少生产者等。
  2. 时间解耦。在交互过程中,交互方不需要同时处于活动状态。例如,当发布者正在发布一些事件时,订阅者可能会断开连接,当它在线时,它可以对其做出反应。
  3. 同步解耦。发布者在生成事件时不会被阻塞,并且订阅者可以在订阅者订阅的事件到达时通过回调异步通知他们。

回答by Simon Miller

Simple answerThe original question was looking for a simple answer. Here's my attempt.

简单的答案最初的问题是寻找一个简单的答案。这是我的尝试。

Javascript doesn't provide any mechanism for code objects to create their own events. So you need a kind of event mechanism. the Publish / subscribe pattern will answer this need, and it's up to you to choose a mechanism that best suits your own needs.

Javascript 没有为代码对象提供任何机制来创建它们自己的事件。所以你需要一种事件机制。发布/订阅模式将满足这一需求,您可以选择最适合您自己需求的机制。

Now we can see a need for the pub/sub pattern, then would you rather have to handle DOM events differently from how you handle your pub/sub events? For the sake of reducing complexity, and other concepts such as separation of concerns (SoC) you might see the benefit of everything being uniform.

现在我们可以看到对 pub/sub 模式的需求,那么您是否愿意以不同于处理 pub/sub 事件的方式处理 DOM 事件?为了降低复杂性和其他概念,例如关注点分离 (SoC),您可能会看到一切统一的好处。

So paradoxically, more code creates a better separation of concerns, which scales well up to very complex web pages.

因此矛盾的是,更多的代码创建了更好的关注点分离,这可以很好地扩展到非常复杂的网页。

I hope someone finds this a good enough discussion without going into detail.

我希望有人认为这是一个足够好的讨论,而无需详细说明。