четверг, 1 декабря 2016 г.

Шаблоны в веб-разработке

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

Но, как и всё вокруг, наш веб-сайт имеет тенденцию разрастаться и видоизменяться. Из одной веб-страницы мы делаем другую копированием соответствующего HTML-файла и изменением его содержимого, и этих файлов становится всё больше и больше. Рано или поздно мы заметим тревожные симптомы:
  1. различий внутри двух близких по времени файлов наберётся дай бог процентов на 10;
  2. HTML-код на сайте от страницы к странице становится всё более и более разнородным, потому что мы узнаём новые приёмы, а старый код исправлять лень или некогда;
  3. всё труднее поддерживать единообразное оформление, например, чтобы изменить шапку страниц или название одного пункта навигации, приходится править кучу HTML-файлов, валяющихся на сайте с начала времён.
И постепенно становится ясно: пришла пора задуматься о том, как упорядочить этот нарастающий хаос.

К сожалению, это прозрение приходит с осознанием того, что придётся переделать пару дюжин уже существующих и пусть не вписывающихся в ваше новое видение, но хорошо работающих веб-страниц. Что ж, на это надо потратить время - оно того стоит.

Будем использовать модельный пример. Пусть у нас есть сайт, стоящий из трёх веб-страниц, соответствующие HTML-файлы которых выглядят так:

Файл index.html
<html>
<title>Главная страница</title>
<body>
<div>Шапка сайта</div>
<div><a href="first.html">На первую страницу</a> | <a href="second.html">На вторую страницу</a><div>
</body>
</html>

Файл first.html
<html>
<title>Первая страница</title>
<body>
<div>Шапка сайта</div>
<div><a href="/">На главную</a> | <a href="second.html">На вторую страницу</a></div>
<div>Содержимое первой страницы</div>
</body>
</html>

Файл second.html
<html>
<title>Вторая страница</title>
<body>
<div>Шапка сайта</div>
<div><a href="/">На главную</a> | <a href="first.html">На первую страницу</a></div>
<div>Содержимое второй страницы</div>
</body>
</html>

Итак, приступим.

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

Например, можно творчески использовать тег <iframe>, выделив шапку сайта и меню в отдельный фрейм. Наш модельный сайт в этом случае может выглядеть так:

Файл header.html
<html>
<body>
<div>Шапка сайта</div>
<div>
<a target="_top" href="/">На главную</a> |
<a target="_top" href="first.html">На первую страницу</a> |
<a target="_top" href="second.html">На вторую страницу</a>
</div>
</body>
</html>

Файл index.html
<html>
<title>Главная страница</title>
<body>
<div><iframe src="header.html"></div>
</body>
</html>

Файл first.html
<html>
<title>Первая страница</title>
<body>
<div><iframe src="header.html"></div>
<div>Содержимое первой страницы</div>
</body>
</html>

Файл second.html
<html>
<title>Вторая страница</title>
<body>
<div><iframe src="header.html"></div>
<div>Содержимое второй страницы</div>
</body>
</html>

Теперь, если мы захотим вместо "Шапка сайта" написать "Кепка сайта", нам не придётся править все три HTML-файла, достаточно поменять лишь страницу header.html. Но на самом деле это - не очень хороший подход. Так не удастся избавиться от большей части однообразного содержимого HTML-файлов: заголовков, разметки и т.п. Поэтому более разумным вариантом представляется использование шаблонов.

Что такое шаблон в веб-разработке? Ближайшим его аналогом в материальном мире является бланк. Да-да, та самая отпечатанная типографским способом разлинованная бумажка, вписав в которую свои ФИО и другие реквизиты получаешь заявление на загранпаспорт, квитанцию о переводе денежных средств, товарную накладную и т.п. Так что при этом подходе нам понадобится разработать некую заготовку, в которую останется лишь вписать в нужный момент нужные данные. Но кто будет эти данные вписывать?

Тут опять-таки возможны два варианта:
  1. наполнять шаблон данными на сервере и отдавать клиенту готовый документ;
  2. передать шаблон и данные клиенту, и пусть его браузер сам их компонует как хочет.

Первый путь предполагает наличие на стороне сервера какого-то механизма для обработки клиентских запросов. Стоит отметить, что в наше цивилизованное время сами веб-серверы научились в какой-то мере производить необходимые манипуляции со страницами перед отправкой их клиенту. Мы имеем в виду технологию Server Side Includes (SSI).

В частности, наш модельный пример с использованием SSI принимает вид:

Файл header.shtml
<div>Шапка сайта</div>
<div>
<a href="/">На главную</a> |
<a href="first.shtml">На первую страницу</a> |
<a href="second.shtml">На вторую страницу</a>
</div>

Файл index.shtml
<html>
<title>Главная страница</title>
<body>
<!--#include file="header.shtml"-->
</body>
</html>

Файл first.shtml
<html>
<title>Первая страница</title>
<body>
<!--#include file="header.shtml"-->
<div>Содержимое первой страницы</div>
</body>
</html>

Файл second.shtml
<html>
<title>Вторая страница</title>
<body>
<!--#include file="header.shtml"-->
<div>Содержимое второй страницы</div>
</body>
</html>

Как видим, с помощью этого механизма можно собирать веб-страницы из разрозненных частей, избегая таким образом избыточного их дублирования. А с учетом наличия средств, обеспечивающих ветвление, SSI оказывается безусловно интересным вариантом в некоторых случаях.

Более тонкой настройке поддаются программы или скрипты для обработки шаблонов, написанные на каком-нибудь подходящем языке программирования. Это может быть PHP, Python, ASP, node.js, черт, дьявол, да всё что угодно вплоть до bash-скриптов. У каждого из этих языков есть свои достаточно мощные инструменты и модули для работы с шаблонами. Мы не будем углубляться в эти дебри, а покажем на нашем модельном примере сам принцип обработки шаблонов с помощью серверных приложений.

Пусть у нас на сайте установлен Python. Перепишем наш пример следующим образом:

Файл template.html
<html>
<title>%(title)s</title>
<body>
<div>Шапка сайта</div>
<div>
<a href="/">На главную</a> |
<a href="first.py">На первую страницу</a> |
<a href="second.py">На вторую страницу</a>
</div>
<div>%(content)s</div>
</body>
</html>

Файл index.py
from mod_python import Cookie, apache

def index(req):

  return open('template.html').read() % \
    {
    'title' : 'Главная страница',
    'content' : ''
    }

Файл first.py
from mod_python import Cookie, apache

def index(req):

  return open('template.html').read() % \
    {
    'title' : 'Первая страница',
    'content' : 'Содержимое первой страницы'
    }

Файл second.py
from mod_python import Cookie, apache

def index(req):

  return open('template.html').read() % \
    {
    'title' : 'Вторая страница',
    'content' : 'Содержимое второй страницы'
    }

Как видим, в этом примере данные удалось полностью отвязать от их отображения, что является несомненным плюсом. Правда, при этом увеличилась нагрузка на сервер и пришлось изучить Python, но оно того стоит. Весь HTML код оказался в файле шаблона, и если мы, скажем, захотим сверстать наш сайт как-то по-другому, нам не придётся переписывать кучу файлов HTML. Конечно, большое множество изменений, связанных с оформлением, можно сделать с помощью стилевых файлов. Но, скажем, подключить какой-нибудь javascript или установить тег META с помощью CSS, насколько я в курсе, не получится.

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

Файл template.xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/root">

    <html>
    <title><xsl:value-of select="title"/></title>
    <body>
    <div>Шапка сайта</div>
    <div>
    <a href="/">На главную</a> |
    <a href="first.xml">На первую страницу</a> |
    <a href="second.xml">На вторую страницу</a>
    </div>
    <div><xsl:value-of select="content"/></div>
    </body>
    </html>

  </xsl:template>

<xsl:stylesheet>

Файл index.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<root>
<title>Главная страница</title>
<content/>
</root>

Файл first.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<root>
<title>Первая страница</title>
<content>Содержимое первой страницы</content>
</root>

Файл second.xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<root>
<title>Вторая страница</title>
<content>Содержимое второй страницы</content>
</root>

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

Как оказалось, не все браузеры поддерживают эту технологию. Например, браузер, встроенный в Android, до 4-й версии XML+XSLT не понимал. И, самое главное: сайты, сверстанные с использованием XML+XSLT, крайне неохотно индексируются поисковиками. То есть, боты преобразованием XSLT не заморачиваются. Можно слегка поправить ситуацию, если привести XML-файлы с данными к виду "почти HTML", но это - явный костыль.

Резюмирую. Задача уменьшения количества повторений одного и того же HTML кода по всему сайту в принципе решаема, причем путей её решения может быть несколько. Все они, конечно, требуют дополнительных знаний, но прогрессом, как известно, движет лень, и вышеупомянутые технологии - лишнее тому доказательство.

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

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