Большая организация веб-приложений 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, и мне пришлось выработать большую часть этого, когда я шел.
Ответ 2
Эти 2 ресурса помогли мне настроить мои базовые приложения на прочном основании:
Ответ 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]()