Подробный обзор Backbone.Marionette (Часть 2).

В первой части этой серии статей мы обсуждали приложение Backbone.Marionette. В этот раз мы рассмотрим модульную систему, встроенную в Backbone.Marionette. Попасть к списку модулей вы можете через пункт Application (приложение). Модули сами по себе – это довольно информативная тема, которая заслуживает подробного рассмотрения в отдельной статье.

Что такое модули?

Прежде чем мы углубимся в основные детали использования модульной системы Marionette, необходимо четко определиться со значением термина модуль. Модуль – это независимый фрагмент кода, который выполняет определенную функцию. Конкретно взятый модуль можно использовать совместно с другими модулями для создания целостной системы. Чем более независимым является фрагмент кода, тем легче его можно будет заменить или провести внутреннюю модификацию, не затрагивая другие части системы.

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

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

Инициализация модуля.

Давайте начнем с основных пунктов инициализации модуля. Как уже упоминалось, перейти к списку модулей можно через раздел Application. Все действия мы будем подкреплять примерами. Затем мы сможем использовать команду module для инициализации модуля.

var App = new Backbone.Marionette.Application();
var myModule = App.module(‘myModule’);

Это довольно просто, правда? Так и есть, но это всего лишь пример создания простейшего модуля. А что конкретно мы создали? По сути, мы сказали приложению, что хотим создать шаблон модуля, без добавленного нами функционала, и что он будет называться myModule (в соответствии с аргументом, который мы передали в module). Но что такое шаблон модуля? Это экземпляр объекта Marionette.Module.

Module содержит встроенные функциональные возможности, такие как обработка событий (через EventAggregator, которые мы подробно обсудим в следующей статье), начиная с инициализаторов (как у Application) и заканчивая финализаторами (мы рассмотрим их в разделе «Запуск и отключение модулей»).

Стандартная инициализация модуля.

Теперь давайте рассмотрим пример инициализации модуля для заданных нами функциональных требований.

App.module("myModule", function(myModule, App, Backbone, Marionette, $, _){

    // Private Data And Functions
    var privateData = "this is private data";
    var privateFunction = function(){
        console.log(privateData);}
    // Public Data And Functions
    myModule.someData = "public data";
    myModule.someFunction = function(){
        privateFunction();
        console.log(myModule.someData);}
});

Как вы видите, здесь представлено много команд. Давайте посмотрим на первую строку и разберем последовательность действий. Как и раньше, мы вызываем App.module и присваиваем имя модулю. Но теперь мы также передаем значение функции. Функция содержит несколько аргументов. Готов поспорить, вы можете определить какие именно, просто взглянув на имена, которые я им задал, но я все равно объясню:

  • myModule является тем самым модулем, который вы пытаетесь создать. Помните, что он уже создан для вас, и это новый экземпляр объекта Module. Возможно, что вы захотите расширить его при помощи каких-то новых свойств или методов; в противном случае можно просто придерживаться краткого синтаксиса, который не требует передачи в функцию.
  • App — это Application объект, который вызывается функцией module.
  • Backbone — это, естественно, ссылка на Backbone библиотеку.
  • Marionette — это ссылка на Backbone.Marionette библиотеку. На самом деле к ней можно попасть через Backbone, но вы также можете создать псевдоним и сделать имя короче.
  • $-это ваша DOM библиотека, которая будет либо JQuery либо Zepto (или, возможно, что-то еще в будущем).
  • _ — это ссылка на Underscore или Lodash, в зависимости от того, что вы используете.

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

Нужно отметить, что большинство из этих аргументов не нужны; в конце концов, у вас уже есть доступ ко всем ссылкам? Тем не менее, данная возможность полезна в нескольких ситуациях:

  • Когда появляется необходимость сократить имена аргументов, для экономии байтов памяти.
  • Если вы используете RequireJS или другой модуль загрузки, вам нужно только, создать зависимость объекта от функции Application. Все остальное будет доступно благодаря аргументам, вызываемым функцией Module.

Как бы там ни было, но давайте вернемся к объяснению остальных действий, выполняемых кодом выше. Внутри функции, вы можете использовать символы закрытия и создавать частные переменные и функции, что мы, собственно говоря, и сделали. Вы также можете представлять данные и функции открыто, добавляя их в качестве свойств myModule. Это и есть процедура создания и расширения нашего модуля. Нет необходимости что-либо возвращать, потому что модуль будет доступен непосредственно через команду App, обо всем этом я расскажу в теме “доступ к модулю” в разделе ниже.

Примечание: убедитесь, что вы всего лишь добавляете свойство к module переменной и не устанавливаете ее значение равным чему-нибудь (например, myModule = {…}). В случае, если вы все же присвоили какое-то значение module переменной, то поменяется адресация, и позже в модуле появятся соответствующие изменения.

