Вложенная маршрутизация

Backbone.js предоставляет мощный механизм маршрутизации через объект Router, позволяющий управлять состоянием приложения в зависимости от URL. В сложных приложениях возникает необходимость организации вложенных маршрутов, когда один маршрут может иметь несколько подмаршрутов, каждый из которых управляет отдельным представлением или логикой.

Основы маршрутизации

Маршрутизатор Backbone создается с помощью расширения Backbone.Router:

var AppRouter = Backbone.Router.extend({
    routes: {
        "": "home",
        "posts": "showPosts",
        "posts/:id": "showPost"
    },

    home: function() {
        console.log("Главная страница");
    },

    showPosts: function() {
        console.log("Список постов");
    },

    showPost: function(id) {
        console.log("Просмотр поста с id:", id);
    }
});

Каждый маршрут связывает URL-шаблон с методом, который выполняет необходимую логику.

Концепция вложенных маршрутов

Вложенные маршруты позволяют разделить функциональность приложения на иерархические уровни. Например, можно иметь общий маршрут для раздела «Посты» и подмаршруты для отдельных действий: просмотр, редактирование, создание.

Пример структуры вложенных маршрутов:

#posts
  ├─ /posts         → список постов
  ├─ /posts/:id     → просмотр поста
  └─ /posts/:id/edit → редактирование поста

Организация вложенных маршрутов

  1. Главный маршрутизатор отвечает за общие разделы приложения.
  2. Вложенные маршрутизаторы создаются для управления специфической логикой разделов.
  3. Главный маршрутизатор делегирует обработку подмаршрутов вложенному маршрутизатору.
var PostsRouter = Backbone.Router.extend({
    routes: {
        "": "list",
        ":id": "view",
        ":id/edit": "edit"
    },

    list: function() {
        console.log("Список всех постов");
    },

    view: function(id) {
        console.log("Просмотр поста:", id);
    },

    edit: function(id) {
        console.log("Редактирование поста:", id);
    }
});

var AppRouter = Backbone.Router.extend({
    routes: {
        "posts*actions": "handlePosts"
    },

    handlePosts: function(actions) {
        if (!this.postsRouter) {
            this.postsRouter = new PostsRouter();
            Backbone.history.loadUrl("posts" + actions);
        }
    }
});

В данном примере:

  • Главный маршрутизатор перехватывает все URL, начинающиеся с posts.
  • Создается экземпляр вложенного маршрутизатора PostsRouter.
  • Метод Backbone.history.loadUrl позволяет запустить маршрутизацию внутри вложенного маршрутизатора.

Передача состояния между маршрутизаторами

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

  • Общие модели и коллекции (Backbone.Model и Backbone.Collection)
  • Событийные шины (Backbone.Events) для синхронизации действий

Пример событийной шины:

var vent = _.extend({}, Backbone.Events);

var PostsRouter = Backbone.Router.extend({
    routes: {
        "": "list",
        ":id": "view"
    },

    list: function() {
        vent.trigger("posts:list");
    },

    view: function(id) {
        vent.trigger("posts:view", id);
    }
});

vent.on("posts:list", function() {
    console.log("Отображение списка постов через шину событий");
});

vent.on("posts:view", function(id) {
    console.log("Отображение поста через шину событий, id:", id);
});

Такой подход позволяет:

  • Изолировать маршруты и их обработчики
  • Легко расширять функциональность
  • Поддерживать модульность кода

Управление состоянием при навигации

При вложенной маршрутизации важно корректно обрабатывать перенаправления и возврат к предыдущему состоянию. Для этого Backbone предоставляет метод:

Backbone.history.navigate(fragment, {trigger: true});
  • fragment — путь для навигации
  • trigger: true — немедленный вызов соответствующего маршрута

Пример использования при переходе между подмаршрутами:

vent.on("posts:view", function(id) {
    Backbone.history.navigate("posts/" + id, {trigger: true});
});

Поддержка динамических подмаршрутов

Вложенные маршруты часто требуют динамического построения URL. Backbone позволяет использовать шаблоны с параметрами (:id, *actions).

Пример сложного подмаршрута:

"posts/:postId/comments/:commentId/edit": "editComment"

Метод маршрутизатора получит оба параметра:

editComment: function(postId, commentId) {
    console.log("Редактирование комментария", commentId, "в посте", postId);
}

Взаимодействие с представлениями

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

var PostView = Backbone.View.extend({
    render: function(post) {
        this.$el.html("<h2>" + post.get("title") + "</h2>");
        return this;
    }
});

var postView = new PostView({el: "#content"});
vent.on("posts:view", function(id) {
    var post = postsCollection.get(id);
    postView.render(post);
});

Такой подход позволяет разделять маршрутизацию и визуализацию, делая код гибким и поддерживаемым.

Особенности реализации

  • Вложенные маршруты лучше держать в отдельных модулях, чтобы избежать разрастания главного маршрутизатора.
  • Необходимо контролировать инициализацию вложенных маршрутизаторов, чтобы избежать повторного создания экземпляров.
  • Использование событийной шины позволяет сохранять состояние приложения и синхронизировать действия между разными компонентами.

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