Что такое шаблонизатор html
Шаблонизация в PHP
Шаблонизация — это работа по интеграции готовой, статичной вёрстки на сайт.
Ведя работу над сайтом, написанном на PHP, мы разрабатываем логику, которая управляет представлением.
Логика — это PHP-сценарии, где происходит объявление переменных, функций, использование массивов и циклов, получение и обработка информации.
Представление — это внешний вид сайта. Сюда входят его страницы, которые содержат общие элементы оформления (шапка, меню, подвал) и контент (страницы, статьи и тому подобное).
Имея на руках готовую вёрстку, надо правильно её внедрить в работающий сайт, чтобы в определённых местах страниц информация выводилась динамически, под контролем PHP-сценариев.
Что такое «шаблон» и причём тут рыба?
Шаблон — это готовая вёрстка страницы или блока, которая состоит только из оформления, и не содержит никакого контента (полезной информации).
Шаблон по своей сути — это обычный PHP-сценарий, который на 90% состоит из HTML-кода и только на 10% из PHP-конструкций. Основная задача программиста в процессе работы над сайтом — это превратить статичные HTML-страницы в динамичные PHP-шаблоны, которые будут использоваться для показа итоговых страниц.
Но если статичная вёрстка страниц не содержит динамического контента, то что будет на его месте до начала внедрения этих страниц? Когда дизайнер или верстальщик хотят показать как будет выглядеть страница на сайте, то вместо реального контента используется так называемая «рыба».
Рыба — это заполнитель. Бессмысленный текст, который используется в вёрстке, чтобы показать как будет выглядеть страница, наполненная контентом.
Проблемы типичного процесса вёрстки
Когда верстальщик работает над несколькими страницами, ему приходится копировать бол́ьшую часть HTML-кода между страницами, так как эти страницы содержат много повторяющихся элементов: подключение стилей, шапка сайта с логотипов, футер сайта, различные меню и тому подобное.
В результате, когда дело доходит до правок, то при необходимости поменять что-нибудь в той же шапке, это изменение нужно будет сделать во всех HTML-файлах, чтобы они выглядели одинаково. Конечно, такой мартышкин труд никому не понравится.
Общие фрагменты страниц сайта
Взглянув на страницы практически любого сайта, можно заметить их сходство друг с другом. Ведь все страницы состоят из общих частей, которые не меняются, а также из областей с уникальным содержимым. И как было бы удобно редактировать общие блоки, вроде шапки сайта, отдельно, а затем вкладывать их во все страницы. Тогда при смене логотипа потребуется внести изменение только в одном месте, а все страницы сайта обновятся автоматически.
Шаблонизация — это и есть деление всей вёрстки на независимые шаблоны и дальнейшее их подключение и вкладывание друг в друга.
Сбор страницы из частей
Если поделить всю вёрстку сайта на отдельные, небольшие шаблоны мы получим сразу несколько преимуществ. Станет легче следить за единообразием интерфейса. Выделив представление сайта в шаблоны, мы также существенно упростим PHP-сценарии, ведь в них останется только PHP-код. Можно даже доверить верстальщику редактировать непосредственно шаблоны, так как в них почти нет программного кода, а тот, что есть, очень простой.
Потренируйтесь собирать страницы из фрагментов, выполнив это задание.
Термины шаблонизации
В разговоре о шаблонизации очень важно придерживаться определённых терминов, чтобы всегда было понимание, о чём идёт речь. Договоримся использовать следующую терминологию:
Лейаут — это шаблон, который содержит HTML-код, общий для всех страниц сайта. В нём могут содержаться подключение стилей, мета-теги, шапка, подвал. Также лейаут содержит область для вставки уникального содержимого каждой страницы.
Шаблон страницы — это шаблон с уникальным для одной страницы HTML-кодом. Например, для главной страницы там может быть список новостей. Также шаблон страницы может включать в себя блоки.
Блок — это шаблон очень небольшого блока страницы. Например, это может быть один элемент списка новостей. Удобство блоков в том, один блок могут включать разные страницы.
Содержимое шаблона
Что же находится внутри шаблона? Вы уже знаете, что шаблон почти полностью состоит из HTML-кода. Но помимо HTML-тегов, там также лежат данные и простая логика.
Данные в шаблоне
Шаблон показывает динамическую информацию. Прилагательное «динамическая» означает, что эти данные могут меняться и показываться в зависимости от различных условий. Сама информация, как правило, хранится в базе данных, а PHP-сценарий извлекает её оттуда и передаёт в шаблон.
Вне зависимости от источника информации есть правило, которое никогда нельзя нарушать: любой шаблон (лейаут, шаблон страницы, блок) должен иметь доступ только к тем данным, которые ему явно передали.
Такую изоляцию данных обеспечивает специальная функция-шаблонизатор, о которой пойдёт речь далее.
Логика шаблона
В шаблоне должна находиться только несложная логика. Иными словами, шаблоны не содержат «тяжёлого» PHP-кода, а только простые конструкции.
Так, в шаблоне можно показывать переменные, использовать условия, циклы, обходить массивы, вызывать функции и подключать файлы. Всё другое запрещено. Оставшаяся бизнес-логика остаётся в PHP-сценариях, которые вызывают шаблоны и передают в них информацию.
Функция-шаблонизатор
Шаблонизатор — это функция, которая подключает файл шаблона, передаёт ему данные и возвращает сгенерированный HTML.
Именно шаблонизатор является тем клеем, что скрепляет воедино отдельные шаблоны в итоговую страницу. Работает он следующим образом: PHP-сценарий страницы выполяет все действия для подготовки необходимой информации, к примеру, запрашивает записи из базы данных. Эти записи в виде массива отправляются шаблонизатору вместе с именем шаблона страницы.
Шаблонизатор подключает указанный файл шаблона и передаёт туда всю информацию. Но, вместо вывода на экран содержимого этого шаблона, он захватывает получившийся HTML-код и возвращает его.
Затем сценарий вызывает шалбонизатор ещё раз, но теперь с его помощью подключает общий лейаут, куда отправляется общая информация, а также содержимое страницы, полученное из предыдущего шага. Весь результат работы выводится на экран.
Пример использования
Посмотрим на примере, как это всё работает. Начнём с того, что определим три шаблона: лейаут, шаблон страницы и какой нибудь блок.
Напоминаю, что в лейаут выносим общий HTML-код.
[layout.php]
Теперь очередь за шаблоном страницы:
[main.php]
Здесь обрати внимание, что шаблон страницы помимо интерации по массиву, для каждого его элемента вызывает функцию-шаблонизатор. Шаблонизатор получает контент из шаблона блока и показывает его внутри списка.
11 шаблонизаторов для фронтенда
Какой шаблонизатор страниц для своего веб-проекта выбрать из всего этого зоопарка? Обо всем этом в нашем новом материале.
doT.js
Быстрый и лаконичный JavaScript-шаблонизатор для Node.js и браузера.
Dust.js
Dust.js – JavaScript-шаблонизатор от Linkedin. Он происходит от семейства ctemplate и спроектирован, чтобы работать асинхронно как на сервере, так и в браузере. Главное достоинство Dust в том, что он позволяет сильно сократить объем логики в шаблоне. Вместо типичного вида шаблона:
Dust позволяет делать так:
dōmo позволяет совмещать html-разметку и css в JavaScript-логике и делать это как в браузере, так и на сервере.
HyperScript
Этот шаблонизатор позволяет создавать JavaScript-шаблоны страницы и на сервере, и на клиенте, а также обладает чистым синтаксисом.
Marco
Marco – это легковесный и быстрый html-шаблонизатор от eBay. Он работает на Node.js и поддерживает потоковую передачу данных, асинхронный рендеринг и пользовательские теги.
Простой однофайловый компонент, который срендерит кнопку и счетчик, он будет показывать, сколько раз на кнопке кликнули.
Mustache
Шаблонизатор с малым количеством логики. Работает с огромным количеством языков и фреймворков, вроде Node.js, а также поддерживается многими известными редакторами кода, например Emacs, Atom и Vim.
Handlebars.js
Handlebars.js – самое известное расширение Mustache. Оно обратно совместимо с Mustache, и, так же как и родитель, разделяет основную логику и разметку.
Nunjucks.js
Наиболее сложный, расширяемый и функциональный из современных JavaScript-шаблонизаторов. Разработан Mozilla, совместим с Node.js.
Template7
В JavaScript-шаблонизаторе Template7 основное внимание уделяется разработке под мобильные платформы, а его синтаксис напоминает синтаксис Handlebars.
Главный принцип шаблонизатора Haml (HTML abstraction markup language) – разметка должна быть красивой.
Pug (ex Jade)
Бывший шаблонизатор Jade, ныне известный как Pug, обладает самым чистым синтаксисом среди перечисленных в этой подборке. Он вдохновлен Haml, но имеет меньшее количество служебных символов, а вложенность в нем регулируется так же как и в Python – отступами.
15 шаблонизаторов для фронтенд-разработки
Число JS-библиотек ни в коей мере не уменьшается; наоборот, оно растёт с каждым днём. Когда мы доходим до приложений JS, лучшим выбором оказываются шаблоны, чем полноценные библиотеки, потому что это приводит к более чистому базовому коду и лучшему процессу работы с ними.
Не так давно я писал, что вы могли бы попробовать написать свою библиотеку, когда придёт время. Шаблонизаторы же требуют несколько больших навыков и понимания языка, с которым вы работаете, поэтому лучше полагаться на любой шаблонизатор из имеющихся в списке ниже.
Их список можно найти в Википедии, там отлично сравниваются движки для разных языков программирования для веба, но там действительно не фокусируются на одном языке, поэтому я хотел бы посмотреть, как много движков можно перечислить для Javascript.
Если вы разрабатываете на Javascript, то узнаете ряд движков, но можно узнать и о некоторых новых. Я с удовольствием продолжу этот список вместе, и надеюсь, что он вас порадует.
ICanHaz.js
Очень простая библиотека для работы с шаблонами Mustache, другой, более быстрый движок. С ним можно определять фрагменты шаблонов в тегах скриптов с type=”text/html” и вызывать их через id, с валидацией.
Hogan.js
Twitter стремится быть частью опенсорс-сообщества. Hogan.js можно использовать как компоновщик промежуточных шаблонов, для компиляции их заранее, чтобы браузер обрабатывал эти шаблоны динамически.
Содержит то же, что и Mustache, но делает всё быстрее и эффективнее.
Handlebars.js
Handlebars — расширение шаблонизатора Mustache, созданный Крисом Ванстратом (Chris Wanstrath). Эти шаблонизаторы — имеют языки, не перегруженные логикой, разделяющие код и представление, совпадающие с нашими лучшими ожиданиями.
mustache.js
Это — самый популярный шаблонизатор сейчас в мире, и почти каждая библиотека этой категории так или иначе проистекают и идейно перекликаются с ним. Документацию очень легко прочитать, понять, и настоятельно рекомендуется использовать — он прекрасно запускается на многих языках программирования, что обеспечивает уровень популярности для него выше среднего.
Transparency
Transparency — клиентский движок шаблонизации, связывающий данные в DOM. Пишется на нём просто, понятно, простой нативный HTML и такой же JS. Нет необходимости изучать синтаксис шаблонов, всё очевидно и просто в использовании.
doT.js
Хорошо подходит для Node.js-сообщества. Быстрый, малоразмерный, без зависимостей. Героически добился баланса скорости и гибкости. (Во многом унаследовал качества Underscore.js-шаблонизатора (компонента этой библиотеки — перев.)).
Walrus
В современных веб-приложениях участвует много логики представления. Эта логика не относится к коду приложения и моделям, это — уровень представления. Язык шаблонизатора — хорошее место для неё.
Эта цель преследуется так концентрированно, что скорость шаблонизации не приоритетна; скорее, Walrus ориентирован на облегчение разработки шаблонов веб-приложений.
chibi.js
Chibi даёт всё для экономии траффика и времени для отображения шаблона, небольшая и лёгкая библиотека, которая поможет лучше шаблонизировать приложение. Больше фокусируется на CSS вместо использования анимации. (Дословная «вода» автора — перев.)
templayed.js
Это — самый маленький шаблонизатор, с которым вы столкнётесь, гарантированно (Nano — перев.). Он построен на основе Mustache, прост в использовании и понимании. Сайт имеет большой демо-пример, в котором можно запускать и тестировать код.
Подобно templayed, ECT тоже имеет демо-страницы установки на сайте, с которыми можно «поиграться» и понаблюдать живые результаты. Он сделан для скорости, и заявляет о себе как о самом быстром шаблонизаторе для JS (построен на Coffeescript). Совместим с Node.js и имеет понятный синтаксис. На Github есть бенчмарки и модульные тесты, показывающие эффективность этой библиотеки.
Pithy.js
Имеется внутренний DSL для генерации HTML в JavaScript. Это отлично для небольших фронтенд-проектов, но не рекомендуется для тяжёлых HTML-страниц.
T.js использует простую структуру данных Javascript для представления данных HTML / XML.
Nunjucks
созданный в Mozilla, Nunjucks сделан для нуждающихся в производительности и гибкости из-за возможности расширять пользовательскую библиотеку плагинов и функций.
Dust.js
Dust расширяет Mustache и предлагает высокое качество исполнения по сравнению с другими решениями из этого списка. Содержит очень простой и понятный API.
Движки шаблонизации Javascript
Я не пытался приводить примеры, потому что множество ссылок на официальные страницы содержат демонстрации.
Надеюсь, что вы смогли открыть для себя новые варианты для вашего следующего проекта. Уверен, что много альтернатив не упомянуто, но использование перечисленных ощущается больше всего.
Как и зачем создавать собственный шаблонизатор для DOM
Авторизуйтесь
Как и зачем создавать собственный шаблонизатор для DOM
Фронтенд-разработчик в Miro и автор курса «Мидл фронтенд-разработчик» в Яндекс.Практикуме
Посмотрим в код
Прежде чем начинать изучать шаблонизацию, давайте вспомним два базовых подхода к созданию разметки.
Перед нами два отрывка кода, которые отвечают за одинаковую разметку для списка чатов: декларативный и императивный. Так это выглядит в HTML:
Теперь создаём такое же дерево, но через DOM API в JavaScript:
HTML выглядит понятнее, чем множество однотипных вызовов DOM API. Однако есть и минус: HTML статичен — его прописывают один раз и пользуются, а если нужно описать десятки или сотни однотипных элементов на странице, придётся написать всю разметку.
Есть огромный соблазн отдать эту рутинную работу коду. Или, например, когда десятки или сотни элементов приходят с сервера в процессе работы приложения, нужно создавать dom-элементы динамически, наполняя их данными, как в примере кода выше.
3–4 декабря, Онлайн, Беcплатно
Где-то на стыке этих двух подходов работы с DOM появляется шаблонизация. С одной стороны, она позволяет описать разметку декларативно, а с другой стороны — описать динамические данные прямо в этой разметке.
Представьте, если бы вы могли создать DOM-дерево с динамическими данными, не используя createElement и описание свойств, а например, вот так:
Такая запись читается проще, чем манипуляции с document.createElement. Здесь можно:
К подобному синтаксису шаблона мы и будем стремиться.
Зачем это всё?
Действительно, зачем в век React, Angular и Vue заниматься такой мелочью, как шаблонизация? Все современные фреймворки умеют динамически подставлять данные в DOM и даже больше: циклы, условные операторы, компоненты, жизненный цикл.
В конце концов, если не пользоваться фреймворками — есть уже готовые шаблонизаторы, можно же взять их?
Сколько бы ни было на рынке инструментов, какие бы возможности они ни предоставляли, фактически всё сводится к 26 буквам английского алфавита, десятку символов и идеям: паттернам программирования и проектирования, принципам построения чистой архитектуры и чистого кода, шаблонам решения типовых задач.
Понимая идеи и принципы решения тех или иных задач, вам будет проще разобраться в любых существующих инструментах — вы будете понимать, какую задачу и с помощью каких принципов они решают. Такой подход намного продуктивнее, нежели простое заучивание API-фреймворков.
Именно поэтому написана эта статья — чтобы показать один из возможных подходов к динамическому формированию разметки.
Идея и синтаксис шаблона
Шаблонизатор — это функция, которая на вход получает некоторую разметку в виде чистого текста и контекст, а на выход даёт готовый DOM-элемент или текст, готовый к использованию в innerHTML. Под контекстом подразумевается набор информации, который подставляется вместо переменных в шаблонной строке.
Чтобы шаблонизатор мог работать, нужно придумать соглашение о синтаксисе — как описать в шаблоне места, куда должны подставляться данные из контекста. Обычно используют какие-нибудь скобки, в которые помещают идентификаторы переменных.
Скобки удваиваются, чтобы не спутать динамические данные со статическим текстом, который может быть в шаблоне. Они достаточно часто употребляются в текстах, поэтому без удвоения их легко перепутать:
Если выбрать круглые скобки в качестве маркера динамических данных и не удвоить их, непонятно, где переменная, а где просто текст в скобках.
Примем за обозначение переменных в шаблоне удвоенные фигурные скобки — такое решение достаточно распространённое. Таким образом шаблон для примера выше будет таким:
Хранение шаблона
Так как шаблон — это строка, то его можно хранить просто в строке 🙂
Если пойти дальше и развивать идею, окажется, что необязательно хранить строку с шаблоном именно в файле с кодом. Хранение шаблона превращается в отдельную задачу в духе «получить откуда-то шаблон», например, из тега
Значение у скрипта type отличается от text/javascript, поэтому браузер не будет пытаться обработать его как JavaScript.
Получить содержимое шаблона очень просто:
Также организовать хранение шаблона можно в файле с любым расширением и импортировать его. У популярных шаблонизаторов зафиксированы свои расширения, чтобы редакторы кода могли легко их распознавать.
Можно создать и использовать шаблоны следующим образом:
Однако для такого решения, возможно, придётся настроить ваш сборщик, чтобы он корректно импортировал подобные файлы.
Желаемый синтаксис использования
Теперь, когда мы понимаем синтаксис и метод хранения шаблона, пора подумать о том, как оживлять этот шаблон и превращать его в HTML.
Что нам предстоит сделать:
Представим, как может выглядеть третий пункт:
На самом деле API шаблонизатора может выглядеть так, как вам нравится — всё зависит от задач.
Мы остановимся на третьем варианте — const markup = new Templator(template); markup.compile(data). Это позволит единожды создать экземпляр с шаблоном и в дальнейшем вызывать его метод compile с нужными данными для получения разметки.
Возможные способы реализации
Есть много способов реализовать шаблонизатора для DOM под капотом — от самых простых до сложнейших. Рассмотрим два основных:
Первый способ — базовый и немного «костыльный». Его опасность — в innerHTML, который нежелательно использовать в более-менее серьёзных приложениях. Второй способ — достаточно мощный: он позволяет вводить различные операторы и операции в шаблон, превращая шаблоны в язык программирования.
Сейчас мы осуществим первый способ реализации, чтобы обрести базовое понимание шаблонизации.
Реализуем шаблонизатор для DOM
Попробуем заставить работать следующий код:
Раз мы приняли решение делать шаблонизатор для DOM на классе, напишем заготовку такого класса:
Конструктор принимает шаблон в виде обычной строки, а его превращение в разметку будет скрыто в методе compile.
Proof of concept — регулярное выражение
Прежде чем писать реальный код в заготовке класса, давайте потренируемся на тестовых данных. В первую очередь нам надо научиться находить в строке шаблонную переменную и на что-то её заменять.
Мы договорились о способе записи переменных в шаблоне — << someVariable >>. Найти такие фрагменты в строке нам поможет простое регулярное выражение /\<\<(.*?)\>\>/.
Попробуем применить регулярное выражение к строке с помощью метода exec:
Итак, если совпадение в строке есть, возвращается массив, у которого во втором элементе лежит то, что нам нужно — то, что внутри фигурных скобок. Это значение мы и будем использовать, чтобы обратиться к ключу объекта-контекста: «Если exec вернул не null, заменим в шаблоне встреченную шаблонную переменную на значение из контекста».
Пробуем дальше — у нас в шаблоне наверняка может встретиться не одна переменная, а несколько.
Дело в том, что у регулярных выражений есть несколько хитростей. В нашем случае регулярное выражение находит первое совпадение и останавливает работу метода exec. Требуется два шага, чтобы заставить регулярное выражение работать со всеми совпадениями.
Посмотрим на примере:
Чтобы не писать exec вручную, завернём его в цикл. Но нам нужно не просто выполнить exec, а ещё и поработать с его результатом. Для этого воспользуемся особенностью JS, когда операция присваивания возвращает присваиваемое значение:
Теперь этот цикл пройдётся по всей строке и найдёт все совпадения — то что нужно!
Пишем метод compile
Теперь, когда у нас достаточно навыков работы с регулярным выражением, нужно его применить. Алгоритм следующий:
Интересный момент в выражении new RegExp(match[0], ‘gi’). Вы помните, что exec первым элементом массива возвращает полное совпадение из строки регулярному выражению. Здесь мы создаём из этого совпадения новое регулярное выражение, чтобы заменить все вхождения в шаблон подобной строки на настоящее значение.
Такая реализация кажется достаточной, но на самом деле нужно разобраться с парой моментов:
Эти вопросы не имеют однозначного ответа и выходят за рамки статьи: у нас нет цели написать полноценный шаблонизатор — мы хотим познакомиться с принципами его построения. Однако остановимся на последнем вопросе, чтобы в будущем не наделать ошибок.
Добавляем «лёгкие» обработчики событий
Итак, если в ключе контекста встречается функция, нужно корректно её обработать. Прежде всего нужно договориться о правиле применения функций в шаблоне, на соблюдение которого мы будем полагаться.
Пусть в шаблоне переменные значения с функциями помещаются только в атрибуты on*:
Вот почему обработчик называется «лёгким» — это не настоящий addEventListener, а использование DOM API. Этот шаблон должен превратиться в следующий HTML:
Обратите внимание: в HTML это именно вызов функции со скобками в конце.
Такой способ можно довольно просто добавить в текущий шаблонизатор, чтобы не перегружать его ненужной логикой. Всё остальное будет лишним и слишком сложным в ситуации простого знакомства с шаблонизацией. А ещё этот механизм не позволяет передавать в функцию атрибут event. Минусов хватает, но начинать с чего-то надо 🙂
Но откуда возьмётся handleClick в области видимости, где его будет вызывать Browser API? Чтобы этот HTML-элемент работал корректно, функция handleClick должна находиться в window — именно там её будет искать браузер.
Помимо добавления скобок, ещё придётся присвоить функцию из контекста в какое-то поле window. Сделаем это:
Заключение
Итак, мы изучили, что такое шаблонизация, какие вопросы стоит задавать при создании шаблонизатора и как на них можно отвечать. Реализация получилась, что называется, на коленке, но она вполне отражает ход размышлений и при этом даже работает 🙂
Несомненно, эту реализацию можно улучшить, но стоит ли? В приведённом случае мы всё равно подвержены уязвимости в виде innerHTML, потому что не создаём в шаблонизаторе настоящий DOM, а только оперируем строкой.
Для создания настоящего DOM надо как минимум написать настоящий парсер для языка разметки, выстроить работу с абстрактным синтаксическим деревом (AST) и создать компилятор из AST в DOM API, чтобы на выходе из шаблонизатора был честный DOM-элемент с наполнением. А ещё чтобы это всё было создано с помощью document.createElement. Но эта тема тянет на несколько статей, и если вам интересно — мы с командой курса «Мидл фронтенд-разработчик» их напишем.
Немного практики
Сделайте так, чтобы в синтаксисе шаблона можно было размещать обращения к полям объекта: