Сухой cпособ верстки многостраничных сайтов

в 10:10, , рубрики: DRY, flask, Веб-разработка, верстка, метки: , ,

Сухой cпособ верстки многостраничных сайтов

Я бы хотел, чтобы статья была интересна, а главное, понятна всем, кто занимается версткой. Однако, моя проблема заключается в том, что она лежит на рубеже знаний серверных и клиентских разработчиков. И если первые хоть изредка, но верстают, то вторые, как правило, «на сервер» не заглядывают вообще — в кавычках, потому-что речь не о железе или ОС — нам понадобится кое-какой серверный софт. В общем, я постараюсь подойти к сути вопроса постепенно, чтобы не повторить комикс с рисованием совы. Особо нетерпеливым я просто скажу здесь: я верстаю с помощью Flask. Тех же, кого интересуют детали — прошу под кат.

Сделав достаточно дизайн-макетов, начинаешь замечать как часто отвлекаешься на одни и те же вещи: собрал блок с товаром, склонировал десять раз; опа, рейтинг забыл; удалил все клоны, поправил «первый», снова наделал копий; нужна «скидка», снова удалил, снова склонировал. Или надо все ссылки подчеркнуть едва заметным бордером снизу, а еще добавить закгруления к картинкам и кромку чуть светлее. Вместо того чтобы работать — страдаешь ерундой.

HTML+CSS

С бордером и с тенями спасает css. Поэтому, в скором времени, я стал делать лишь небольшой эскиз в редакторе и приступал к верстке. С применением фреймворков на less/sass так вообще сказка. Сам не зная того, я убил сразу двух зайцев — в верстке начинаешь понимать как макет работает: цвет ховера ссылок может быть нечитабельным, ядовито ярким или резким, форма неудобной или негибкой, информация расположена слишком плотно или текст нельзя читать с комфортом, да и, банальное, шрифт рендерится в браузере по другому — я качественно подтянул свои работы. Ну и верстать потом ничего не нужно: вот она — готовая верстка.

Однако, какая-нибудь мелкая правка в общем хедере ставила вопрос: править во всех файлах или только в одном? С теми же товарами проблема не решена — теперь я клонирую не слои, но хтмл код.

Для решения подобных задач созданы шаблонизаторы.

Flask

Сухой cпособ верстки многостраничных сайтов

Это микро-фреймворк на Python. Я недавно начал его изучение и уж очень он мне понравился, смотрите: минимальное приложение на flask умещается всего в 9 питоновских строчек:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

Я попытаюсь описать хотя бы часть, всей тонны боннусов, которых я получил.

Jinja2

Сухой cпособ верстки многостраничных сайтов

В коробке с Flask идет шаблонизатор Jinja2.

<title>{% block title %}{% endblock %}</title>
<ul>
{% for user in users %}
  <li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>

— мощный и невероятно простой шаблонизатор, более того: он является одним из самых быстрых в мире Python.
— его синтаксис очень близок к джанговскому — если проект на джанго, при определенном опыте, адаптация проходит совершенно незаметно. Что касается остальных шаблонизаторов: нужно будет {% for item in items %} заменить на свой любимый синтаксис. В целом, в джиндже реализованы весьма распространенные и зарекомендовавшие себя приемы, так что порт не займет много времени.
— шаблонизатор обладает рядом преимуществ перед джанговским, например, можно сделать так: for x in foo: bar[x]; есть рекурсивный loop и еще много чего вкусного, но все это выходит за рамки статьи, сейчас нас интересует: {% include %}, {% for %}, {% if %} и {% block %}.

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

Дев-сервер

Во фласке есть встроенный веб-сервер для разработки. Вам не понадобится настраивать nginx, apache, самбу, nfs или еще что-то в этом роде. Просто запускаете его на 0.0.0.0 и тестируйте свою работу в локальной сети на винудоусах, маках, линуксах, планшетах, смартфонах, да хоть на кофеварке.

Хотите дать потестировать другим? Легко: коммитьте, пуште, поднимите дев-сервер в Сети, повесьте pull на крон — и готово. Как только дев-сервер увидит обновления, если в этом будет необходимость, он самостоятельно перезагрузится.

