Директивы в разметке или разметка с кодом?

в 9:12, , рубрики: html, mvc, php, оптимизация, Программирование, шаблонизаторы, метки: , , ,

Практически все существующие html-шаблонизаторы используют проверенный способ — «директивы в разметке». Это позволяет создать видимость инкапсуляции шаблона и, как будто бы, позволяет достичь разделения логики и отображения. На практике, шаблоны невозможно реализовать без условий, циклов и вызовов функций, а это — зависимость непосредственно разметки от используемого языка и его возможностей. При этом поддержка и изменение шаблона ложится на плечи программиста — ведь верстальщик не должен знать о ядре системы и ее объектах.
Меня давно заинтересовал другой подход к проблеме — он пришел из JavaScript, а если взять глобальнее, то из самой сути HTML.

Итак, в этой статье я расскажу о способе шаблонизации «HTML+Код».

Суть проблемы

В проектах, где количество разработчиков больше одного, задача разделения кода и отображения — одна из первоочередных. Также эта задача стоит при использовании 2-х и более языков программирования в проекте — к примеру, нам нужно иметь возможность генерировать один и тот же HTML при запросе с сервера и при вызове модальной формы на клиентской машине.
Для последнего случая существуют решения в виде JSRender и других JS-шаблонизаторов. Но, естественно, поддержка шаблонизаторов для серверного языка и javascript — нетривиальная задача, а с учетом возможности вызова сторонних объектов из шаблона — и вовсе нереальная.
Да и говоря в целом про JS-шаблонизацию и перекладывание логики на JS (ExtJS и иже с ним), появляется куча минусов, вот основные:

  1. Перекладывание обработки кода на клиентскую машину хорошо для малобюджетных проектов, но для крупных это означает зависимость от скорости устройства пользователя, версий JS и различий их реализации в браузере (как следствие удорожание поддержки).
  2. Для нормального разделения логики при загрузке страницы приходится использовать несколько Ajax-запросов с прелоадерами, что тоже увеличивает стоимость загрузки страницы. Хотя этот пункт важен не всегда, но мой опыт говорит об обратном.
  3. Конечно же, SEO-оптимизация. Пока поисковые системы не научатся (официально) рендерить страницу с учетом JavaScript (лентяи!) — говорить о полноценной работе с JS в таких проектах явно рано.
  4. Безопасность — чем больше логики видит ваш потенциальный враг, тем легче ему будет вас взламывать

Кстати, есть еще одно решение шаблонизации — это XSLT. На входе мы имеем XML и правила его обработки, на выходе — HTML. Но тут минусов, пожалуй, будет даже больше:

  1. Изучение еще одного языка и, как следствие, найм еще одного сотрудника — вряд ли кого-то порадует.
  2. Показная декларативность в этой технологии на практике выливается в ужасную нечеловеческую логику.
  3. Все известные мне реализации 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();
}

Выводы

У этого подхода есть минусы (замеченные мною, прошу всех указать на остальные):

  1. Императивность — если у вас используется шаблонизатор с возможностью внутренней оптимизации директив, то это может быть минусом.
  2. Скорость работы — в целом, это не так, но пока метод работает медленнее нативной шаблонизации со вставками PHP-кода (ну или скомпилированного таким образом).
  3. Нет встроенной поддержки в серверных языках (но на c++ можно сделать оболочку над webkit). Хотя с другой стороны, smarty тоже не встроен в PHP.

Но плюсы гораздо мощнее:

  1. Полноценное разделение кода и разметки.
  2. Нет необходимости учить еще один язык (от шаблонизатора) — все frontend-разработчики (да и не только) знают JQuery и пр.
  3. Возможность использования всех средств серверного языка в шаблонах (классы, функции и т.д.), в отличие от обычных шаблонизаторов со своим языком.

В целом, все десктопные и мобильные платформы (Windows, IOS, Android и др.) используют именно императивный подход к загрузке шаблона, т.е. есть XAML, XIB или XML и отдельный контроллер. Почему в web-приложениях используется другой подход — может быть, по инерции с тех пор, когда HTML был дополнением к строкам? Кто знает — расскажите.

Дорогие друзья, если вам интересно развитие такого направления — то скажите об этом или проголосуйте.

P.S. К сожалению, я не придумал картинок к данному посту.

Автор: arvitaly

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js