Эта статья была рецензирована Агбонгхама Коллинз и Райаном Ченки . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
Underscore.js — это библиотека JavaScript, написанная Джереми Ашкенасом , которая предоставляет функциональные утилиты для различных случаев использования, с которыми мы, как разработчики, можем столкнуться при работе с веб-проектом.
Это делает для кода, который легче читать:
_.isEmpty({}); // true
Это делает для кода, который легче написать:
_.flatten([[0, 1], [2, 3], [4, 5]]); // [0, 1, 2, 3, 4, 5]
Он предлагает функции, для которых нет собственного метода 1: 1:
_.range(5); // [0, 1, 2, 3, 4]
Он даже может использоваться как шаблонный движок сам по себе:
_.template('<p><%= text %></p>', {text: 'SitePoint Rocks!'}); // <p>SitePoint Rocks!</p>
Underscore — это легковесная библиотека (всего 5,7 КБ, минимизированная и Gzipped), которая используется в различных известных проектах, таких как:
Теперь давайте уточним и начнем погружаться в его основные возможности.
Хорошие части
В этом уроке я собираюсь выделить три наиболее распространенных метода Underscore:
Я объясню, как они используются по отдельности, а затем свяжу их вместе, чтобы создать демонстрационное приложение, которое вы можете найти в конце урока . Как всегда, код для этой демонстрации доступен на Github .
Если вы хотите следовать примерам, вам нужно взять копию библиотеки, например, из вашего любимого CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
И если вам по пути нужна помощь, или вам просто интересно узнать больше, не забывайте, что документация Underscore обширна. Он также имеет большое и активное сообщество, что означает, что помощь легко найти.
_.each
: _.each
циклы
Нет ни одного проекта, в котором в какой-то момент кода не было бы ничего похожего на этот фрагмент:
var artists = ['Pharrel Williams', 'Led Zeppelin', 'Rolling Stones']; for(var i = 0; i < artists.length; i++) { console.log('artist: ' + artists[i]); }
Подчеркивание позволяет вам писать эквивалентный код, используя синтаксис, который является более читабельным:
var artists = ['Pharrel Williams', 'Led Zeppelin', 'Rolling Stones']; _.each(artists, function(artist, index, artists) { console.log('artist: ' + artist); });
Аккуратно, а? _.each()
принимает два параметра:
- Массив (или объект) для перебора.
- Функция обратного вызова.
Для каждого элемента в нашем массиве _.each()
вызовет функцию обратного вызова (в документации упоминается как iteratee ). Внутри обратного вызова мы получаем доступ к еще трем параметрам:
- Значение массива для текущего индекса итерации (
artist
). Например, для приведенного выше фрагмента мы получили бы «Pharrel Williams» для первой итерации. - Номер текущей итерации (
index
), который в нашем случае будет варьироваться от 0 до 2. - Массив, который мы перебираем (
artists
).
Как вы можете видеть, код более читабелен, и мы можем получить доступ к отдельным элементам массива без необходимости в artists[i]
, как мы видели в примере, в котором использовался цикл for
.
Далее мы посмотрим, как ведет себя шаблонизатор.
_.template()
: интуитивно понятный и _.template()
Со времени появления одностраничного приложения наличие надежного шаблонизатора внешнего интерфейса стало фундаментальной потребностью в нашем рабочем стеке.
Underscore предоставляет шаблонизатор, который для тех, кто знаком с такими языками, как PHP или Ruby on Rails, покажется довольно знакомым.
Продолжая наш предыдущий фрагмент, мы продемонстрируем, как работает _.template()
. Мы сделаем это, добавив пару строк в наш код, как показано ниже:
var artists = ['Led Zeppelin', 'ACDC', 'Rolling Stones'], artistTemplate = _.template('<li><%= artist %></li>'), content = ''; _.each(artists, function(artist, index, artists) { content += artistTemplate({ artist: artist }); }); var container = document.createElement('ol'); container.innerHTML = content; document.body.appendChild(container);
Здесь мы _.template()
функцию _.template()
со строковым аргументом, который включает некоторые данные внутри разделителей ( <%= artist %>
). При таком _.template()
возвращает функцию, которую мы можем использовать снова и снова.
Мы можем вызвать нашу новую функцию, используя artistTemplate()
, передав ей литерал объекта в качестве аргумента. Это вернет строку, которую мы первоначально передали в _.template()
, заменяя любые свойства объекта, которые соответствуют свободным переменным шаблона. В нашем случае <%= artist %>
будет заменено значением в атрибуте artist
объекта.
Механизм шаблонов Underscore позволяет не только заменять отдельные значения, но и выполнять сценарии внутри самого шаблона. С помощью одной модификации мы можем сделать наш фрагмент еще более мощным.
var artists = ['Led Zeppelin', 'ACDC', 'Rolling Stones'], artistTemplate = _.template( '<% _.each(artists, function(artist, index, artists) { %>' + '<li><%= artist %></li>' + '<% }); %>' ), content = artistTemplate({ artists: artists }); var container = document.createElement('ol'); container.innerHTML = content; document.body.appendChild(container);
Мы включили наш вызов _.each()
в строку, которая представляет наш шаблон, что приводит нас к изменению способа вызова шаблона. Поскольку теперь мы выполняем итерации внутри функции _.template()
, мы можем передать полный массив artistTemplate()
в artistTemplate()
(ранее мы передавали отдельных художников). Вывод этого кода будет таким же, как в предыдущем примере.
Когда мы хотим, чтобы _.template()
оценивал код JavaScript, нам просто нужно окружить наш код между <% %>
_.template()
<% %>
вместо <%= %>
.
Поскольку _.template
шаблона, сгенерированного _.template
работает так же, как и _.template
функции, мы можем продвинуть наш фрагмент на один шаг вперед и вызвать один шаблон из другого, используя теги <% %>
. Таким образом, мы можем создавать многократно используемые шаблоны, поскольку у нас может быть другой шаблон-обертка для нашего списка исполнителей и просто вызывать шаблон для каждого из элементов, которые он содержит.
Наконец, давайте посмотрим на _.filter()
.
_.filter()
: все, что вам нужно, это булева функция
_.filter()
получает массив и функцию обратного вызова в качестве аргументов. Затем он вызывает функцию для каждого из элементов в массиве и возвращает новый массив, содержащий те элементы, для которых функция оценивается как нечто истинное .
Наша функция обратного вызова также получит три аргумента, как в случае _.each()
: элемент в массиве, соответствующий текущему индексу итерации, индекс итерации и сам массив.
Чтобы прояснить это, давайте внесем несколько изменений в наш фрагмент.
var artists = ['Led Zeppelin', 'ACDC', 'Rolling Stones'], artistTemplate = _.template( '<% _.each(artists, function(artist, index, artists) { %>' + '<li><%= artist %></li>' + '<% }); %>' ), content = artistTemplate({ artists: _.filter(artists, function(artist, index, artists) { return artist === 'ACDC'; }) }); var container = document.createElement('ol'); container.innerHTML = content; document.body.appendChild(container);
Как вы уже догадались, в нашем шаблоне мы получим ['ACDC']
в качестве аргумента массива. Вот демонстрация того, что мы получили до сих пор.
Достаточно сказано. Давайте заставим вещи работать на что-то, что имеет немного больше смысла.
Наше демо-приложение
Не забудьте, код для этой демонстрации доступен на Github .
Мы создадим небольшое приложение, которое использует API, отображает полученную информацию и позволяет пользователю фильтровать то, что отображается. Для этого мы будем использовать:
В частности, приложение будет извлекать некоторую информацию об исполнителе из Spotify, и с помощью Underscore _.template
, _.each
и _.filter
мы будем отображать ее на странице и позволять пользователю сузить результаты по жанрам.
Для этого мы разделим наш код на три разных модуля:
-
_isAwesome.Config
: содержит информацию, которую мы будем использовать в приложении. -
_isAwesome.Template
: заботится о компиляции шаблона. -
_isAwesome
: это основной модуль, отвечающий за реагирование на действия пользователя и обновление пользовательского интерфейса.
Все они следуют шаблону модуля .
Модуль конфигурации
Модуль Config
содержит идентификаторы используемых шаблонов, а также URL-адрес API, который мы запросим, а также идентификаторы исполнителей, которые мы хотим получить из Spotify. Таким образом, мы можем добавить больше художников, просто добавив дополнительные элементы в массив.
Шаблонный модуль
Этот модуль отвечает за компиляцию шаблонов, вызывая getTemplates()
в модуле Config
.
Основной модуль
Этот модуль отвечает за отправку Ajax-запроса на URL-адрес, который мы получаем из модуля Config
и отрисовку содержимого с использованием шаблонов из модуля Template
.
Помимо этого, этот модуль также заботится о фильтрации элементов на основе фильтра, по которому щелкнул пользователь.
Оба фильтра и наши шаблоны включены как часть HTML.
Для реализации фильтрации мы будем полагаться на атрибуты данных HTML 5 и интерфейс данных jQuery . Это скорее вопрос удобства, но если вы хотите сделать это изначально, поддержка браузера очень хорошая .
Это разметка кнопок, которые мы будем использовать для фильтрации:
<button class="btn btn-default sized" data-filter-field="genres" data-filter-value="album rock" data-action="filter">Album Rock</button>
Это пример объекта, который мы передадим нашей функции фильтра:
{ action: 'filter', field: 'genres', value: 'rock' }
Мы будем иметь HTML для наших шаблонов как часть нашего index.html
внутри <script>
, который мы предотвращаем от выполнения, устанавливая его тип в нечто отличное от обычного text/javascript
. Просто для согласованности мы установим это underscore/template
.
У нас будет два шаблона. Первый будет содержать список художников, а второй будет содержать отдельных художников для отображения. Как мы видели выше, мы будем использовать то, что мы называем встроенными шаблонами . Мы будем вызывать один шаблон ( 'item-tpl'
) из другого ( 'item-list'
).
Затем в нижней части файла мы включим наши библиотеки и наши три сценария. Кроме того, просто чтобы сделать его более привлекательным, у нас в заголовке будет несколько основных стилей.
Вот и все.
Вывод
Подчеркивать — это радость работы, и, как я продемонстрировал, он позволяет писать чистый, читаемый и простой в обслуживании код.
Есть еще пара вещей, которые мы могли бы добавить в наше приложение (например, динамически генерировать наши фильтры с помощью _.pluck () ), но я думаю, у нас достаточно для начала.
Как насчет тебя? Ты работал с Underscore? Хотели бы вы попробовать? Вы пробовали альтернативу (например, lodash ), которая предоставляет аналогичные возможности? Позвольте мне знать в комментариях ниже.