Не так давно, мне удалось поистине оценить эту «фичу» при разработке мобильной версии сайта для одной компании. Сверстав «общее», я быстро развернул дев-сервер и пока доделывал остальные части сайта, мне помогали тестировать уже готовое во множестве браузеров на множестве девайсов. Любой сотрудник мог открыть демку, погонять ее, дать фидбек и если что-то было не понятно, не удобно, не очевидно, вносилось изменение, еще до основного программного кода.

Я называю это «Сайтом Шредингера» — сайта еще нет, но он открывается. Название пришло мне в голову, когда я, однажды, объяснял менеджеру, что это еще не сайт, но, да он открывается в браузере, но нет это еще не сайт, хотя, да, я понимаю, что он открывается. Оно на 30% короче чем «Прототип сайта на Flask» и на 100% меньше содержит латиницы. Это запатентованное название, так что, если у вас есть с этим какие-то проблемы — увидимся в суде.

Статика

Помимо этого flask умеет сервить статику из коробки. Если вы используете, например, less, где в главный файл импортируются подключаемые модули, то наверняка столкнулись с тем, что в хроме локально такой фокус не проходит. Связано это с политикой безопасности браузера. Можно запустить его с дополнительным флагом или использовать веб-сервер — за нас всю работу сделает flask.

Flask — это Python

А это значит, что вы можете пойти дальше, например, сделать дев-базу на sqlite, загнать в нее рыбу и получить более реалистичный протатип. Вы можете добавить чуть больше логики в код, чтобы продемонстрировать какую-то «фичу».
Или сделать сайт целиком.

Пробуем

Структура проекта у нас будет такая:

habr/
├── habr.py
├── templates/
└── static/

В templates Flask будет искать темплейты, static — понятно, для статики, habr.py — наше приложение.

Мы будем верстать примеры из коробки бутстрапа, это те, что лежат в docs/examples. Вообще, это первое что я делаю приступая к верстке: клонирую репу и качаю less.js.

$ mkdir -p habr/templates
$ cd habr
$ touch habr.py
$ git clone git://github.com/twitter/bootstrap.git static
$ wget https://raw.github.com/cloudhead/less.js/master/dist/less-1.3.3.min.js -O static/js/less.js

Забегая вперед, порекомендую скачать и положить рядышком или в виртуалку (venv) вот этот файл:

$ wget https://raw.github.com/mitsuhiko/jinja2-htmlcompress/master/jinja2htmlcompress.py

Само приложение:

#!/usr/bin/env python

from flask import Flask
from flask import render_template
from jinja2.exceptions import TemplateNotFound
app = Flask(__name__)
app.jinja_env.add_extension('jinja2htmlcompress.SelectiveHTMLCompress')

