Статьи

Лучшие практики при работе с шаблонами JavaScript

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


Если вам нужно что-то с большим интересом, могу я порекомендовать руль ?

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

Если проект относительно прост, вы можете использовать Underscore.js . По большей части эта библиотека предлагает утилиты функционального программирования, но у нее есть метод _.template который не может облегчить задачу. По умолчанию он использует разделители ERB <%= %> , но это легко изменить. Прелесть этого решения в том, что в любом проекте, для которого требуются шаблоны, Underscore, скорее всего, уже загружен, просто из-за его общей полезности. Чтобы узнать больше о Underscore, ознакомьтесь с полным руководством Siddharth прямо здесь, на Nettuts +.

Если вам нужно что-то с большим интересом, могу я порекомендовать руль ? Благодаря множеству полезных блочных выражений (таких как #each для зацикливания и #if для условных выражений) и возможности регистрации собственных вспомогательных функций, Handlebars предоставит вам все необходимое для создания даже самых сложных шаблонов.

Если вы не знакомы с Handlebars, Габриэль Манрикс поможет вам в этом уроке .

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

Рули
Handlebars — отличный движок для JavaScript.

Большинство библиотек шаблонов превращают объект данных, который вы передаете в шаблон, в контекст.

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

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

1
2
3
4
5
6
7
// assume data object and template function
 
data.formatPrice = function (priceInCents) {
    return «$» + (priceInCents / 100).toFixed(2);
}
 
var html = template(data);

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

1
2
3
4
5
var productTemplate = function (data) {
    var template = _.template(«the template string»);
    data.helperFunction1 = function () { return «whatever» };
    return template(data);
};

Есть несколько способов улучшить это (вы можете начать с кэширования «необработанной» шаблонной функции вне этой функции, возможно, через замыкание), но это основная идея. Теперь вы можете просто передать свои данные в этот productTemplate и получить доступ к своим вспомогательным функциям.


Есть несколько альтернативных решений, которые могут быть лучше, особенно в более сложных сценариях.

Все шаблоны JavaScript, очевидно, начинаются как текст. Наиболее популярное (и естественное) место для их хранения — в вашем HTML-документе — обычно в теге script с атрибутом альтернативного type , чтобы браузер не пытался их выполнить. Достаточно легко получить атрибут innerHTML тега script и передать его в функцию создания шаблона, когда вы будете готовы.

Однако это не единственный способ сделать это; на самом деле, это не может быть оптимальным способом. Есть несколько альтернативных решений, которые могут быть лучше, особенно в более сложных сценариях.

Первая альтернатива — хранить их все в файле JavaScript. Конечно, это означает, что ваши шаблоны будут храниться в виде строк вместо более удобочитаемого HTML-кода с отступами, но оставайтесь со мной на секунду! Во-первых, для шаблонов, которые длиннее одной строки (большинство шаблонов), вам не нужно использовать громоздкую оберточную строку. Вместо этого попробуйте что-то вроде этого:

1
2
3
4
5
6
7
8
9
Templates = {};
 
Templates.contactEntry = [
    «<h1> {{fullName}} </h1>»,
    «<ul>»,
        «<li> Email: {{email}} </li>»,
        «<li> Phone: {{tel}} </li>»,
    «</ul>»
].join(«\n»);

Хранение шаблона в массиве, как это делает его гораздо проще обрабатывать. Используя такой синтаксис, вы можете легко сохранить все ваши шаблоны в их собственном файле JavaScript и загрузить этот файл на страницу, прежде чем вам понадобятся шаблоны. И, конечно, вам не нужно хранить их все в одном объекте Template , но это помогает организовать вещи. Этот объект Templates может даже быть свойством глобального объекта приложения (например, MyApp.Templates ).

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

1
2
3
4
5
for (var tmpl in Templates) {
    if (Templates.hasOwnProperty(tmpl) {
        Templates[t] = _.template(Templates[t]);
    }
}

Если вы используете AMD в своем приложении, этот метод все еще будет работать; просто поместите это в модуль шаблонов, который возвращает объект Templates . Однако многие решения AMD имеют текстовый плагин, который позволяет загружать текстовые файлы; вместо обычного объекта модуля вы получите строку взамен. Если вы используете библиотеку RequireJS, вам нужно будет включить плагин text.js в тот же каталог, что и файл require.js . Затем вы можете сделать что-то вроде:

1
2
3
require([«text!templates/document.html»], function (documentTemplate) {
 
});

Этот параметр documentTemplate будет строкой, содержащей любое содержимое в этом файле templates/document.html . Делая это таким образом, вы не сможете поместить несколько шаблонов в один файл, если не хотите манипулировать этой строкой.


Если вы используете конвейер активов в приложении Rails, используйте Sprockets для предварительной компиляции функций шаблона.

Если вы подумаете об этом на секунду, браузер проделывает дополнительную работу каждый раз, когда вы создаете шаблон. Обычно этот шаблон начинается с строки, которую вы передаете в функцию создания шаблона. Эта функция возвращает другую функцию, в которую вы можете передавать данные и получать HTML. Дополнительная работа — это часть «создание шаблона»; нет никаких причин, почему это нельзя сделать до того, как JavaScript будет отправлен клиенту. Желательно, чтобы вы добавили эту работу в процесс сборки вместе с минимизацией CSS и конкатенацией JS.

К сожалению, предварительная компиляция шаблонов JavaScript не так проста, как минимизация или конкатенация … по крайней мере, пока, вероятно, из-за множества способов создания шаблонов. Если вы используете Grunt или Yeoman, вы можете посмотреть плагины ( как этот ) на веб-сайте Grunt . Если вы используете конвейер активов в приложении Rails, вы можете воспользоваться Sprockets для предварительной компиляции функций вашего шаблона.

Да, и если вы любитель приключений (и подписчик Tuts + Premium), вы можете присоединиться ко мне, поскольку я предварительно скомпилирую шаблоны в моем курсе Advanced Backbone Patterns and Techniques .


Нет оценки в шаблонах.

Не так давно, когда я искал другой проект , я наткнулся на интересную идею о шаблонах JavaScript в превосходной книге « Рецепты с Backbone» . С тех пор это стало лучшей практикой в ​​сообществе: нет оценки в шаблонах. Конечно, интерполяция переменных — это, строго говоря, оценка, но здесь я больше ссылаюсь на логический код. Вы можете поместить любой JavaScript-код в теги-разделители, но он может легко выйти из-под контроля. Мы все знаем, что рекомендуется хранить ваши HTML, CSS и JavaScript отдельно; это облегчает отслеживание кода и обнаружение ошибок, когда это необходимо. То же самое верно для шаблонов: они должны быть местом только для интерполяции значений. Любая логика или преобразование данных должны выполняться вне шаблона.

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

1
2
3
4
5
6
<h1> My List </h1>
<ul id=»myList»>
    <% list.forEach(function (item) { %>
        <li> <%= item.name %> </li>
    <% });
</ul>

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

Шаблон оболочки:

1
2
3
<h1> My List </h1>
<ul id=»myList»>
</ul>

Под-шаблон:

1
<li> <%= name %> </li>

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

В соответствии с этим, рекомендуется следовать указаниям используемой платформы или библиотеки. Например, я обнаружил, что при использовании Backbone с шаблонами Underscore проще использовать внешние циклы и под-шаблоны: минимальная функциональность шаблонов Underscore не предлагает никакого синтаксиса циклов, а метод render Backbone является отличным местом для выполнения. что зацикливание и вставка суб-шаблонов. Однако при использовании Meteor, который строит шаблоны Handlebars, гораздо проще зацикливать внутри шаблонов блок #each ; (и использовать суб-шаблоны тоже, если хотите).


Узнайте больше о Backbone.stickit в Tuts + Premium.

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

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

1
this.model.on(‘change’, this.render, this);

Таким образом, всякий раз, когда атрибут модели изменяется, render функция render шаблона, и шаблон перерисовывается. Кроме того, вы можете использовать плагин, например, backbone.stickit , который будет управлять привязками для вас. Если вы работаете с Meteor и используете один из его реактивных источников данных, вы получите эту привязку бесплатно — никаких дополнительных работ не требуется. Я не достаточно знаком с другими фреймворками, чтобы точно знать, как они это делают, но у любого фреймворка, который стоит использовать, должна быть похожая функция.


Очень быстро ваши шаблоны могут выйти из-под контроля и стать громоздкими.

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

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


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


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