Ранее я отмечал, что можно использовать в качестве пользовательских аргументов. Фактически, вы можете использовать столько пользовательских аргументов, сколько захотите. Взгляните на код ниже, чтобы увидеть, как это сделать.

App.module("myModule", function(myModule, App, Backbone, Marionette, $, _, customArg1, customArg2){
    // Create Your Module
}, customArg1, customArg2);

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

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

App.module("myModule", function(){ // Private Data And Functions
    var privateData = "this is private data";
    var privateFunction = function(){
        console.log(privateData);}
    // Public Data And Functions
    this.someData = "public data";
    this.someFunction = function(){
        privateFunction();
        console.log(this.someData);}
});

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

Разбиение процесса инициализации.

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

// File 1
App.module("myModule", function(){
    this.someData = "public data";});
// File 2 
App.module("myModule", function(){ // Private Data And Functions
    var privateData = "this is private data";
    var privateFunction = function(){
        console.log(privateData);}
    this.someFunction = function(){
        privateFunction();
        console.log(this.someData);}
});

Этот код дает тот же результат, что и предыдущая инициализация, с тем лишь отличием, что она распределена. Это работает, потому что в File 2 происходит вызов модуля, который мы инициализировали в File 1 (при условии, что File 1 был запущен до File 2). Если вы пытаетесь получить доступ к частной переменной или функции, она должна быть определена в модуле инициализации, там, где она используется, потому что доступ возможен только в рамках структурного блока.

Вызов модуля.

Что хорошего в создании модулей, если мы не можем получить к ним доступ? Для полноценного использования модуля необходимо правильно организовать функцию его вызова. В самом первом примере фрагментов кода, вы видели, что когда я вызывал функцию module, я присвоил ее возвращение значения переменной. Это потому, что мы используем один и тот же метод и инициализации и вызова модулей.

 var myModule = App.module("myModule"); 

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

Однако, это не единственный способ вызова модулей. Когда модуль будет создан, он получит привязку непосредственно к Application объекту, для которого был создан. Это означает, что вы можете также использовать нормальное обозначение точки доступа к модулю; но на этот раз, обозначение должно быть определено заранее, иначе вы получите неопределенность.

 // Works but I don't recommend it 
 var myModule = App.myModule;

Хотя представленный синтаксис короче, но он не передает нужный смысл для других разработчиков. Я бы рекомендовал использовать функцию module для получения доступа к нужным модулям. Прежде всего потому, что очевидным становится вызов модуля, а не какого-то из свойств App. Одновременно это удобно и рискованно, ведь вам придется создавать новый модуль, если он еще не существует. Риск заключается в возможной ошибке написания имени модуля. У вас не будет никакого способа проверить правильность работы модуля и его существование, пока вы не попытаетесь организовать его вызов.

Подмодули.

Модули также могут содержать подмодули. К сожалению, функция Module не имеет своего собственного module метода, поэтому вы не можете добавлять к ней подмодули напрямую, но это нас не остановит. Вместо того чтобы создавать подмодуль, вы вызываете функцию module на App, как вы это привыкли делать; но для имени модуля, вам необходимо поставить точку (.) после имени родительского модуля, а затем разместить название подмодуля.

 App.module('myModule.newModule', function(){ ... }); 

Благодаря использованию точечного разделителя в имени модуля, Marionette может определить, что команда перед точкой должна создавать подмодуль. Всегда присутствует потенциальная опасность в том, что если родительский модуль не был создан до момента вызова, он будет создан вместе с его подмодулем. Проблемы могут также возникнуть из-за неправильного написания, о котором я упоминал ранее. Вы могли бы завершить создание модуля, который вам не нужен, но обратите внимание, что подмодуль должен быть приложен к нему, а не к модулю, который вы будете использовать.

Доступ к подмодулям.

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

// These all work. The first example is recommended
var newModule = App.module('myModule.newModule');
var newModule = App.module('myModule').newModule;
var newModule = App.myModule.newModule;
// These don't work. Modules don't have a 'module' function
var newModule = App.myModule.module('newModule');
var newModule = App.module('myModule').module('newModule');

Любой из этих методов доступа к подмодулю будет одинаково хорошо работать только в том случае, если и модуль и подмодуль уже созданы.

Запуск и остановка модулей.

Если вы читали предыдущие публикации статей из серии о функции Application (приложение), то знаете, что организацию функции Application нужно начинать с команды start. Подобная организационная структура запуска применяется и к модулями, а остановлены они могут быть с помощью команды stop.

