Большая организация веб-приложений backbone.js

В настоящее время я работаю над большим веб-приложением, основанным на backbone.js, и у вас много проблем с организацией, "зомби" и т.д., поэтому я решил сделать главный рефакторинг кода. Я уже написал кучу вспомогательных функций для работы с "зомби"; однако я хотел бы начать с самого начала и создать хорошую структуру/организацию для кода. Я не нашел много замечательных учебников/примеров для крупномасштабной организации backbone.js, поэтому я начал с нуля и хотел бы узнать, могу ли я получить некоторые мнения о том, где я начал.

Я, очевидно, настроил свой код в глобальном пространстве имен; но я также хотел бы сохранить это пространство имен довольно чистым. Мой главный app.js хранит файлы классов отдельно от глобального пространства имен; вы можете зарегистрировать класс (чтобы его можно было создать), используя функцию reg(), а функция inst() создает экземпляр класса из массива классов. Таким образом, помимо 3-х методов пространство имен MyApp имеет только Router, Model и View:

var MyApp = (function () {

    var classes = {
        Routers: {},
        Collections: {},
        Models: {},
        Views: {}
    };

    methods = {

        init: function () {
            MyApp.Router = MyApp.inst('Routers', 'App');
            MyApp.Model = MyApp.inst('Models', 'App');
            MyApp.View = MyApp.inst('Views', 'App');
            Backbone.history.start();
        },

        reg: function (type, name, C) {
            classes[type][name] = C;
        },

        inst: function (type, C, attrs) {
            return new classes[type][C](attrs || {});
        }

    };

    return methods;

}());

$(MyApp.init);

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

MyApp.reg('Models', 'App', Model);

Это похоже на ненужный способ организовать код? У других есть примеры того, как организовать действительно большие проекты со многими маршрутизаторами, коллекциями, моделями и представлениями?

Ответ 1

Недавно я работал над проектом Backbone под названием GapVis (здесь, представленный здесь контент). Я не знаю, действительно ли это "действительно большой", но он большой и относительно сложный - 24 класса просмотра, 5 маршрутизаторов и т.д. Возможно, стоит взглянуть, хотя я не знаю, что все мои подходы будут Соответствующий. Вы можете увидеть некоторые из моих соображений в длинном вступительном комментарии в главном файле app.js . Несколько ключевых архитектурных вариантов:

  • У меня есть модель singleton State, которая содержит всю текущую информацию о состоянии - текущее представление, какие идентификаторы модели мы ищем и т.д. Каждое представление, которое должно изменить состояние приложения, делает это, устанавливая атрибуты на State, и каждое представление, которое должно отвечать на состояние, прослушивает эту модель для событий. Это даже верно для представлений, которые изменяют состояние и обновление - обработчики событий пользовательского интерфейса в events никогда не повторно отображают представление, это делается вместо этого посредством привязки функций отображения к состоянию. Этот шаблон действительно помог разделить взгляды друг от друга - представления никогда не вызывают метод на другом представлении.

  • Мои маршрутизаторы обрабатываются как специализированные представления - они реагируют на события пользовательского интерфейса (например, вводя URL), обновляя состояние, и они реагируют на изменения состояния путем обновления пользовательского интерфейса (т.е. изменения URL-адреса).

  • Я делаю несколько вещей, похожих на то, что вы предлагаете. В моем пространстве имен есть функция init, похожая на вашу, и объект settings для констант. Но я помещал большинство классов модели и представления в пространство имен, потому что мне нужно было ссылаться на них в нескольких файлах.

  • Я использую систему регистрации для своих маршрутизаторов и считаюсь одной для своих просмотров, так как хороший способ сохранить "мастер-классы" (AppRouter и AppView) от того, чтобы знать о каждом представлении, Однако в случае AppView оказалось, что порядок детских представлений важен, поэтому я закончил жесткое кодирование этих классов.

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

Ответ 3

Я использую пространство, аналогичное тому, что вы делаете (по крайней мере для части классов), и все мои модели, представления и контроллеры выглядят следующим образом:

просмотров/blocks.js:

(function(cn){
    cn.classes.views.blocks = cn.classes.views.base.extend({

        events: {},

        blocksTemplate: cn.helpers.loadTemplate('tmpl_page_blocks'),

        initialize: function(){
        },

        render: function(){
            $(this.el).html(this.blocksTemplate());
        },

        registerEvents: function(){},
        unregisterEvents: function(){}
    });
})(companyname);

Мое пространство имен JavaScript выглядит так, хотя я улучшаю его каждый раз, когда я создаю новое приложение:

 companyname:{                                                                                                                                                                                 
   $: function(){},      <== Shortcut reference to document.getElementById                                                                                                                      
   appView: {},          <== Reference to instantiated AppView class.                                                                                                                           
   classes = {           <== Namespace for all custom Backbone classes.                                                                                                                         
     views : {},                                                                                                                                                                                
     models : {},                                                                                                                                                                               
     collections: {},                                                                                                                                                                           
     controllers : {},                                                                                                                                                                          
     Router: null                                                                                                                                                                               
   },                                                                                                                                                                                           
   models: {},          <== Instantiated models.                                                                                                                                                
   controllers: {},     <== Instantiated controllers.                                                                                                                                           
   router: {},          <== Instantiated routers.                                                                                                                                               
   helpers: {},         <== Reusable helper platform methods.                                                                                                                                   
   currentView: {},     <== A reference to the current view so that we can destroy it.                                                                                                          
   init: function(){}   <== Bootstrap code, starts the app.                                                                                                                           
 } 

Что бы я хотел, чтобы все мои взгляды имели, я добавил базовое представление. Мой контроллер вызовет registerEvents на любом новом представлении, которое он создает (после рендера) и unregisterEvents, перед тем, как он его уничтожит. Не все представления имеют эти два дополнительных метода, поэтому он сначала проверяет существование.

Не забывайте, что все представления имеют встроенный this.el.remove();. Это не только убивает элемент контейнера views, но и отбрасывает все связанные с ним события. В зависимости от того, как вы создаете свои представления через свой контроллер, вы можете не захотеть убить элемент и вместо этого this.el.unbind() отключить все события.

Ответ 4

Фактически, по-разному есть преимущества и недостатки разных способов. Самое главное - найти подходящий способ организации файлов. Ниже приводится организация проекта, который я сейчас делаю. Таким образом, фокус будет таким же, как и файлы, связанные с модулем, помещаются в папку. Например: модуль people, этот модуль все файлы помещаются в каталог modules/base/people. После обновления и обслуживания этого модуля нужно только сосредоточиться на файлах в этом каталоге в строке, не будет влиять на файлы вне каталога и улучшена ремонтопригодность.

Надеюсь, что мой ответ может помочь вам, я надеюсь, вы получите ценный совет.

enter image description here