Привет! Меня зовут Сергей Кузнецов, я руковожу отделом Frontend-разработки в AGIMA. Сегодня хотелось бы рассказать о разработке собственных библиотек элементов для крупных проектов. В статье объясню, зачем они нужны, почему собственные библиотеки могут быть полезнее готовых и какой алгоритм мы используем в работе.
Для чего нужны собственные библиотеки
При разработке любого крупного проекта и для его дальнейшей поддержки существует 2 пути:
- Сначала собрать сайт, а потом из его элементов собрать библиотеку элементов.
- Сначала собрать библиотеку и уже на ее основе проект.
Первый вариант более распространен, но мы не будем его рассматривать — он не дает всех плюсов второго решения. А вот если библиотека собрана до разработки основного проекта, то это немного тормозит разработку на ранних этапах, но зато дает существенный выигрыш на длинной дистанции. У такого решения несколько преимуществ:
- в коде нет дублей уже существующих решений,
- проще искать подходящие элементы по всем уже собранным макетам,
- код унифицирован,
- проще обновлять и добавлять новые элементы и тиражировать их в нужных местах.
Особенно важно использовать библиотеки при разработке серии проектов, для которых существует продуманный фирменный стиль. В этом случае библиотеки превращаются в полноценную дизайн-систему.
Почему готовые библиотеки не всегда полезны
В целом механизм создания подобных вещей кажется интуитивно понятным для опытных разработчиков. Но на практике мы сталкивались с вопросами, как именно лучше сделать, потому что, как и в любой относительно сложной задаче, здесь несколько способов достижения результата.
Для начала мы можем вообще задаться вопросом, не проще ли взять готовую библиотеку элементов. Да вот хоть тот же Bootstrap. Но тут, как говорится, не всё так однозначно. Вот с какими проблемами вы можете столкнуться:
- Самая меньшая проблема — это отсутствие опыта работы с какой-либо выбранной готовой библиотекой.
- Избыточность. Так как подобные решения популярны и их разработчики стремятся охватить как можно больше пользователей, они добавляют всё больше и больше возможных пресетов, настроек, дополнений и расширений. В итоге условно ради 20 нужных нам элементов, мы скачиваем пару сотен. Ну, и не забываем, что всё это всё равно необходимо кастомизировать и настраивать, если ваш проект хоть немного отличается от стандартных решений. А для крупных корпоративных систем дизайнеры стараются сделать покрасивее и поуникальнее. В рамках разумного, конечно, но всё же элементы визуально могут отличаться значительно. Отсюда вытекает следующая и, пожалуй, основная проблема.
- Наследование версионности тех вещей, которые вы изрядно переработали при подгонке готового решения под ваши нужды. Таким образом, есть смысл создать своё решение, заточенное под конкретный проект, где будет строго то, что необходимо вам, и не возникнет проблем с изменениями.
Как мы разрабатываем библиотеки
Изначальный набор базовых характеристик для разработки:
- атомарная структура;
- отсутствие внешних зависимостей;
- использование версионности;
- использование GIT и NPM;
- простой интерфейс;
- документация.
Всё начинается с правильной структуры самих элементов. В целом ничего лучшего, чем атомарный дизайн, еще не придумано. Его суть сводится к следующему: мы создаем максимально простые компоненты, которые выполняют какую-то минимально возможную функцию. Далее эти вещи соединяются в несколько более сложные «молекулы», которые могут стать блоками на странице. А далее в полноценные функциональные блоки. Степень дробления на молекулы и блоки в целом каждая команда определяет сама исходя из опыта и требований к проекту.
Отсутствие внешних включений иногда не является обязательным условием, но в целом, если можно избежать зависимости от чужого кода, то лучше так и сделать.
Далее идет поддержка разных версий. Для одиночного проекта это, пожалуй, не слишком актуальное требование, если только вы не планируете его поддерживать годами, лишь косметически обновляя время от времени. С большой долей вероятности вы будете обновлять проект целиком, и старые решения уже не подойдут. Совсем другой случай, когда вы поддерживаете несколько проектов, выполненных в едином корпоративном стиле, но с вариациями. Вот тут разные версии очень пригодятся.
Версионность мы реализуем сразу и в репозитории GIT, и через модули NPM. Последнее удобно использовать для автоматического обновления пакетов: достаточно запустить сборщик с указанными версиями.
С интерфейсом можно не изобретать велосипед, а воспользоваться проверенным решением по схеме: слева колонка с названиями, справа содержимое.
Далее у нас идет документация. Есть несколько способов ее реализации но мы остановили свой выбор на Markdown. Если кратко, то это облегчённый язык разметки, созданный для обозначения форматирования в простом тексте с максимальным сохранением его читаемости человеком, и пригодный для машинного преобразования в HTML. Выглядит примерно так:
Вопросы, возникшие в ходе разработки, выглядели как вилка решений, где мы выбирали между несколькими вариантами. Суть вопросов сводилась к следующему:
- Какую структуру проекта использовать?
- Отдельные пакеты или модификаторы?
- Как использовать общие элементы?
- Как использовать готовые решения/библиотеки?
В итоге мы пришли к такой схеме:
Этот вариант дает нам следующие преимущества:
- Мы можем собирать компонент, библиотеку или всё сразу, что избавляет нас от необходимости создавать отдельные репозитории для разных частей библиотеки.
- Мы автоматически получаем генерацию документации к компонентам и добавляем к примеру/странице. Главное — не забывать писать комментарии при разработке элементов или их обновлении.
- При сборке любого компонента создаем страницу предпросмотра и сразу открываем в браузере для удобства использования.
Между пакетами и модификаторами мы предпочли последнее. Пакеты решили зарезервировать за кардинальными изменениями версий, а разные подвиды одинаковых элементов оформить через добавление модификаторов с включением их в документацию. Это делалось, чтобы пользователь/разработчик могли просто подставить нужный класс и получить требуемый вариант внутри одной версии компонента.
Что касается использования общих элементов и библиотек, то суть сводится к следующей проблеме: если мы выносим их в отдельное место, то мы теряем в компактности, так как библиотеки нужны только там, где используются, а устанавливать придется всё. Если же мы кладем библиотеку в каждый отдельный компонент, мы мало того, что раздуваем размер самой библиотеки, так еще и теряем возможность следить за версиями этих библиотек, так как в каждом компоненте оказывается своя копия. В итоге эту проблему решали через установку зависимостей в самих компонентах, чтобы локальный компонент был связан с папкой с глобальными компонентами.
На этом всё. Надеюсь, эта статья поможет кому-то при решении подобных задач.