воскресенье, 19 октября 2014 г.

Underscore.js: микрошаблоны JavaScript

Трудности перевода.

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

Первое понятие относится к области программной архитектуры и означает некоторые идеи или концепции, которые можно выделить во множестве программных проектов вне зависимости от языка программирования. Наиболее корректный перевод слова pattern - повторяющийся мотив (или рисунок), подобно рисунку плитки или обоев. Принятый в IT-среде перевод - шаблоны проектирования (или даже "калька" - "паттерны" проектирования).

Второе понятие (о котором и пойдёт речь в данной заметке) связано с вёрсткой веб-страниц. Шаблоном (или веб-шаблоном) называют текстовую строку (или даже файл), содержащую как обычную последовательность html-тегов, выводимую без изменений, так и специальные символы и ссылки, которые некоторая процедура заменяет на конкретные значения. Более подходящим названием для этого программного элемента был бы термин "бланк". Подобно своему бумажному прототипу веб-шаблон состоит из неизменных, заранее отпечатанных в типографии элементов, часто утвержденных некоторым стандартом и пустых клеток, ячеек или полос, которые заполняются от руки или на печатной машинке. Такие клетки и ячейки часто содержат подсказки мелким шрифтом - Ф.И.О. (фамилия-имя-отчество) или М.П. (место печати).

Немного истории.

Первые "шаблоны", которые "бланки" появились в языке КОБОЛ, ставшем стандартом в бизнес-среде. Зарплатные ведомости или различные отчеты представляют собой те самые бланки, которые содержат стандартные элементы и конкретные данные в виде фамилий и сумм. Древние языки программирования не имели такой роскоши как конкатенация строк, поэтому вывод данных, содержащих постоянные и переменные данные был довольно громоздким. Типичная функция вывода на экран могла выводить только один параметр - строку или переменную, а иногда для вывода разных типов данных требовались разные функции. Нужно было следить за всеми пробелами и переводами строк, выравниваниями, вывод разрывался в самом неподходящем месте.

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

Name: John  Surname: Smith  Country: USA  Salary: $30000

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

prints("Name: ");     prints(name); 
prints(" Surname: "); prints(surname); 
prints(" Country: "); prints(country); 
prints(" Salary: $"); printn(salary); 
println();

можно использовать такую конструкцию:

printf("Name: %s Surname: %s Country: %s Salary: $%d\n", 
name, surname, country, salary);

Первый параметр функции printf и является таким бланком-шаблоном. Значения переменных, которые передаются в функцию как остальные параметры, заменяют специальные символы %s (строковая переменная) и %d (числовая переменная).

Для работы с шаблонами в Underscore и lodash используется функция _.template и переменная _.templateSettings.

_.template(templateString, [settings])            // underscore
_.template(templateString, [data], [settings])    // lodash

Функция template является генератором функций на основании строки шаблона (templateString). Это строка может содержать три типа тегов, содержимое которых преобразуется при выводе (все остальные части строки выводятся без изменений):

<% %>  - вычисление (evaluate), RE: /<%([\s\S]+?)%>/g
<%= %> - замена (interpolate), RE: /<%=([\s\S]+?)%>/g
<%- %> - эскапирование (escape), RE: /<%-([\s\S]+?)%>/g


Содержимое тега "вычисление" - обычный JavaScript-код. Шаблонизатор заменяет код на результат его вычисления.
Содержимое тега "замена" - ключи некоторого объекта (который передается в качестве параметра сгенерированной функции). В итоговой строке ключи объекта заменяются на значения.
Тег "эскапирование" ведёт себя аналогично тегу "замена" с HTML-эскапирование результатов.

Результат выполнения _.template() - функция, которая может получать объект с данными, заполнять шаблон этими данными и возвращать текстовую строку (это может быть строка таблицы или элемент списка HTML-документа).

var tmpl = "Hello, <%= name %>!";
var data = { name: "John" };

var ftmpl = _.template(tmpl);
console.log(ftmpl(data));      // > Hello, John!

В случае lodash объект для заполнения шаблона можно передать в качестве параметра не сгенерированной функции а сразу функции template.
console.log(_.template(tmpl, data)); // Только lodash
Или же учитывая тот факт, что template возвращает функции, можно вызвать ее немедленно в обеих билиотеках:

console.log(_.template(tmpl)(data)); // lodash и underscore

Параметры шаблонов хранятся в объекте _.templateSettings, но могут перекрываться параметрами из опционального объекта settings.

Переменная _.templateSettings является объектов с ключами interpolate, evaluate, escape, содержащими соответствующие регулярные выражения для указания форматов тегов, используемых в шаблоне. Для того, чтобы использовать теги в стиле Mustache можно использовать следующие регулярные выражения:

_.templateSettings.evaluate = /\{\{([\s\S]+?\}\}/g;
_.templateSettings.interpolate = /\{\{=([\s\S]+?\}\}/g;
_.templateSettings.escape = /\{\{-([\s\S]+?\}\}/g;

По умолчанию шаблон может "заполняться" объектом с любым именем, лишь бы этот объект имел поля с ключами, совпадающими с теми, которые используются в шаблоне. Для этого внутри генерируемой функции используется крайне медленный оператор with. Если поместить в строку шаблона тег интерполяции в виде ключ.значение и указать имя объекта из которого шаблон будет брать значения, в параметре settings:

_.template("Hello, <%= data.name %>", { variable: data })(data);

вместо


_.template("Hello, <%= name %>")(data);

заполнение шаблона будет происходить гораздо быстрее (до 10 раз). Однако при использовании шаблона теперь всегда нужно будет использовать временную переменную с именем, которое "прописано" в шаблоне.

Комментариев нет :

Отправить комментарий