Как стать автором
Обновить

Что делать, если брать фронтенд-фреймворк – это излишество

Время на прочтение 9 мин
Количество просмотров 8.2K

Пример @@include


Современные фронтенд-фреймворки дают удивительные возможности. React, Vue, Angular и другие созданы делать то, что раньше было невозможно, – веб-приложения. В 2020 скачивать и устанавливать приложения уже необязательно. Зачем, если всё можно сделать на сайте?


Однако с большой силой приходит большое количество зависимостей. И находятся энтузиасты, которые решают, что поднять большой фреймворк для своего небольшого проекта, – не такая уж плохая идея. Даже если это просто лендинг.


В этом вопросе я поддерживаю "консерваторов". Не нужно писать лендинги и многостранички на Create-React-App, для этого пойдет и обычная статика.


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


Что делать? Писать простыню HTML-разметки в одном файле? Хранить данные во view? Это не сделать шаг назад, это сорваться и упасть в пропасть. Это не просто неудобно, это идет вразрез с современной парадигмой фронтенд-разработки.


Во-первых, data-driven. Данные во главе угла, а внешний вид – лишь их отображение. Пользователь делает действие, данные меняются, вслед меняется отображение. Так работают все современные фреймворки.


Еще один элемент современного подхода – компонентность. Делим приложение на мелкие самодостаточные переиспользуемые куски. Больше переиспользуемости – меньше кода. Меньше кода – меньше багов.


До этого мы уже обсуждали, как реализовать data-driven минимальными усилиями. Мой выбор – Alpine.js. Что же делать с компонентностью? Для простых статических сайтов я предлагаю самый простой вариант – gulp-file-include.


Столько говорил о современности и парадигмах, а закончилось всё библиотекой, которой уже лет 100? Ну, на самом деле, она не такая уж и старая. 4 года с версии 1.0.0, столько же, сколько первой стабильной версии React (15). Да и зачем заново изобретать велосипед, если уже всего готово.


У библиотеки всего шестьсот звезд на Github и 6,5 тысяч скачиваний в неделю на npm, но она дает нам всё, что нужно, чтобы быстро разделить простыню HTML на небольшие удобные компоненты. И при этом не добавив ни байта дополнительного кода в конечный результат.


Единственная преграда – знакомство с Gulp. Ну, на самом деле, писать даже простой лендинг без подобного инструмента сегодня – не очень правильно. Поэтому, если вы не знаете Gulp, узнайте. Вот для этого статья на Хабре, вот ссылка на перевод документации. Мы на этом останавливаться не будем.


Если вы хотите не прерываться, а продолжить со мной, вот ссылка на мой пресет на Github, с которым мы будем работать, там же вы можете найти мой gulpfile.


Итак, что будем делать? Вот это


Что мы будем делать


Выглядит просто. Посмотрим, как это выглядит в HTML.


Как это выглядит в HTML
<section class="text-gray-700 body-font">
  <div class="container px-5 py-24 mx-auto">
    <h1 class="mb-20 text-2xl font-medium text-center text-gray-900 sm:text-3xl title-font">
      Проблемы, которые я решаю
    </h1>
    <div class="flex flex-wrap -mx-4 -mt-4 -mb-10 sm:-m-4 md:mb-10">
      <div
        class="flex flex-col items-center p-4 mb-6 sm:flex-row lg:w-1/3 md:mb-0 sm:items-stretch"
      >
        <div
          class="inline-flex items-center justify-center flex-shrink-0 w-12 h-12 mb-4 text-indigo-500 bg-indigo-100 rounded-full"
        >
          <svg
            class="w-6 h-6"
            fill="none"
            stroke="currentColor"
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            viewBox="0 0 24 24"
          >
            <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
          </svg>
        </div>
        <div class="flex-grow pl-6">
          <h2 class="mb-2 text-xl font-medium text-gray-900 title-font">Оптимизация скорости</h2>
          <p class="text-lg leading-relaxed">
            Увеличим быстродействие системы при загрузке, уменьшим нагрузку на процессор и оперативную память, исключим из автозагрузки требовательные к ресурсам устройства программы.
          </p>
        </div>
      </div>
      <div
        class="flex flex-col items-center p-4 mb-6 lg:w-1/3 md:mb-0 sm:flex-row sm:items-stretch"
      >
        <div
          class="inline-flex items-center justify-center flex-shrink-0 w-12 h-12 mb-4 text-indigo-500 bg-indigo-100 rounded-full"
        >
          <svg
            class="w-6 h-6"
            fill="none"
            stroke="currentColor"
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            viewBox="0 0 24 24"
          >
            <circle cx="6" cy="6" r="3"></circle>
            <circle cx="6" cy="18" r="3"></circle>
            <path d="M20 4L8.12 15.88M14.47 14.48L20 20M8.12 8.12L12 12"></path>
          </svg>
        </div>
        <div class="flex-grow pl-6">
          <h2 class="mb-2 text-xl font-medium text-gray-900 title-font">
            Восстановление системных файлов
          </h2>
          <p class="text-lg leading-relaxed">
            В случае некорректной работы системы и устройств, проведём анализ системных файлов и восстановим их, если они повреждены.
          </p>
        </div>
      </div>
      <div
        class="flex flex-col items-center p-4 mb-6 lg:w-1/3 md:mb-0 sm:flex-row sm:items-stretch"
      >
        <div
          class="inline-flex items-center justify-center flex-shrink-0 w-12 h-12 mb-4 text-indigo-500 bg-indigo-100 rounded-full"
        >
          <svg
            class="w-6 h-6"
            fill="none"
            stroke="currentColor"
            stroke-linecap="round"
            stroke-linejoin="round"
            stroke-width="2"
            viewBox="0 0 24 24"
          >
            <path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"></path>
            <circle cx="12" cy="7" r="4"></circle>
          </svg>
        </div>
        <div class="flex-grow pl-6">
          <h2 class="mb-2 text-xl font-medium text-gray-900 title-font">
            Установка и обновление драйверов устройств
          </h2>
          <p class="text-lg leading-relaxed">
            При неработоспособности какого-либо из устройств или проблемах, связанных с их некорректной работой, произведём установку, обновление или откат драйверов.
          </p>
        </div>
      </div>
    </div>
  </div>