@app.route('/')
@app.route('/<page>/')
def any_page(page='home'):
    try:
        return render_template(page + '.html')
    except TemplateNotFound:
        return render_template('404.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

По порядку:
— импортируем нужные модули
— создаем приложение
— подключаем расширение к шаблонизатору (потом покажу зачем)
— единственная функция обслуживает все страницы сайта. Т.е. по адресу /login/ будет осуществлен поиск файла login.html, для главной страницы — home.html. Если запрошенный темплейт не будет найден, вернется 404 страница — т. е. я предполагаю, что 404.html вы сделаете сразу, если ее нет — ничего страшного — просто вывалится ошибка.
— запуская сервер по адресу 0.0.0.0 мы делаем его доступным локально и в сети, т.е. на 127.0.0.1 и например, по адресу 192.168.0.100. По-умолчанию сервер доступен на 5000 порту.

Сделаем файл для удобства исполняемым.

$ chmod +x habr.py

Все, готово.

Верстаем

Если вы знакомы с шаблонизаторами и, уж тем более, работаете с django/flask, скорее всего, здесь статья для вас заканчивается. Для остальных я покажу решение вышеперечисленных вопросов.

Сверстаем промо-страницу из бутстрапа:

Сухой cпособ верстки многостраничных сайтов

{% block %}

Начнем с того, что создадим базовый шаблон templates/base.html, который будет содержать основную структуру верстки

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Template · Bootstrap</title>
        <link rel="stylesheet/less" href="/static/less/bootstrap.less">
        <script src="/static/js/less.js"></script>
    </head>

    <body>
        {% block body %}{% endblock %}
    </body>
</html>

Тег {% block %} — это что-то вроде окна, через которое, дочерние файлы смогут выводить свое содержимое — «расширять родителя». Если блок из другого файла не вызывается, то используется содержимое родителя. Кроме того, блоки могут быть вложенными — чем выше в DOM-e блок, тем больше дочерний файл сможет заменить в основном шаблоне:

<body>
    {% block body %}
        <div class="container">
            {% block content %}{% endblock %}
        </div>
    {% endblock %}
</body>
{% include %}

Удобно, чтобы повторяющийся код не болтался под ногами. Сохраним код футера и хеадера в отдельные файлы footer.html и header.html соответственно. Импортируем их в основной шаблон.

{% block body %}
    <div class="container">
        {% include "header.html" %}
        {% block content %}{% endblock %}
        {% include footer.html %}
    </div>
{% endblock %}

Теперь можно править их отдельно, без 120 уровней вложенности тегов.

<hr>
<div class="footer">
    <p>© Company 2012</p>
</div>
{% if %}

Представим, что нам нужно потестировать верстку на различных устройствах. 20 файлов less и less.js порвут любой смартфон на тряпки. Поэтому вам может понравиться вот это:

{% if True %}
    <link rel="stylesheet/less" href="/static/less/bootstrap.less">
    <script src="/static/js/less.js"></script>
{% else %}
    <link rel="stylesheet" href="/static/css/bootstrap.css">
{% endif %}

Перед тем как выложить верстку на сервер, меняем True на False и будет подключен css файл. Правда круто? Еще б.

Да, знающие люди скажут, что Flask добавляет в окружение переменную config, в которую можно запихать наш флаг. И используя импортируемые настройки, можно создать локальный файл настроек на сервере, где фласк автоматически переключит тумблер less/css. По типу {% if config.get(«USE_LESS») %}.

Подробнее тут:
flask.pocoo.org/docs/config/
flask.pocoo.org/docs/templating/

{% extends %}

Создадим файл templates/home.html. Это, как говорилось выше, шаблон главной страницы (вот это поворот!). Она выглядит как файл marketing-narrow.html в бутстрапе:

{% extends "base.html" %}
{% block content %}
    <div class="jumbotron">
        <h1>Marketing stuff!</h1>
…
{% endblock %}

При обращению к файлу, шаблонизатор найдет тег {% extends %}, затем он будет искать у указанного родителя блок «content», чтобы заменить его содержимое содержимым в файле home.html.

Тем самым… Уф, ну понятно же? Едем дальше.

{% for %}

В файле examples/marketing-narrow.html есть повторяющиеся блоки. Давайте напечатаем их в цикле:

<div class="row-fluid">
    {% for i in range(3) %}
        <div class="span4">
            <h2>Heading</h2>
            <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
            <p><a class="btn" href="#">View details »</a></p>
        </div>
    {% endfor %}
</div>

… и будет три одинаковых блока. А вот и нет :)

<div class="span4">
    <h2>{{ lipsum(n=1, html=False, min=1, max=3) }}</h2>
    <p>{{ lipsum(n=1, html=False, min=20, max=30) }}</p>
    <p><a class="btn" href="#">View details »</a></p>
</div>

Встроенная функция lipsum сгенерит уникальный текст для каждого из них!

Давайте, представим, что это инлайн-блоки и нам не нужны лишние пробелы.

{% for i in range(3) -%}
…
{%- endfor %}