Как вы помните (если вы читали предыдущую статью), вы можете добавить инициализаторы с помощью команды addInitializer к функции Application, и они будут вызываться одновременно при запуске функции (или будут немедленно вызваны, если функция Application уже работает). Несколько другие события происходят, когда вы начинаете работу с функции Application. Вот все события по порядку:

  • срабатывание initialize:before события,
  • начинается вызов всех инициализированных модулей,
  • запускаются все инициализаторы в том порядке, в котором они были добавлены,
  • срабатывание initialize:after события,
  • срабатывание start события.

Функция Module ведет себя очень похожим образом. Число событий, и некоторые из имен событий различны, но в целом это тот же самый процесс. При запуске модуля, это:

  • срабатывание before:start события,
  • начинается вызов всех его инициализированных подмодулей,
  • запускаются все его инициализаторы в том порядке, в котором они были добавлены,
  • срабатывание start события.

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

  • срабатывание before:stop события,
  • остановка работы подмодулей,
  • запуск финализаторов в том порядке, в котором они были добавлены,
  • срабатывание stop события.

Инициализаторы и финализаторы могут иметь и несколько другое применение. На самом деле, они являются весьма полезными при использовании в модуле определения. Таким образом, вы можете инициализировать модуль без фактического создания любых объектов, которые будут использоваться, а затем написать собственные инициализаторы, чтобы начать создавать объекты и их установки, такие, как в примере ниже.

App.module("myModule", function(myModule){
    myModule.startWithParent = false;
    var UsefulClass = function() {...}; // Constructor definition
    UsefulClass.prototype ... // Finish defining UsefulClass...
    myModule.addInitializer(function() {
        myModule.useful = new UsefulClass();
        // More setup
    });
    myModule.addFinalizer(function() {
        myModule.useful = null;// More tear down
    });
});

Автоматический и ручной запуск.

Когда модуль определен, по умолчанию происходит его автоматический вызов в то же время, что и его родительской функции (или корневой объект Application или родительский модуль). Если модуль инициализирован в родительской функции, которая уже начались, его вызов происходит немедленно.

Вы можете убрать автоматический запуск модуля, изменяя его инициализацию одним из двух способов. Внутри блока инициализации, можно разместить команду startWithParent со значением false, или вы можете передать значение объекта (вместо функции) в module, который уже имеет команду startWithParent со значением, установленным на false и свойство define, чтобы заменить нормальную функцию.

// Set 'startWithParent' inside function
App.module("myModule", function(){
    // Assign 'startWithParent' to false
    this.startWithParent = false;});