</section>

Воу! Не так уж и просто, как хотелось бы. Безусловно, TailwindCSS, который здесь используется, тоже даёт о себе знать. Если бы мне пришлось разбираться в этом коде, я бы первым дизлайкнул статью, заявляющую, что TailwindCSS – это новый шаг эволюции. Но нет, я её написал. А всё потому, что этот код можно красиво поделить на компоненты, где эта гора классов превратится в осмысленную картину, которая не только ухудшит, а даже улучшить наш developer-experience.


Но вернемся к плагину. Начнем с небольшой теории. Чтобы с помощью gulp-file-include вставить один HTML в другой, нам нужно написать команду @@include(<путь до файла>, <объект с переменными>).


При этом в gulpfile можно многое настроить под себя. У меня настроено примерно так:


function html() {
  return src('src/*.html')
    .pipe(fileinclude({ basepath: './src/partials' }))
    .pipe(dest('dist'));
}

Берем все HTML-файлы из src, прогоняем через наш плагин и кладем в папку dist. При этом в настройки плагина можно передать ряд опций. Быстро пройдемся по ним.


  • prefix – можно изменить префикс с @@ на любой другой.
  • suffix – можно добавить суффикс.
  • basepath – можно настроить, как просчитываются пути в директивах. По умолчанию '@file' – от HTML-файла. Есть еще '@root' – от корня, или любой другой путь. В нашем случае, я создал специальную папку в src partials, где и будут лежать все наши компоненты. Важно правильно настроить Gulp, чтобы эти компоненты не попали в окончальную сборку. В примере выше, Gulp берет только файлы из корня src, не заглядывая в папки. Это то, что нам нужно.
  • filters – позволяет задавать функции, которые потом можно будет запускать из разметки. Смотрите примеры в документации.
  • context – "глобальные" переменные для условий @@if.

Также есть ряд директив:


  • @@include – вставка HTML-файла в другой HTML.
  • @@if – условия; переменные берутся из "глобального" context и/или из объекта переменных использованного @@include.
  • @@for – обычный цикл по массиву из context/переменных @@include.
  • @@loop – проходимся по массиву объектов, используя данные из них как переменные, и вставляем для каждого компонент. Может использовать JSON.

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


В нашем случае нас больше всего интересует @@loop. Мы перенесем все данные из карточек в JSON-файл, создадим компонент и передадим ему его данные, как пропсы.


Что у нас входит в данные? Правильный ответ: заголовок карточки, текст и SVG. В переменные мы можем передавать как просто текст, так и HTML как строку. Он просто подставит её, куда мы скажем.


Вот как выглядит JSON (data.json).