Вот эти милые "-" палочки удалят лишнее между тегами (http://jinja.pocoo.org/docs/templates/#whitespace-control). Еще, как вариант, можно использовать «font-size:0» для контейнера, но нет пробелов — нет проблем.

Ах да. Пусть эти блоки выводятся еще где-то. Ну не клонировать же их, в самом деле.

{% for i in range(3) %}
        {% include "news.html" %}
{% endfor %}
login.html

Давайте для закрепления сверстаем еще форму авторизации.

Сухой cпособ верстки многостраничных сайтов

{% extends "base.html" %}
{% block body %}
    <div class="container">
        <form class="form-signin">
        ...
        </form>
    </div>
{% endblock %}

Обратите внимание на {% block body %} — используя блок верхнего уровня, мы удалим со страницы футер и хеадер, но все остальное останется.

{% strip %}

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

{% strip %}
    <head>
        <meta charset="utf-8">
        <title>Template · Bootstrap</title>
        {% if True %}
            <link rel="stylesheet/less" href="/static/less/bootstrap.less">
            <script src="/static/js/less.js"></script>
        {% else %}
            <link rel="stylesheet" href="/static/css/bootstrap.css">
        {% endif %}
    </head>
{% endstrip %}

Strip также полезен, если вам надо избавиться от пробелов в инлайн-блоках, если они, скажем, забиваются вручную или вы используете {% include %} в цикле, потому-как знак минуса в таком случае удалит только пробелы в начале и в конце импортируемого файла, но никак не пробелы между импортами.

Немного о плохом

— лучше сразу указать адрес фавикона чтобы он смотрел в статик: /static/whatever.ico — браузер запросит /favicon.ico и flask посчитает его обычной страницей. Он начнет сыпать ошибки в консоль об отсутствии темплейта favicon.ico.html. Можно сделать условие в урле приложения, но, поверьте, проще так.
— если в less указать бекграунду изображение, например, background-image: url("whoever.png");, а затем изменить изображение, сохранив название, то потом его очень тяжело выкурить из кеша — просто добейте в конец названия "?22323" url("whoever.png?23232"), где цифры — рандомное число, чтобы заставить браузер перезагрузить изображение. С этим я столкнулся только пол года спустя, т.к. для превью я использую тупо серый фон, а элементы дизайн макета меняются ну очень редко, а то и вовсе используют css3 — с этим никаких проблем.
— хоть я и не могу представить себе программиста, который предпочтет чистый html взамен готовым шаблонам, такая вероятность все же есть. В таком случае я, наверное, порекомендовал бы настроить паука скачивающего сайты, типа FreeDownloadManager или wget, и за 10 секунд получить весь хтмл.

На этом описание метода заканчивается

Всех, кого интересует как это все добро установить — идем дальше.

Установка всего этого добра

Мы создадим виртуальную среду, куда установим все наши пакеты. Она необязательна, но лучше приучиться сразу к хорошему. Виртуальная среда — это по сути директория с софтом, по привычке я положу проект туда же, т.е. так:

habr/ — виртуальная среда
├── bin/
├── lib/
…
└── habr/ — наш проект
    ├── habr.py
     …

habr/habr/habr.py — бррр.

Ubuntu

Для прочих дистров меняем aptitude на свой пакетный менеджер.

$ sudo aptitude install python-setuptools
$ sudo easy_install pip
$ sudo pip install virtualenv
$ virtualenv --no-site-packages habr
$ cd habr
$ source bin/activate
Windows

Качаем пакеты python, python-easyinstall, ставим. Дальше жмем Super + Pause (super — это с флажком :), для XP переходим во вкладку «Дополнительно», для Vista/Seven жмем по ссыле «Дополнительные параметры». Жмем на кнопку «Параметры среды», добавляем переменную PATH со значением «C:Python27;C:Python27Scripts;C:Python26Libsite-packages;». Жмем окей.
Дальше жмем Ctrl+R, набирем cmd, жмем энтер, пишем:

easy_install pip # если будет ругаться на права, возможно надо запустить консоль с правами админа 
pip install virtualenv 
virtualenv --no-site-packages habr
cd habr
scriptspip.exe install Flask
# Теперь к своему приложению вы будете обращаться, например, так:
d:Developmenthabrscriptspython habrhabr.py
В заключение

Скажу, что Flask, конкретно к методу, не имеет отношения. Просто мне он подходит по многим причинам: приложение пишется по памяти в несколько строк, шаблоны почти джанговские, но гибче и самое главное — это python. Используйте то, что для вас удобнее. Хотя, возможно это будет фласк — в таком случае я попал в точку.
Скачать примеры из статьи, zip, 73 KB.
Вот и все. Спасибо за внимание.

Автор: magic4x

Источник

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


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