Когда мы создавали компоненты приложения StaffJoy, мы поняли, что у нас была странная производственная ошибка, из-за которой представления Backbone.js не удалялись должным образом, в результате чего в приложении по-прежнему прослушивались мошеннические события.

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

Исследовательская работа

Когда мы рассмотрели это, оказалось, что это довольно распространенная проблема для приложений Backbone.js. Backbone сам по себе не справляется с этой проблемой, а это означает, что не существует стандартной практики очистки представлений. Некоторые из найденных нами решений проблемы включали:

  • Пользовательский сборщик мусора, который запускается между визуализацией представления.
  • Функция для управления переходом вида — например. Решение Дерика Бейли 2011 года

Решение Дерика оказалось самым простым и легким решением. Когда мы реализовали его, мы столкнулись с парой проблем, требующих модификации:

  • Он удаляет родительский элемент контейнера.
  • Он не отменяет функцию делегатаEvents представлений.
  • Он не дает возможности передавать параметры функциям рендеринга.

Наше решение

Мы модифицировали решение Дерика в нашем маршрутизаторе следующим образом.

Backbone.View.prototype.close = function() {  
    $(this.el).empty();
    this.unbind();
    this.undelegateEvents();
};

appView = function AppView(){

    this.showView = function(view, opts) {

        if (this.currentView){
            this.currentView.close();
        }
    this.currentView = view;
    this.currentView.render(opts);
    };

};
router = new App.Router({appView: App.appView});

Мы используем прототипирование Javascript, чтобы расширить функцию закрытия для каждого объекта представления. Используя пустой вызов JQuery вместо исходного удаления, он сохраняет родительский элемент при удалении всего html внутри него.

Исходное решение Дерика вызывает unbind, но это не позволяет отменить привязку декларативных событий, настроенных в представлении. Мы полагаем, что эти декларативные события были добавлены в Backbone после первоначальной публикации Дерика в блоге.

Наконец, мы разрешаем передавать прототип параметров в функцию showView, поскольку функция рендеринга часто запускается после выборки данных из нашего API, поэтому данные необходимо включать во время рендеринга.

Чтобы использовать AppView, его нужно инициализировать в роутере:

var Router = Backbone.Router.extend({  
    initialize: function(options) {
        this.appView = options.appView;
    },
});

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

home: function() {  
    var self = this,
          opts = {“hello”: “world”},
          homeView = new Views.HomeView()
    ;
    self.appView.showView(homeView, opts);
}

Наконец, мы пишем обёртку для рендеринга представления.

Движение вперед

Мы думаем, что это довольно простое решение этой проблемы; если у вас есть какие-либо предложения, улучшения или лучшие практики — сообщите нам об этом! Зарегистрируйтесь, чтобы протестировать приложение StaffJoy StaffJoy.com.