// -- or --
// Pass in object 
App.module("myModule", {
    startWithParent: false,
define: function(){
        // Define module here }
});
App.start();
// myModule wasn't started, so we need to do it manually
App.module('myModule').start("Data that will be passed along");

Сейчас у модуля не будет автозапуска. Вы должны вызвать команду start вручную, чтобы запустить его, как сделал я в приведенном выше примере. Данные, которые передаются командой start, могут быть абсолютно любого типа, и они будут приняты вместе с подмодулями, когда те начнут свое действие, инициализаторами, и событиями before:start и start.

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

Другие события и встроенные функциональные возможности.

Я уже упоминал, что Module содержит некоторые встроенные функциональные возможности, такие как EventAggregator. Мы можем использовать команду on для модуля, чтобы наблюдать за событиями, связанными с запуском и остановкой. Это еще не все. Если нет никаких других встроенных событий, то модуль может определить и инициировать свои собственные события. Взгляните:

App.module('myModule', function(myModule) {
    myModule.doSomething = function() {
        // Do some stuff
        myModule.trigger('something happened', randomData); }
});

Теперь, когда мы вызвали doSomething в модуле, произойдет вызов события something happened (что-то случилось), на которое вы можете подписаться:

App.module('myModule').on('something happened', function(data) {
    // Whatever arguments were passed to `trigger` after the name of the event will show up as arguments to this function
    // Do something with `data`
});

Это очень похоже на то, что мы делаем с событиями в коллекциях, моделями и представлениями в нормализированном Backbone коде.

Как в действительности можно использовать модуль.

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

Gmail является единственным приложением, которое на самом деле состоит из нескольких небольших приложений: почтовое приложение, чат-приложение, приложение для телефонных звонков и менеджер контактов. Каждое из них является независимым — оно может существовать само по себе — но все они расположены в рамках одного приложения и могут взаимодействовать друг с другом. Когда мы впервые запускаем Gmail, менеджер контактов не появляется, также нет и окна чата. Если бы мы попытались представить такую структуру с помощью приложения Marionette, каждое из этих подчиненных приложений будет представлено в виде модуля. Когда пользователь нажимает кнопку чтобы открыть менеджер контактов, нужно сначала остановить приложение электронной почты (потому что оно скрывается для нормальной скорости работы, хотя мы можем оставить его работающим и просто убедиться, что оно не отображается в объектной модели документов DOM), а затем запускать менеджер.

Рассмотрим другой пример приложения, построенного в основном из виджетов. Каждый виджет будет представлять собой отдельный модуль, который вы можете запускать и останавливать для того, чтобы показать или скрыть его. Данная возможность характерна для настраиваемой панели, такой как iGoogle или приборной панели в задней части WordPress.

Естественно, что мы не ограничены в использовании модулей Marionette для подобных случаев, хотя это и трудно использовать в традиционном смысле. Все потому, что модули Marionette полностью содержат значения объектов, в то время как традиционные модули “классы” предназначены для создания экземпляров.

Вывод.

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

Источник изображения на первой странице: ruiwen.

(al) (ea)

Приступая к работе с JQuery mobile

jQuery Mobile 1.2 compatibility table

Уже давно никто не сомневается в том, что в процессе дизайна мобильного устройства важно учитывать потребности потенциальных пользователей. В то время, как мобильные браузеры и платформы создают все новые проблемы, мобильный фреймфорк JQuery Mobile, с открытым исходным кодом, поможет нам добиться успеха в разработке мобильных приложений.

JQuery Mobile  может помочь в следующих случаях:

Читать далее

Календари для рабочего стола: Апрель 2013 (Пасхальное Дополнение)

Smashing Desktop Wallpaper - April 2013 (Easter Edition)

 

Мы всегда стараемся бросить вызов нашим артистическим способностями и сотворить интересные, красивые и творческие работы. И, как большинство дизайнеров, мы обычно обращаемся к различным источникам вдохновения. И в самом деле, мы нашли наилучшее — обои на рабочий стол, которые более своеобразны, нежели другие. Эта креативная миссия продолжается уже на протяжении более пяти лет, и мы очень благодарны всем дизайнерам, которые вносили и продолжают усердно добавлять свои работы каждый месяц.

Этот пост содержит обои для рабочего стола, созданные художниками со всего света на апрель 2013 года. Обе версии, как с календарем, так и без него вы можете скачать бесплатно. Пришло время обновить ваши обои.

Читать далее

Одна невыдуманная история адаптации сайта под мобильные устройства

Contact information and directions were made more prominent in the layout on small screens, to assist clients who are visiting for the first time.

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

На данный момент одной из самых интересных концепций, к которой мы уже начали обращаться, является небезызвестная идея “Сначала мобильные” от дизайнера Люка Врублевского. Впервые он рассказал о своём подходе на страницах личного блога, а затем написал ряд книг на эту тему. Как правило, такой подход позволяет сконцентрироваться на главных задачах и моментально перейти к сути разработки как относительно программного содержания, так и относительно взаимодействия с пользователями.

Читать далее

Навигация для мега-сайтов.

Image showing navigation dominating page real estate

Для подавляющего большинства веб-сайтов, создание навигации не является особенно сложной задачей. Часто достаточно всего лишь разместить основную панель навигации, поддерживаемую суб-навигацией.

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

Читать далее

До свидания, Zen Coding. Привет, Emmet!

Emmet Demonstration - Initializers

Еще в 2009 году, Сергей Чикуенок написал статью, представляя новый способ написания HTML и CSS кода. Этот революционный плагин, под названием Zen Coding, помогал разработчикам на протяжении многих лет и в настоящее время достиг нового уровня.

Emmet, ранее известный как Zen Coding, является самым производительным и экономичным по времени плагином текстового редактора, который вы когда-либо увидите. Моментально оборачивая простые и краткие фрагменты кода в сложные, Emmet может помочь вам работать более продуктивно.

Читать далее

C-Swipe: эргономичное решение для навигации Android приложений

Flipboard app for Android 4.

В настоящее время существует около 3997 различных мобильных устройств на Android. В идеале навигация вашего мобильного приложения должна работать на всех этих устройствах. C-Swipe может вам в этом помочь. C-Swipe — это альтернативная навигационная модель для планшетных компьютеров и мобильных устройств. Эта модель считается наиболее эргономичной по сравнению с остальными.

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

Читать далее

Отзывчивый веб-дизайн и физические параметры мобильных устройств

Mobile devices

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

Читать далее

Об отзывчивом веб — дизайне

create-first-iphone-app-500

С момента появления iPhone в 2007 году, сенсорный экран мобильных устройств произвел настоящий фурор. Сенсорный экран добавил новые возможности, но в тоже время добавил и новые проблемы, способы мышления и взаимодействия между пользователем и мобильным продуктом. Теперь самый важный вопрос для всех, клиентов и разработчиков, как мы можем обеспечить лучший мобильный опыт пользователей? Читать далее