Практически все существующие html-шаблонизаторы используют проверенный способ — «директивы в разметке». Это позволяет создать видимость инкапсуляции шаблона и, как будто бы, позволяет достичь разделения логики и отображения. На практике, шаблоны невозможно реализовать без условий, циклов и вызовов функций, а это — зависимость непосредственно разметки от используемого языка и его возможностей. При этом поддержка и изменение шаблона ложится на плечи программиста — ведь верстальщик не должен знать о ядре системы и ее объектах.
Меня давно заинтересовал другой подход к проблеме — он пришел из JavaScript, а если взять глобальнее, то из самой сути HTML.
Итак, в этой статье я расскажу о способе шаблонизации «HTML+Код».
Суть проблемы
В проектах, где количество разработчиков больше одного, задача разделения кода и отображения — одна из первоочередных. Также эта задача стоит при использовании 2-х и более языков программирования в проекте — к примеру, нам нужно иметь возможность генерировать один и тот же HTML при запросе с сервера и при вызове модальной формы на клиентской машине.
Для последнего случая существуют решения в виде JSRender и других JS-шаблонизаторов. Но, естественно, поддержка шаблонизаторов для серверного языка и javascript — нетривиальная задача, а с учетом возможности вызова сторонних объектов из шаблона — и вовсе нереальная.
Да и говоря в целом про JS-шаблонизацию и перекладывание логики на JS (ExtJS и иже с ним), появляется куча минусов, вот основные:
- Перекладывание обработки кода на клиентскую машину хорошо для малобюджетных проектов, но для крупных это означает зависимость от скорости устройства пользователя, версий JS и различий их реализации в браузере (как следствие удорожание поддержки).
- Для нормального разделения логики при загрузке страницы приходится использовать несколько Ajax-запросов с прелоадерами, что тоже увеличивает стоимость загрузки страницы. Хотя этот пункт важен не всегда, но мой опыт говорит об обратном.
- Конечно же, SEO-оптимизация. Пока поисковые системы не научатся (официально) рендерить страницу с учетом JavaScript (лентяи!) — говорить о полноценной работе с JS в таких проектах явно рано.
- Безопасность — чем больше логики видит ваш потенциальный враг, тем легче ему будет вас взламывать
Кстати, есть еще одно решение шаблонизации — это XSLT. На входе мы имеем XML и правила его обработки, на выходе — HTML. Но тут минусов, пожалуй, будет даже больше:
- Изучение еще одного языка и, как следствие, найм еще одного сотрудника — вряд ли кого-то порадует.
- Показная декларативность в этой технологии на практике выливается в ужасную нечеловеческую логику.
- Все известные мне реализации XSLT — ужасно тормозные (это могло бы быть не так при развитии технологии, но она сама по себе не очень).
Итак, мы хотим использовать чистый платформо-независимый HTML для статичного отображения.
Решение
Нам нужно каким-то образом генерировать HTML на сервере, при этом чтобы сам HTML лежал в отдельном файле, базе или памяти.
Собственно, из условий задачи решение тоже однозначно — мы храним HTML отдельно, а обрабатываем его с помощью какого-то кода.
Так как серверные задачи я чаще всего реализую на PHP — то и рассказывать буду применительно к нему. Но в целом — этот подход можно реализовать абсолютно для любого языка программирования.
В PHP для работы с HTML имеется DOM-библиотека и, конечно, интереснее всего было бы использовать встроенные средства. К примеру, загружаем HTML в DOM, загружаем необходимые нам данные и выводим его.
$doc = new DOMDocument;
$doc->loadHTML("<html><body>Test<div id=t>TEST1</div><br></body></html>");
$doc->getElementById('t')->setAttribute('attribute1','value');
echo $doc->saveHTML();
И это уже работает, но… Ужасный громоздкий код, скорость еще хуже. Парсинг HTML различается от версии к версии и никак не соответствует новым стандартам. Итак, это решение меня категорически не устроило.
И тут сразу вспоминается, как мы решили проблему количества кода с помощью JQuery, mootools, а в последствии, и встроенной функциональности querySelector в браузерах. Это селекторы и XPath. И такой подход кардинально меняет видение, к примеру:
$this('#t')->attr('attribute1','value');
вместо
$doc->getElementById('t')->setAttribute('attribute1','value');
Можно написать собственный парсер HTML на PHP (как я когда-то:-) или воспользоваться DOMDocument. На данный момент (как мне известно) существует несколько реализаций подобного на PHP, к примеру phpQuery. Однако скорость работы говорит нам о невозможности использовать такие решения в production-версиях.
Поэтому, сейчас я пишу PHP-расширение на C. На данный момент в расширении только собственно парсинг HTML в массив. Скорость парсинга для строки 50-100кБ — до 1ms. Сама библиотека классов на PHP, но в планах и ее перевести в расширение. Загрузка полной страницы с использованием 10 шаблонов и загрузкой в них данных (подготовленных заранее) — сейчас до 14ms, но при переходе полностью на C — цифра должна значительно уменьшиться.
Примеры использования
Наверное, этот раздел лишний, так как все прекрасно умеют работать с JQuery и HTML, но все же приведу парочку для наглядности в PHP. Некоторые примеры красивые, некоторые не очень.
Пример №1
Задача: необходимо заполнить значениями форму.
HTML-разметка
<form>
<input type="hidden" name="id" />
<select name="list1"><option value="1">1</option><option value="2">12</option></select>
</form>
Логика:
$object = Objects::get();//ассоциативный массив
foreach( $object as $name => $value)
{
$this('[name=' . $name . ']')->val( $value ); // В зависимости от элемента (select, input или textarea) - выполняется по разному
}
Пример №2
Задача: нам необходимо заполнить список значениями.
HTML-разметка
<ul id="firms">
<li><a href=""></a></li>
</ul>
Логика
$firms_ul = $this('#firms');
$firms_ul_row = $firms_ul->find('li');
$html = '';
foreach( $list as $row )
{
$firms_ul_row->find('a')->html( $row->title )->attr('href', $row->link );
$html .= $firms_ul_row->html();
}
$firms_ul->html( $firms_ul_row );
Пример №3
Задача: Вставить текст в строке.
Строка: на сайте сейчас 9 пользователей.
HTML
на сайте сейчас <UserCounts>9</UserCounts> пользователей.
Соответственно, php-код
$this('UserCounts')->replaceWith( $user_count );
Пример №4
Задача: скрыть блок у неавторизованного пользователя.
HTML
Текст
<div id="cookies"></div>
Текст2
Код
if ( User::isAuth() === false )
{
$this('#cookies')->remove();
}
Выводы
У этого подхода есть минусы (замеченные мною, прошу всех указать на остальные):
- Императивность — если у вас используется шаблонизатор с возможностью внутренней оптимизации директив, то это может быть минусом.
- Скорость работы — в целом, это не так, но пока метод работает медленнее нативной шаблонизации со вставками PHP-кода (ну или скомпилированного таким образом).
- Нет встроенной поддержки в серверных языках (но на c++ можно сделать оболочку над webkit). Хотя с другой стороны, smarty тоже не встроен в PHP.
Но плюсы гораздо мощнее:
- Полноценное разделение кода и разметки.
- Нет необходимости учить еще один язык (от шаблонизатора) — все frontend-разработчики (да и не только) знают JQuery и пр.
- Возможность использования всех средств серверного языка в шаблонах (классы, функции и т.д.), в отличие от обычных шаблонизаторов со своим языком.
В целом, все десктопные и мобильные платформы (Windows, IOS, Android и др.) используют именно императивный подход к загрузке шаблона, т.е. есть XAML, XIB или XML и отдельный контроллер. Почему в web-приложениях используется другой подход — может быть, по инерции с тех пор, когда HTML был дополнением к строкам? Кто знает — расскажите.
Дорогие друзья, если вам интересно развитие такого направления — то скажите об этом или проголосуйте.
P.S. К сожалению, я не придумал картинок к данному посту.
Автор: arvitaly