Введение

В 2020 году разговоры о «простом JavaScript» могут показаться странными. У нас есть TypeScript, и у нас есть много библиотек и отличных фреймворков, таких как Angular, React и так далее.

Но правда в том, что время от времени, как профессиональный инженер-программист, вы обнаружите, что работаете над устаревшим веб-приложением, которое не использует никакой фреймворк на стороне интерфейса. Единственная дружелюбно выглядящая вещь, которую вы найдете (иногда), — это jQuery. Вот и все.

Тем не менее, вам все равно придется реализовать хорошее решение, надеюсь, структурировав интерфейс в компонентах, отделив интерфейс от сервера, написав модули JS, чтобы они функционировали как своего рода модели представления для ваших компонентов пользовательского интерфейса. И при этом вам нужно будет использовать какой-то «протокол связи», чтобы эти компоненты хорошо работали вместе и общались друг с другом.

Недавно я оказался в такой ситуации, и одним из первых действий, которые я сделал, было внедрение модуля JavaScript Messaging Service для обработки связи между компонентами, реализующими Шаблон обмена сообщениями, в частности, публикация /subscribe шаблон обмена сообщениями.

В этой статье я покажу вам код (очень простой) и объясню, как он работает, и какие варианты я сделал.

Репозиторий GitHub

Если вам нужно готовое решение и вы предпочитаете проверять код самостоятельно, вот репозиторий GitHub с кодом: не стесняйтесь использовать его по своему усмотрению.

Исходный код модуля находится в файле ./services/messaging-service.js.

Модуль службы обмена сообщениями

Для создания базовой службы обмена сообщениями нам нужны как минимум три основные функции:

  • Подписаться
  • Опубликовать
  • [Необязательно] Отменить подписку

Функция подписки

Это функция, которую компоненты будут вызывать для регистрации, чтобы получать уведомления всякий раз, когда будет опубликована определенная тема.
Чтобы зарегистрироваться, компоненты должны предоставить информацию о:

  • Кто они
  • тема, которая им интересна
  • Что они собираются делать, когда что-то будет опубликовано по теме

Функция Подписаться может выглядеть примерно так:

var subscriptions = [];

function subscribe (target, topic, callback) {
    if (hasSubscription(target))
        return;

    subscriptions.push({
        target,
        topic,
        callback
    });
}

function hasSubscription (target) {
    var found = false;

    for (var i = 0; i < subscriptions.length; i++) {
        var subscription = subscriptions[i];

        if (subscription.target === target && subscription.topic === topic) {
            found = true;
            break;
        }
    }

    return found;
}

При вызове этой функции мы создаем объект (назовем его subscription object) и сохраняем его внутри переменной массива subscriptions для последующего использования (строка 7).

Назначение параметра target — определить, кто подписывается на тему.
Параметр topic — это тема, которую мы подписка на.
Параметр обратный вызов должен быть функцией делегата, которая будет вызываться, когда что-то будет опубликовано по этой теме.

Наконец, перед созданием объекта подписки мы проверяем и убеждаемся, что у нас еще нет подписки для той же пары цель/тема (строка 4).
Эта проверка позволит избежать дублирования подписок во время выполнения (и, следовательно, дублирования вызовов обратного вызова), хотя это должно быть обеспечено путем правильного написания кода приложения (поэтому вы можете отключить эту проверку, если вы ХОТИТЕ, чтобы ваше приложение вело себя плохо в случае ошибки программирования в этом аспекте).

Функция публикации

Это функция, которую компоненты будут вызывать для публикации чего-либо по определенной теме.
Чтобы использовать эту функцию, компоненты должны предоставить информацию о:

  • тема, о которой они публикуют
  • Что они на самом деле публикуют (обычно информацию/данные)

Функция Опубликовать может выглядеть примерно так:

function publish (topic, payload) {
    if (!payload)
        payload = null;

    for (var i = 0; i < subscriptions.length; i++) {
        var subscription = subscriptions[i];

        if (subscription.topic === topic)
            subscription.callback(payload);
    }
}

Здесь происходит довольно просто: когда вызывается функция публикации, служба обмена сообщениями внутренне проверяет наличие любых подписок (строка 5) на запрошенную тему (строка 8) и для каждой соответствующей подписки вызывает соответствующий обратный вызов, передавая его как все, что содержится во входном параметре payload.

Таким образом, каждый компонент, подписанный на эту тему, будет пассивно уведомлен и будет что-то делать (= то, что находится внутри функции обратного вызова), даже не зная, кто опубликовал обновление в теме: на самом деле они ничего не знают. им все равно, и им не нужно знать, кто опубликовал обновление (= разъединение).

Функция отписки

Это можно рассматривать как бонусную функцию. Его цель, как вы можете подумать, состоит в том, чтобы позволить компоненту отписаться от темы, чтобы перестать получать обновления темы.

Код симметричен функции subscribe и выглядит следующим образом:

function unsubscribe (target, topic) {
    for (var i = subscriptions.length - 1; i >= 0; i--) {
        var subscription = subscriptions[i];

        if (subscription.target === target && subscription.topic === topic)
            subscriptions.splice(i, 1);
    }
}

Здесь мы просто удаляем любой объект подписки, соответствующий предоставленным параметрам target и topic.

Образец кода

Я подготовил репозиторий GitHub с кодом службы обмена сообщениями вместе с некоторым примером кода, который демонстрирует возможный вариант использования.

Вы можете найти исходный код модуля в файле ./services/messaging-service.js.

Используя npm (после выполнения npm install в корневом каталоге проекта), вы можете запустить пример кода, выполнив следующую команду:

http-server -p [PORT]

PORT должен быть любым портом, на котором вы хотите запустить локальный http-сервер.

Затем, загрузив страницу http://localhost:[PORT]/index.html, вы увидите образец страницы:

Теперь, как следует из подсказки, если вы нажмете кнопку «Изменить имя», имя Джек Джонс изменится (я оставлю вам сюрприз от результата).

Я знаю, это очень простой пример, но его цель — просто продемонстрировать, как на практике использовать службу обмена сообщениями: вам решать, как использовать весь ее потенциал в реальном веб-приложении.

Заключение

Обмен сообщениями — это простой, но эффективный способ обеспечения взаимодействия между компонентами, поддерживающий разделение/развязку и позволяющий создавать более чистый код.

Если вам нужен полный код, не забудьте зайти в репозиторий GitHub, где вы можете найти полный исходный файл.

Я надеюсь, что это может быть полезно для вас!