Блочная верстка с наследованием - что это такое
Собственно, про блочну верстку я писал уже не раз, и даже как-то приводил ее пример в этом топике: livestreet.ru/blog/wishlist/13103.html (и мне кажется, что имеет смысл прочитать топик по ссылке, прежде чем читать этот; хотя и не обязательно — можно и после этого).
Но то ли пример там слишком сложный, то ли за один раз не получается все объяснить. Поэтому я решил еще один топик запостить с простыми и понятными (как мне кажется) примерами.
Вот, в упрощенном виде, типовой макет страницы LS-сайта:
И, предположим, мы решили сделать новую верстку, чтобы итоговый HTML-код выглядел примерно так:
Давайте разберем, как это реализуется с помощью нынешнего подхода к построению LS-шаблонов, и как я это предлагаю делать.
header.tpl
Теперь, мы хотим создать, допустим, три шаблона – для главной страницы (index.tpl), для списка топиков (topics.tpl) и для списка блогов (blogs.tpl). И мы делаем это так – для каждого шаблона пишем ту часть, которую хотим воткнуть секцию b-content, а потом собираем окончательные шаблоны по кусочкам:
index.tpl
topics.tpl
blogs.tpl
index.tpl
topics.tpl
blogs.tpl
Т.е. Smarty при компиляции шаблона topics.tpl возьмет шаблон index.tpl и заменит в нем блоки, которые были переопределены (в нашем случае блок b-content). Если вдруг нам надо не заменить блок, а только добавить к блоку дополнительное содержимое, то в наследуемом шаблоне нужно использовать в директиве {block} ключевые слова append или prepend:
Тогда содержимое будет добавляться в конец или в начала блока, не трогая контента родителя.
Возможно, кто-то помнит, как я говорил в предыдущих обсуждениях, что блок заменяет два хука, так вот – это оно то самое и есть.
И, конечно, наследование может быть сколь угодно глубоким (т.е. у конечного шаблона может быть много предков), и блоки могут быть вложены друг в друга.
Когда используется «блочная» верстка с наследованием, то при компиляции Smarty собирает из всей цепочки один единственный итоговый шаблон. И именно этот единственный скомпилированный шаблон использует при рендеринге страницы.
Разумеется, не стоит предаваться излишнему фанатизму и навсегда отказываться от использования директивы {include}. Но, сдается мне, вполне реально уменьшить число инклудов на порядок. А это значит, что можно уменьшить число файловых операций при рендеринге страницы примерно в 10 раз.
Из самых серьезных минусов я пока вижу только то, что это несколько непривычно, и надо иначе настроить свои мозги при разработке шаблона с «блочной» версткой. Возможно, еще какие-то минусы есть, которые я не вижу. Если кто-то увидит – дайте знать.
Но то ли пример там слишком сложный, то ли за один раз не получается все объяснить. Поэтому я решил еще один топик запостить с простыми и понятными (как мне кажется) примерами.
Вот, в упрощенном виде, типовой макет страницы LS-сайта:
И, предположим, мы решили сделать новую верстку, чтобы итоговый HTML-код выглядел примерно так:
<body>
<section class="b-view">
<header class="b-view-header">
</header>
<section class="b-view-middle">
<section class="b-content">
</section>
<section class="b-sidebar">
</section>
</section>
<footer class="b-view-footer">
</footer>
</section>
</body>
Давайте разберем, как это реализуется с помощью нынешнего подхода к построению LS-шаблонов, и как я это предлагаю делать.
«Кусочная» (или «ленточная») верстка
Как это делается в нынешней парадигме LS-верстки (я называю ее «кусочной» или «ленточной»). Мы разбиваем HTML-код на части («кусочки»), которые потом пихаем в разные шаблоны. Как минимум, мы делаем из макета два «кусочка»:header.tpl
<body>
<section class="b-view">
<header class="b-view-header">
</header>
<section class="b-view-middle">
<section class="b-content">
Footer.tpl
</section>
<section class="b-sidebar">
</section>
</section>
<footer class="b-view-footer">
</footer>
</section>
</body>
Теперь, мы хотим создать, допустим, три шаблона – для главной страницы (index.tpl), для списка топиков (topics.tpl) и для списка блогов (blogs.tpl). И мы делаем это так – для каждого шаблона пишем ту часть, которую хотим воткнуть секцию b-content, а потом собираем окончательные шаблоны по кусочкам:
index.tpl
{include file=header.tpl}
{include file=index-list.tpl}
{include file=footer.tpl}
topics.tpl
{include file=header.tpl}
{include file=topics-list.tpl}
{include file=footer.tpl}
blogs.tpl
{include file=header.tpl}
{include file=blogs-list.tpl}
{include file=footer.tpl}
«Блочная» верстка
А вот как то же самое можно сделать в «блочной» верстке с наследованием.index.tpl
<body>
<section class="b-view">
<header class="b-view-header">
</header>
<section class="b-view-middle">
<section class="b-content">
{block name="b-content"}
<!—здесь контент для домашней страницы -->
{/block}
</section>
<section class="b-sidebar">
</section>
</section>
<footer class="b-view-footer">
</footer>
</section>
</body>
topics.tpl
{extends file=index.tpl}
{block name="b-content"}
<!—здесь контент для страницы topics -->
{/block}
blogs.tpl
{extends file=index.tpl}
{block name="b-content"}
<!—здесь контент для страницы blogs -->
{/block}
Т.е. Smarty при компиляции шаблона topics.tpl возьмет шаблон index.tpl и заменит в нем блоки, которые были переопределены (в нашем случае блок b-content). Если вдруг нам надо не заменить блок, а только добавить к блоку дополнительное содержимое, то в наследуемом шаблоне нужно использовать в директиве {block} ключевые слова append или prepend:
{block name="b-content" append}
{block name="b-content" prepend}
Тогда содержимое будет добавляться в конец или в начала блока, не трогая контента родителя.
Возможно, кто-то помнит, как я говорил в предыдущих обсуждениях, что блок заменяет два хука, так вот – это оно то самое и есть.
И, конечно, наследование может быть сколь угодно глубоким (т.е. у конечного шаблона может быть много предков), и блоки могут быть вложены друг в друга.
Плюсы и минусы «блочной» верстки
Самый большой плюс в том, что уменьшается число файлов, которые подгружаются движком при отображении уже скомпилированных шаблонов. Дело в том, что директивы Smarty {include} в процессе компиляции шаблонов преобразуются в PHP-директивы include. Т.е. компилируется каждый шаблон по отдельности, и во время рендеринга страницы все шаблоны считываются и подключаются. Но ведь это в нашем примитивном примере всего три кусочка подключаются, в реальности их несколько десятков (в среднем – 35-40), и все они считываются при каждом отображении страницы каждому посетителю сайта.Когда используется «блочная» верстка с наследованием, то при компиляции Smarty собирает из всей цепочки один единственный итоговый шаблон. И именно этот единственный скомпилированный шаблон использует при рендеринге страницы.
Разумеется, не стоит предаваться излишнему фанатизму и навсегда отказываться от использования директивы {include}. Но, сдается мне, вполне реально уменьшить число инклудов на порядок. А это значит, что можно уменьшить число файловых операций при рендеринге страницы примерно в 10 раз.
Из самых серьезных минусов я пока вижу только то, что это несколько непривычно, и надо иначе настроить свои мозги при разработке шаблона с «блочной» версткой. Возможно, еще какие-то минусы есть, которые я не вижу. Если кто-то увидит – дайте знать.
41 комментарий
В идеале(на примере опыта работы с другими фреймворками):
1. Наследоваться от index.php по умолчанию на уровне php, без явного прописывания в каждом tpl файле и возможностью горячей смены как раз таки указыванием.
2. Опустить использование
и по умолчанию то что лежит вне блоков относить к блоку b-content(или просто content), а что бы рассовать данные по другим блокам использовать запись то что указана выше, но с нужным именем блока.
3. Данные штуки с наследованиями позволяют выбирать общую структуру верстки и например для страницы логирования или регистрации(вспоминаем header.lite.tpl) она может коренным образом отличаться. Поэтому index.php стоит вынести в папку layouts, а там уж при желании создавать сколько угодно вариантов скелетов.
Я уверен что придеться танцевать с бубном и делать хаки для реализации подобных вещей, но они упращают код, делают его более минималистичным. Просто желание :)
Тут используется разметка именуемая haml поэтому опускаем html код и видим
Эта запись аналогична
Который в свою очередь будет вставлен в layouts/application.html.haml, в нашем варианте в блок располагаемом в хэде.
Все что определено в файле(у нас это шаблон экшена) и находиться вне content_for будет передано и вставлено сюда(в лэйот), у нас если мы не имеем возможности создавать безымянные блоки можно по умолчанию согласовать и использовать имя content.
Это описания того о чем я говорил и один из примеров реализации этой схемы в рельсах которая успешно используется разработчиками. Конечно подключение скриптов в хэд нельзя назвать хорошим примером, но можно прогуляться по другим лэйотам и увидеть что он находит применение в различных частях макета
Безымянные блоки в Smarty не предусмотрены, поэтому блоки нужно объявлять явно, как в шаблонах-родителях, так и в шаблонах-потомках.
Что касается названий самих блоков в моих примерах, то они тоже не «от балды» даны, это влияние методологии БЭМ, о которой рассуждали до этого. Поэтому, если говорить шире, я постарался совместить тут два понятия «блоки» — и от БЭМ, и от Smarty. Т.е. это термин «двойного назначения», и класс БЭМ-блока совпадает с именем Smarty-блока:
Здесь <section class=«b-content»> — это для CSS-классов,
а {block name=«b-content»} — для наследования шаблонов.
Пример в топике сильно упрощен. А на деле практически все теги типа <section class=«b-...»> должны внутри себя содержать Smarty-блоки {block name=«b-...»}, где класс секции и имя блока совпадают
не будет ли это слегка утомлять, да будет выйгрыш в скорости, но по сути красивее станет только главный лэйот, ну и динамичность возрастет — можно будет прям в экшене изменять другие части шаблона.
Если реализуем 1-ый пункт то на большинство tpl файлов будет приходиться такой код
И это уже хорошо, но еще лучше не обрамлять наш html код если мы и так знаем что нам нужно его поместить в контент и получить следующий вариант
Но при всем при этом у нас сохраняеться возможность наследования, возможность использовать блоки и при необходимости код будет выглядеть так:
Я еще раз повторюсь это в идеале, просто мысли, реализовывать их не обязательно, но если есть возможность то так должно быть удобнее. Будет меньше повторяемого кода.
И вот жесткой привязки нет. Напр., в наследуемом шаблоне можно и так прописать:
и так:
Работать будет одинаково, т.к. порядок блоков тут не играет значения, они будут вставляться в родительском шаблоне туда, где изначально объявлены.
Это уже мое предложение — сделать стандартом жесткую связку HTML-блоков и Smarty-блоков в изначальном макете.
В любом случае уже будет выйгрыш, так что если будет голосование я за вашу идею!
думаю что нужно проголосовать каким-то образом за идею, честно подразумевая, что:
плюсами будет:
попрошу дополнить + и -
Что то я вас не понял, это заложено в Smarty, то есть технология будет работать и старая и новая, потому что для Smarty, и та и та не является старой или новой, просто это две схемы работы, которые были придуманы давным давно, так что все будет работать, то есть и старые шаблоны и такие новые.
я не прав? поправьте :)
вы правы на счет технологий. для вас, как верстальщика шаблонов в целом все верно. Но для разработчиков плагинов, это означает что для шаблона по старой технологии нужно будет добавлять свою страницу по старому методу через инклуды шапки, с заданием меню, футера. По новой же технологиии нужно будет писать:
по мне так сомнительная паника :) мне кажется должно все работать.
То есть смешение технологий, не должно повредить процессу… но все покажут тесты :) как без них.
нужны факты, доказательства, тесты :)
я вот не верю в сказки, да и знаю товарища орта, по многи ситуациям, оно и понятно, не всегда есть время, даже если это реальная штука…
2. я советую изменить структуру шаблона ветки комментариев… я вот напишу скоро про это топик, как я топик с >1000 комментами оптимизировал в 2 раза, не трогая php код вообще, а благодаря только html, надеюсь это внесут в версию :) посмотри, посмотрим…
возможно тогда 102-я станет одной из самых быстрых, безопасных и надежных версий!
Есть реализация этого механизма?
конкретно вот этот момент: как задать эту цепочку?
я с такой непоследовательностью работаю давным давно, только не в LS, а проектах на Питоне, ребята с которыми я работаю все только блочный тип и юзают, но не отказываясь от инклуд.
вобщем я попробую сделать пример одой странице на основе шаблона bootstrap =) посмотрим что получится.
Если что буду спрашивать, как и договорились :)
Чтоб проверить все это на практике, нужно сделать две верстки — «кусочную» и «блочную» — одного и того же дизайна. Причем, не просто тест, а реальную верстку, чтоб по полной все отработало в обоих случаях. Только так проверить можно. Но захочет ли кто-то на это угрохать кучу времени? ;)
Но тут еще есть плюсы, правда, не такие очевидные: напр., будет, как минимум, один файл (макет), в котором видна вся HTML-структура страницы. И все шаблоны будут иметь правильную DOM-структуру, т.е. не будет такого, что какой-то тег открывается в одном шаблоне, а закрывается в другом, что нередко нехилого гемора добавляет.
и проведем опыты :) буду рад если будите помогать. Своих ребят я уже попросил помочь, которые на Питоне фигачат.
Я сейчас пытаюсь структуризировать для себя работу с оформлением, css и прочим для макетов и плагинов.
Также хотелось бы указать на тот факт, что в примерах содержатся семантические ошибки (каскад section, footer внутри section и т. д.) а также необоснованно смешиваются логические и функциональные блоки. Возможно, последнее является следствием тонкой границы между блочной вёрсткой (логическая группировка элементов в уникальные группы, пример — #head на главной Яндекса) и вёрсткой независимыми блоками (функциональная группировка элементов для повторного использования в любом контексте, как на картинке ниже).
Из соглашения по именованию классов я могу заключить, что автор предлагает использовать методологию БЭМ, которая в своём применении вне Яндекса требует особой осторожности. Она была разработана как часть законченного производственного процесса, включающего в себя средства сборки (борщик), оптимизации (имго/цссо) и развёртывания, которые при всём желании за уши в лайвстрит не притащишь. Описанная путаница, вытекающие из неё некорректные упрощения и inhouse-ориентированность БЭМа могут только сбить с толку верстальщика, рассматривающего переход на наследуемые шаблоны.
Поскольку этот пост предназначен для верстальщиков, то я бы предложил поправить примеры, чтобы ориентировать их на хорошие практики. К сожалению, все шаблоны лс, которые я видел, показывают, что их авторы не в полной мере знакомы с HTML и эту ситуацию следует исправлять.
Я бы предложил избегать одинаковых имён в логических и функциональных блоках, что соответствует упомянутой выше методологии. Напомню также, что блок в синтаксисе смарти можно записать лаконичнее:
Насчет миграции — согласен, это потребует усилий, и, возможно, не очень маленьких. Надо все взвесить и определиться — стоит игра свеч или нет. ИМХО, да, стоит.
По семантике спорить не буду, охотно верю, что в этом плане мой пример далек от идеала. Тем более, что действительно — грань между логическими и функциональными блоками весьма зыбкая. Внедрять в полной мере методологию и технологии БЭМ для шаблонов ЛС, думаю, вряд ли целесообразно. Но в целом в предлагаемом подходе влияние БЭМ, конечно есть. В т.ч. и в именовании классов.
Кстати, как раз для того, чтобы не сбивать с толку верстальщика, я и предлагаю одинаковые имена в блоках. Понятно, что далеко не всегда логическому блоку обязательно должен ставиться в соответствие блок функциональный. Но, на мой взгляд, функциональному блоку всегда можно поставить в соответствие блок логический. Разве нет? И в этом случае однозначное сопоставление имен предпочтительней, ИМХО. Так врестальщику будет проще.
Т.е., проще говоря, я предлагаю Smarty-блоки оборачивать в теги с одноименными классами (за исключением блоков внутри тега head, разумеется). А при именовании классов опираться на БЭМ. И, что крайне важно, предлагаю сформулировать не только правила именования классов, но и определить несколько десятков стандартных классов, обязательных к использованию.
Разумеется, все сказанное — ИМХО, не претендующее на истину в последней инстанции. Тем более, что я все ж больше программер, а не верстальщик. Поэтому показать верстальщикам хорошие практики я вряд ли смогу. Лучше будет, если какой-нибудь опытный верстальщик предложит альтернативный подход, более грамотный и целостный, либо разовьет мои предложения, исключив из них ошибки и неточности. Рад буду обсудить.
Преимущества, которые даёт повсеместное внедрение наследования и независимых блоков, прежде всего почувствуют коллективы разработчиков, создающие сайты на основе лайвстрита, а для разработчиков шаблонов и энтузиастов-одиночки, использующих движок на своих сайтах значительной разницы не будет. Кроме, конечно, дополнительных финансовых вложений и затрат времени, которые прямой выгоды им не принесут. На мой взгляд, такие радикальные перемены в зрелом продукте, к которому уже можно отнести лайвстрит, вряд ли будут уместны.
Я также убеждён, что наследование не сможет сыграть роль фактора, который привлечёт дополнительный интерес к движку со стороны верстальщиков. Инклюды понятны и логичны для начинающих, а для более квалифицированных специалистов, повседневно применяющих обсуждаемые методологии, нужна несколько иная мотивация, которой в лайвстрите нет и, возможно, никогда не будет.
Разработчики шаблонов для лайвстрит исторически всегда использовали в качестве основы дефолтный шаблон, внося в него лишь косметические изменения разной степени. Очевидно, что люди, сосредоточенные прежде всего на работе с css, вряд ли станут широко использовать наследование и методологию БЭМ (правильное понимание которой составляет отдельную проблему и требует определённой квалификации).
Если я правильно тебя понял, твоё предложение направлено на популяризацию движка. Я считаю, что она должна реализовываться совершенно иными средствами, которые уже многократно обсуждались. Массовое распространение технологически примитивных конкурирующих продуктов может только подтверждить моё мнение.