Статьи

Элегантно используя Socket.io в магистральных приложениях

Backbone.js — мой любимый современный MVC-фреймворк для клиентского JavaScript. Не то чтобы я серьезно пробовал другие … просто не чувствовал необходимости.

Основным способом организации приложений является то, что Backbone — это данные. Представление реагирует на некоторые действия пользователя и изменяет некоторые данные в модели. Каждый, кто выслушивает это изменение, реагирует и делает что-то либо для своего представления (например, добавляя что-то новое на экран), либо данные изменяются. Иногда изменение данных приводит к изменению URL браузера .

Как бы то ни было, важной частью является то, что события обычно передаются через изменения состояния.

События

Несмотря на богоподобный статус моделей, большая часть вашего кода попадает в представления. Вот где вы должны реагировать на два типа событий:

  • изменения данных
  • действия пользователя

Прослушивание изменений данных проще всего выполнить в функции initialize , что-то вроде этого:

   initialize: function () {
        this.model.on("change", this.render, this);
    }

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

   var that = this;
        this.$el.find('a.button', function () {
            that.button_clicked();
        });

Это может стать громоздким очень быстро … Backbone покрыл нас хэшем событий:

    events: {
        "click a.button": "button_clicked"
    },

Оба этих подхода приводят к  тому, что this.button_clicked вызывается  , когда пользователь нажимает кнопку. За исключением того, что второй подход намного проще для глаз и значительно менее запутан, когда у вас есть десять различных событий, делающих вещи.

Но что происходит, когда вы добавляете socket.io в микс?

Добавление socket.io

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

Я обычно держу сокет в глобальном  объекте приложения  .

Но это грязно

 window.app.socket.on("message", function(message) {
            that.got_message(message);
        });

Или хуже, обрабатывая все о сообщении в обратном вызове!

Вместо этого я начал заставлять все мои представления расширять общий  MainView, который позволяет мне прослушивать события сокета так же, как и действия пользователя:

var MyView = MainView.extend({
 
    socket_events: {
        "message": "got_message"
    }

Затем MainView заботится о связывании событий сокетов с их обратными вызовами так же, как это делает нативный Backbone.

var MainView = Backbone.View.extend({
 
    initialize: function () {
        this.__initialize();
    },
 
    __initialize: function () {
        if (this.socket_events && _.size(this.socket_events) > 0) {
            this.delegateSocketEvents(this.socket_events);
        }
    },
 
    delegateSocketEvents: function (events) {
        for (var key in events) {
            var method = events[key];
            if (!_.isFunction(method)) {
                method = this[events[key]];
            }
 
            if (!method) {
                throw new Error('Method "' + events[key] + '" does not exist');
            }
 
            method = _.bind(method, this);
            window.app.socket.on(key, method);
        };
    }
});

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

Такое дочернее представление просто вызвало бы  это .__ initialize (),  чтобы выполнить функцию инициализации MainView и заставить все работать.

Это далеко не идеально, и чтобы все это работало так, как мне хотелось бы — так же легко, как и действия пользователя — мне нужно было бы сделать запрос на извлечение Backbone… но это хорошее начало для элегантного использования socket.io в backbone. Программы.