[
  {
    "title": "Оптимизация скорости",
    "text": "Увеличим быстродействие системы при загрузке, уменьшим нагрузку на процессор и оперативную память, исключим из автозагрузки требовательные к ресурсам устройства программы.",
    "svg": "<path d=\"M22 12h-4l-3 9L9 3l-3 9H2\"></path>"
  },
  {
    "title": "Восстановление системных файлов",
    "text": "В случае некорректной работы системы и устройств, проведём анализ системных файлов и восстановим их, если они повреждены.",
    "svg": "<circle cx=\"6\" cy=\"6\" r=\"3\"></circle><circle cx=\"6\" cy=\"18\" r=\"3\"></circle><path d=\"M20 4L8.12 15.88M14.47 14.48L20 20M8.12 8.12L12 12\"></path>"
  },
  {
    "title": "Установка и обновление драйверов устройств",
    "text": "При неработоспособности какого-либо из устройств или проблемах, связанных с их некорректной работой, произведём установку, обновление или откат драйверов.",
    "svg": "<path d=\"M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2\"></path><circle cx=\"12\" cy=\"7\" r=\"4\"></circle>"
  }
]

Теперь создадим компонент одной карточки (card.html). Переменные вставляем как @@<имя переменной>.


<div
  class="flex flex-col items-center p-4 mb-6 sm:flex-row lg:w-1/3 md:mb-10 sm:items-stretch"
>
  <div
    class="inline-flex items-center justify-center flex-shrink-0 w-12 h-12 mb-4 text-indigo-500 bg-indigo-100 rounded-full"
  >
    <svg
      class="w-6 h-6"
      fill="none"
      stroke="currentColor"
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="2"
      viewBox="0 0 24 24"
    >
      @@svg
    </svg>
  </div>
  <div class="flex-grow pl-6">
    <h2 class="mb-2 text-xl font-medium text-gray-900 title-font">@@title</h2>
    <p class="text-lg leading-relaxed">@@text</p>
  </div>
</div>

Осталось только создать файл секции (index.html).


<section class="text-gray-700 body-font">
  <div class="container px-5 py-24 mx-auto">
    <h1 class="mb-20 text-2xl font-medium text-center text-gray-900 sm:text-3xl title-font">
      Проблемы, которые я решаю
    </h1>
    <div class="flex flex-wrap -mx-4 -mt-4 -mb-10 sm:-m-4">
      @@loop('problems/card.html', 'partials/problems/data.json')
    </div>
  </div>
</section>

Первым параметром в @@loop передаем путь до компонента (от настроенного ранее basepath), вторым – путь до JSON-файла (от src).


Структура файлов выглядит вот так:


src
│   index.html
│   main.css
│
└───partials
│   │
│   └───problems
│       │   index.html
│       │   card.html
│       │   data.json
...

Теперь сам index.html я могу вставить с помощью @@include в файл основной страницы.


<!DOCTYPE html>
<html lang="ru">
  <head>
    ...
  </head>
  <body>
    ...
    @@include('problems/index.html')
    ...
  </body>
</html>

Минимальными усилиями мы получили полноценное компонентное деление. При этом, это никак не отразится на результате, будет такой же HTML, как и раньше. Конечно, не могу не заметить, что это подкрепляет также тезис про TailwindCSS, – он уже не кажется таким неудобным, как прежде, не так ли? Дальше дело за малым – повторить всё выше перечисленное для всех секций сайта.


На этом закончим знакомство с данным плагином. Цель данной статьи было показать, как легко можно в небольших проектах избежать использование тяжеловесных современных фреймворков, и при этом сохранить те идеи, которые они с собой несут. Конечно, это не единственный вариант, как это сделать. Возможно, вы знаете другие альтернативы, интересно будет услышать о них в комментариях.


UPD: В комментариях указали на отсутствие функционала extend. Это правда и это плохо. Возможно, есть смысл для пуш-реквеста. Я подумаю над этим.

В то же время простым хаком можно легко реализовать данный функционал с помощью обычного @@include.
  • Создаем layout-файл, в который вставляем переменную @@data (имя произвольное, только не "content", оно зарезервировано)

<!DOCTYPE html>
<html lang="ru">
<head>...</head>
<body>
...
@@data
...
</body>
</html>

  • Создаем корневой index файл, в который вставляем наш layout, а переменной data передаем @@include файла с разметкой

@@include('layouts/app.html', {
"data": "@@include('main.html')"
})

Напоследок прошу ответить на вопрос:

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Что бы выбрали вы в ситуации, когда фреймворк избыточен?
21.98% Я бы всё равно использовал фреймворк 20
26.37% Я бы писал ванильную статику 24
17.58% Статические генераторы 16
14.29% PHP 13
16.48% Плагин наподобие gulp-file-include 15
3.3% Другое 3
Проголосовал 91 пользователь. Воздержались 13 пользователей.
Теги:
Хабы:
+2
Комментарии 8
Комментарии Комментарии 8

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн