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

‹ Предыдущий
Следующий ›

Привет, надеюсь, у всех отличный день.

Прежде всего, я хочу поблагодарить Paul Falgout 🎊 Как некоторые из вас могли заметить, эти статьи теперь доступны в Блоге MarionetteJS, и это все благодаря ему. Пол дал мне возможность стать частью этого удивительного сообщества, и я очень благодарен за это, я начал эту серию статей, чтобы улучшить себя и помочь людям, которые, возможно, испытывали трудности, начиная с этого замечательного фреймворка, или, может быть, лучше объясняя некоторые концепции, которые это было не так ясно для них.

Являясь частью блога, я теперь могу общаться с гораздо большим количеством людей, и возможность помочь многим из вас очень полезна. Так что еще раз спасибо за возможность, Пол, и, как всегда, я сделаю все возможное, чтобы продолжать писать потрясающие статьи 🙌

Итак, в прошлой статье мы говорили о том, как использовать Mn.Application в качестве точки входа в наше приложение, а также кратко представили концепцию представления, но что это такое?

Те, кто знаком с шаблоном MVC, должны уже знать, что это такое, но для тех, кто не знаком с View, это в основном то, что вы хотите использовать для рендеринга HTML клиенту (очень краткое и общее объяснение Я знаю, но важно, что это такое).

Когда-то у нас были Mn.LayoutView и Mn.ItemView, но начиная с Mnv3 они были объединены в Mn.View, поэтому, если вы пришли сюда из более старой версии Mn и подумали, что чего-то не хватает, вот почему.

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

Шаблоны

Чтобы отобразить страницу для клиента, Mn использует шаблоны, небольшие фрагменты HTML, которые можно либо определить внутри самого представления, либо мы можем импортировать их из отдельных файлов, ведь это чрезвычайно полезно, если мы хотим повторно использовать определенный шаблон. на других представлениях.

Теперь, хотя мы можем использовать чистые HTML-файлы в качестве наших шаблонов, это не очень интересно, так как большую часть времени мы хотим использовать внутри них динамические данные. См. приведенный ниже пример:

const UserView = Mn.View.extend({ el: 'body' })

Мы создали представление с целью показать клиенту карточку со списком некоторой информации о пользователе, используя свойство el, чтобы указать, что мы хотим отобразить ее в теге body. Для этого мы также создаем html-код:

<div class="user_card">
   <dl>
      <dt>Name</dt>
      <dd> <!-- name here --> </dd>
      <dt>Age</dt>
      <dd> <!-- age here --> </dd>
      <dt>Occupation</dt>
      <dd> <!-- occupation here --> </dd>
   </dl>
</div>

Чтобы сделать это шаблоном нашего представления, нам просто нужно использовать свойство template :

const UserView = Mn.View.extends({
   el: 'body,
   template: require('user_card_template.html')
});
const userView = new UserView().render();

Бум! это все, что вам нужно для рендеринга представления с файлом HTML, есть только одна проблема, мы не отображали никаких пользовательских данных, и это крах использования чистых файлов html. У вас есть кусок статического кода, который вообще не требует динамических данных? Тогда нет проблем, но если вы хотите отображать динамические данные в шаблоне, нам нужно использовать файл Template Engine.

Mn в сочетании с библиотекой под названием underscore у нее есть собственный механизм шаблонов, но, честно говоря, я его ненавижу :P Я думаю, что его синтаксис слишком сложен и не подходит для HTML-кода, что иногда делает весь шаблон нечитаемым. Поскольку использование механизма шаблонов подчеркивания не позволит вам добавить сторонний шаблон, он в основном используется в примерах кода, как вы увидите в Mn официальных документах, но на рынке есть гораздо лучшие варианты, такие как Handlebars синтаксис ведьмы более удобен для пользователя.

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

Примечание. Если вы считаете, что шаблонные литералы слишком запутаны для вас, и вы находите Handlebars более привлекательным, сообщите мне об этом в комментариях, и я сделаю дополнительный раздел о том, как настроить Handlebars для работы. со своим проектом ;)

По умолчанию Mn будет использовать Bb.Model в качестве источника данных для вашего представления, он не нужен, так как вам могут не понадобиться данные, но если он у вас есть, Mn проанализирует его в шаблоне, чтобы мы могли использовать его напрямую:

// This is our User template
const tmpl = ({name, age, occupation}) => `
   <div class="user_card">
      <dl>
         <dt>Name</dt>
         <dd> ${name} </dd>
         <dt>Age</dt>
         <dd> ${age} </dd>
         <dt>Occupation</dt>
         <dd> ${occupation}</dd>
      </dl>
   </div>
`;
// This is the User model we will be using as DataSource
const dataSource = new Backbone.Model({
   name: 'Renato',
   age: 25,
   occupation: 'developer'
});
// This is our User View
const UserView = Mn.View.extend({
   el: 'body,
   model: dataSource,
   template: tmpl
});
const userView = new UserView().render();

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

Но что, если я хочу использовать дополнительную информацию, которой нет в модели?

Mn покрыл это. Как я уже сказал, Mn.View будет использовать Bb.Model в качестве источника данных по умолчанию, но вы всегда можете использовать другой.

Если вы хотите использовать пользовательский источник данных, вы можете использовать метод View templateContext(), который возвращает объект ключ/значение того, что вы хотите передать в шаблон.

// This is our User template
const tmpl = ({name, age, occupation, additionalField}) => `
   ...
   <dt>Additional Content</dt>
   <dd>${additionalField}</dd>
   ...
`;
// This is the User model we will be using as DataSource
const dataSource = new Backbone.Model({
   ...
});
// This is our User View
const UserView = Mn.View.extend({
   el: 'body,
   model: dataSource,
   template: tmpl,
   templateContext(){
      return { additionalField: 'this is an additional field' };
   )
});
const userView = new UserView().render();

Информация в templateContext() объединяется с данными модели, и таким образом мы можем легко назначать динамические данные нашему шаблону.

Регионы

В прошлой статье мы также говорили о регионах и о том, как их можно использовать для компоновки нашей страницы и разделения логических задач путем вложения представлений внутри других представлений. Мы можем сделать это, используя атрибут View regions, который принимает объект ключ/значение в качестве значения:

const RootView = Mn.View.extend({
   ...
   regions: {
      headerRegion: '#header',
      mainRegion  : '#main',
      footerRegion: '#footer' 
   }
   ...
});

Теперь (и это очень важно) мы можем визуализировать новое представление внутри области в любом месте кода, в какой-либо функции, которая вызывается как обратный вызов для события, или даже в initialize() на уже отрендеренном элементе DOM. Mn не накладывает ограничений на то, где вы можете отображать свои представления, но вы должны!

Манипуляции с DOM — сложный процесс, который может серьезно сказаться на производительности, если вы выполняете рендеринг вида за представлением. Чтобы решить эту проблему, Mn предоставляет простой способ рендеринга всех ваших представлений (и вложенных представлений) одновременно. Как? вы просто визуализируете все внутри метода onRender() жизненного цикла, это так просто! и тем самым вы уже улучшаете свой код: D

const RootView = Mn.View.extends({
   ...
   regions: {
      headerRegion: '#header',
      mainRegion  : '#main',
      footerRegion: '#footer' 
   }
   onRender(){
      this.showChildView('headerRegion', new HeaderView());
      this.showChildView('mainRegion', new MainView());
   }
   ...
});

Примечание. Очевидно, что есть несколько вариантов использования, когда вы хотите отображать представления вне метода onRender(), но делайте это крайней мерой, а не шаблоном.

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

Ну, вы можете сделать это, используя метод getChildView(). Вызывая этот метод с именем региона в качестве аргумента, он возвращает представление внутри него, и вы можете использовать его, как обычно, с любым другим представлением.

const RootView = Mn.View.extends({
   ...
   regions: {
      headerRegion: '#header',
      mainRegion  : '#main',
      footerRegion: '#footer' 
   }
   onRender(){
      this.showChildView('headerRegion', new HeaderView());
      this.showChildView('mainRegion', new MainView());
   }
   someFunction(){
      const mainview = this.getChildView('mainRegion');
      mainview.doSomething();
   }
   ...
});

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

UI

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

Вы, наверное, уже заметили, что Mn полагается на jQuery для манипулирования DOM, поэтому вы можете использовать любую функцию jQuery для извлечения, создания и/или управления DOM.

Они пытаются отказаться от этой зависимости, создав DOM API, но, поскольку он все еще находится на ранних стадиях, я не буду использовать его для целей этой статьи, ну, до тех пор, пока он не будет проверен в бою ✌️

Может быть, у вас есть форма, которую вы хотите сериализовать? Вы можете сделать что-то вроде этого:

const formView = Mn.View.extends({ 
   ...
   serializeForm(){
      const data = $('form').serialize();
      // do something with data
   }
})

Это определенно сработает, но прямое манипулирование DOM может вызвать некоторые проблемы!

Что делать, если в DOM есть более одной формы? Мы, очевидно, хотим манипулировать только тем, который связан с этим представлением, но если мы используем селектор jQuery, мы взаимодействуем со всей DOM, а не только с $el представления 😰. А что, если мне нужно использовать этот элемент несколько раз? Продолжать ли мне использовать селекторы jQuery для его поиска? В конечном итоге вы можете присвоить элемент переменной, но что, если он понадобится вам в другой функции? Блин, столько проблем 😤

Mn обеспечивает простой и элегантный способ справиться с этим, позволяя называть части шаблона с помощью свойства ui.

Это дает массу преимуществ:

  • Кэширует элемент, позволяя нам использовать его бесконечно во всем представлении без необходимости постоянно искать его с помощью селектора (более высокая производительность);
  • Используя именованное свойство, мы можем динамически изменять элемент, на который ссылается ui, не теряя контекста! Итак, возможно, у нас есть элемент с идентификатором userList, и мы присвоили ему ui.list. По какой-то причине мы меняем шаблоны и теперь ссылаемся на список с идентификатором betterUserList. Поскольку мы используем ui.list, нам больше ничего менять не нужно, свойство остается тем же, отличается только элемент, на который оно ссылается. Круто, да?
  • Обеспечивает хороший способ разделения проблем. Когда вы видите что-то вроде this.ui, вы сразу понимаете, что это как-то связано с манипуляциями с DOM.

Вот пример:

const formView = Mn.View.extends({ 
   ...
   
   ui: {
      formEl: 'form'
   }
   serializeForm(){
      const data = this.getUI('formEl').serialize();
      // do something with data
   }
})

Итак, мы создаем экземпляр нашего пользовательского интерфейса, назначая объект ключ/значение свойству ui, где ключ — это имя, которое вы хотите присвоить элементу, а значение — jQuery. селектор элемента.

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

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

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

Мир ✌️