Предисловие

Доброго времени суток, коллеги! В этой статье я делюсь своим способом упорядочивания папок и файлов для проектов на React/Next. Я fullstack разработчик с 10+ лет опыта коммерческой разработки, множество стартапов разработал в различных командах, и несколько стартапов разработал в одиночку, в т.ч. своих собственных.

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

Этот способ организации файлов по архитектуре MVC (model, view, controller) не претендует на истину в её последней инстанции, в то же время это подход, к которому я пришёл пройдя достаточно долгий путь, и подход, отлично зарекомендовавший себя своим удобством во множестве проектов. Поэтому если сочтёте информацию полезной - берите на вооружение. Если же у вас свой подход - можете поделиться им в комментариях, или написать отдельную статью. Я не ставлю цель разводить холивар на тему того что этот способ безупречен и лучше всех остальных, это лишь один из способов.

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

Определимся с целью

Что мы хотим когда продумываем файловую структуру? Во первых чтобы у каждого файла с кодом было, в зависимости от того, что в этом файле находится, своё конкретное место. Когда мы хотим найти страницу - мы точно знаем в какой папке она находится. Когда мы хотим найти секцию, скрипт, или что-то ещё - аналогично. Если мы запихнём все компоненты в папку components - в среднем/крупном проекте у нас появится длиннющий список, и потребуется тратить лишнее время чтобы его листать, в поиске нужного компонента. Значит необходимо создавать подгруппы папок. Об этом и статья.

Начало работы:

После инициализации проекта почистим проект от мусорных файлов (стандартные файлы стилей, стартовый шаблон, и тд) и создадим основные папки. Если у вас конфигурация с папкой src - то в ней, если нет, то в корне проекта.

Для хранения JSX-компонентов я создаю папку react.

Зачем нам эта дополнительная папка? Чтобы папки с компонентами страниц, ui-компонентов, и прочая "вёрстка" были рядом друг с другом.

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

Другое дело, когда они находятся рядышком, в одной папке!

В папке react сразу создадим следующие папки:

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

pages - в ней будут компоненты полноценных страниц. папка создаётся только для react, либо когда в next используется роутер отличный от классического, когда файлы страниц находятся в папке pages в корне проекта

views - в ней будут компоненты полноценных страниц, которые фактически могут быть использованы на разных страницах. если в проекте предполагается такая ситуация, для порядка имеет смысл хранить все страницы в папке views, а в компонентах в папке pages вызывать эти компоненты. если же таких ситуаций не предполагается, в react от этой папки можно отказаться. в next.js я в любом случае создаю эту папку, чтобы папки с файлами страниц были рядом с папками других JSX-компонентов.

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

popups - в ней будут компоненты модальных окон. как правило создаётся папка с мастер-компонентом, и рядом располагаются папки с модалками, созданными на основе этого компонента

Я специально не разместил последние 3 папки в папке components, по той причине, что в components у нас будет достаточно папок, а данные папки должны быть в быстром доступе. Что именно будет располагаться в components нуждается в подробном описании, и располагается ближе к концу статьи.

Для хранения JS-логики создадим папку scripts

В ней создадим следующие подпапки:

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

hooks - самописные React-хуки

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

Для хранения глобальных стилей и шрифтов создадим папку styles

Глобальные стили - это общие стили, распространяющиеся на весь проект. Для отдельных компонентов я предпочитаю хранить стили в папке с компонентом, в отдельном файле. Буду показывать на примере scss, т.к. использую его чаще всего. Итак, создаём файлы:

global.scss - именно этот файл будет импортироваться в приложение, а также в этот файл будут импортироваться остальные файлы стилей из папки styles. Также в данном файле прописывается сброс стандартных css-стилей, и импортируются шрифты.

text.scss - в этом файле я прописываю атомарные стили для текста. Как правило в крупных проектах всё стандартизировано - заголовки, подзаголовки, тексты, и т.д.

variables.scss - в этом файле будут храниться остальные css-переменные

animations.scss - в этом файле соответственно хранятся стили css-анимаций

Если в проекте используются локальные шрифты, в папке styles следует создать папку fonts, и разместить их там

Для хранения JS-переменных создадим папку constants

В ней будут JS-файлы в которых переменные будут сгруппированы по смыслу из названия файла. К примеру у меня в этой папке могут находиться такие файлы как:

settings.js - какие-либо настройки

api.js - перечень всех адресов API бэкенда, именно эти переменные используются в запросах к серверу

secret.js - различные токены и секретные ключи. рядом с этим файлом располагается secret.example.js в котором есть переменные, но нет их значений, а сам secret.js находится в .gitignore

urls.js - различные внутренние и внешние ссылки для использования в разных частях проекта. так проще изменив одну переменную сразу автоматически применить изменение везде, где используется ссылка.

И так далее.

Для глобального состояния создадим папку store

И в ней создаём подпапки для файлов того типа глобального состояния, который используете вы (например actions, reducers, и тд). Лично я использую в значительном количестве случаев к сожалению не столь известный, но очень простой и доказавший свою эффективность модуль useGlobalHook, по которому я написал мини-гайд: Лёгкое в использовании глобальное хранилище состояния для React или Next: useGlobalHook.

Для хранения разных локализаций создадим папку locales

Только в том случае, если предполагается, что интерфейс проекта будет доступен более чем на одном языке. Тогда для каждого языка мы создадим отдельный js файл, содержащий переменные с текстом необходимой локализации.

Итак, основные папки мы создали!

Теперь погрузимся в содержимое папки /react/components. Как я писал выше, если мы просто запихнём все папки с компонентами в эту папку, у нас получится длинный список, который листать долго и неудобно. Поэтому компоненты мы будем группировать по папкам.

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

Ещё раз акцентирую внимание: все папки о которых пойдёт речь ниже, необходимо создавать в папке /react/components!

Папка containers

В ней располагаются компоненты, выполняющие функцию обёртки, т.е. компоненты, которые возвращают children.

К примеру это может быть компонент carcas - общая обёртка для всех страниц, выводящая помимо children компоненты header (шапка) и footer (подвал), а также содержащий мета-теги (с возможностью передачи кастомных тегов/состояний через props).

Пример return-а контейнера carcas:

Контейнеры разметок страницы (wrapper-ы, layout-ы), стандартизированные компоненты вывода текста, универсальные компоненты-контейнеры (как например универсальный сворачивающийся и разворачивающийся аккордеон на основе которого можно создать отдельные компоненты аккордеонов для конкретных ситуаций). Ну вы поняли.

Папка inputs

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

Примеры компонентов, к-е должны находиться в папке inputs
Ввод номера телефона
Ввод номера телефона

Компонент выбора цвета (colorpicker)
Компонент выбора цвета (colorpicker)

Выбор одного из нескольких пунктов
Выбор одного из нескольких пунктов

Выбор одного из нескольких пунктов
Выбор одного из нескольких пунктов

Кнопка
Кнопка

Поле поиска с выпадающим списком подсказок
Поле поиска с выпадающим списком подсказок

Переключатель "вкл/выкл"
Переключатель "вкл/выкл"

Загрузчик медиа
Загрузчик медиа

Поле ввода текста
Поле ввода текста

Выпадающий список (selectfield)
Выпадающий список (selectfield)

Выбор доступного времени
Выбор доступного времени

Многострочное поле ввода текста
Многострочное поле ввода текста

Папка forms

Содержит компоненты, являющиеся сочетанием нескольких компонентов из папок inputs. Это может быть к примеру форма ввода логина и пароля (только текстовые поля и кнопка), или форма подписки на e-mail рассылку.

Примеры компонентов, к-е должны находиться в папке forms
этот компонент должен быть в папке /react/components/forms/название.папки.компонента
этот компонент должен быть в папке /react/components/forms/название.папки.компонента
этот компонент должен быть в папке /react/components/forms/название.папки.компонента
этот компонент должен быть в папке /react/components/forms/название.папки.компонента

Папка sliders

Содержит кастомные компоненты-слайдеры. Т.е. если нужно вывести где-то слайдер, обычно мы используем модуль, этот модуль тянет за собой кучу настроек и так далее. Для удобства мы создаём отдельный компонент в этой папке, в котором добиваемся чтобы слайдер выглядел как нам необходимо, и при помощи props добиваемся максимально простого использования и пере-использования конкретного типа отображения слайдера из UI-kit проекта.

Папки icons и illustrations

Только для Next.js, т.к. как ни странно, пока что в него нельзя импортировать иконки как компоненты напрямую из svg (в то время как в React можно). Как понятно из названий папок, в них лежат JSX-компоненты с svg-кодом. Для иллюстраций отдельная папка, чтобы иконки не перемешивались с svg-иллюстрациями.

Папка cards

В ней содержатся компоненты отображающие некоторое количество информации об какой либо отдельной сущности. Чаще всего когда эти компоненты используются в частях проекта, они выводятся циклом через map. Как пример - карточка товара в списке товаров интернет-магазина, элемент "отзыв" из списка отзывов, баннеры (не являющиеся слайдерами), и так далее.

Примеры компонентов, к-е должны находиться в папке cards
этот компонент должен быть в папке /react/components/cards/название.папки.компонента
этот компонент должен быть в папке /react/components/cards/название.папки.компонента

этот компонент должен быть в папке /react/components/cards/название.папки.компонента
этот компонент должен быть в папке /react/components/cards/название.папки.компонента
этот компонент должен быть в папке /react/components/cards/название.папки.компонента
этот компонент должен быть в папке /react/components/cards/название.папки.компонента

этот компонент должен быть в папке /react/components/cards/название.папки.компонента
этот компонент должен быть в папке /react/components/cards/название.папки.компонента

Папка ui

UI расшифровывается как user interface. В данной папке будут располагаться компоненты пользовательского интерфейса не попадающие ни под одну из категорий, описанных выше. Например header, footer, хлебные крошки, пагинация, и т.д.

Очень важно прежде чем создавать папку компонента в папке ui, проверить, не попадает ли он под категории описанные выше, и если попадает - создать его именно в необходимой папке.

Отлично, файловая структура для проекта готова!

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

Ещё несколько рекомендаций:

Создавая папки для самих компонентов, называйте их по смыслу. Экспериментируя, я пробовал называть папки/файлы используяВерблюжийСтиль а также используя_нижнее_подчёркивание, и обнаружил, что наиболее приятным для восприятия является подход, где в названии файла/папки вместо.пробелов.используются.точки

Я рекомендую создавать для каждого компонента папку, где в index.jsx (или index.tsx) будет храниться код компонента, а так же в этой же папке будет располагаться отдельный файл со стилями компонента (какой бы способ вы ни использовали - хоть scss, хоть styled).

Не создавайте несколько компонентов в одном файле, и тем более компонент внутри компонента. Первое не наглядно, а второе так и вовсе вредит оптимизации.

При этом старайтесь каждую часть интерфейса выделять в отдельный компонент, и использовать его. Т.к. одна из основных прелестей React/Next, о которых мы говорим - модульный подход.

Код станет значительно нагляднее, если не пихать львиную долю вёрстки в один файл (хоть и это тоже будет работать), а собирать отдельные части в отдельные "пазлы", и уже из этих "пазлов" собирать общую картину. И каждый "пазл" может сам состоять из нескольких "пазлов".

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

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

Представьте, как выглядела бы страница, если бы JSX-код всех секций находился не в отдельных компонентах, а прямо на этой странице? Это была бы полная жуть, в которой невозможно ориентироваться. А тут смотрим - и сразу понятно, где какая секция. Можно перейти в неё прямо из этого файла, и отредактировать при необходимости.

Не ленитесь создать и заполнить файл readme.md в корне проекта!

Существует куча редакторов markdown, в которых можно создать оформленный текст, без знаний разметки md. В этом файле опишите структуру проекта, чтобы другим программистам (и в т.ч. вам) было проще ориентироваться в файловой структуре проекта. Также опишите в нём, как разворачивать проект в первый раз, как разворачивать для разработки и для продакшена. Прикрепите ссылки на материалы проекта (дизайн в figma, документация к API и так далее.

Веб-сервисы для хостинга репозиториев Git-а (GitHub, GitLab, и т.д.) понимают markdown, и на странице вашего проекта, под списком файлов, будет инструкция со всеми ссылками.

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

Желаю удачи в разработке проектов!

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

Комментарии (10)


  1. artptr86
    23.06.2024 21:42
    +2

    Что же в этой структуре от MVC?

    GIT понимает markdown, и на странице вашего проекта в гите

    Ну камон, не Гит же, а Гитхаб или Гитлаб, хотя бы.


    1. Rikkster Автор
      23.06.2024 21:42

      Что же в этой структуре от MVC?

      То, что мы не городим в одном файле и всю связанную вёрстку и всю связанную логику, конечно же. MVC в названии к тому, что речь пойдёт не про какой нибудь FSD к примеру.

      не Гит же, а Гитхаб или Гитлаб, хотя бы.

      Все GIT-производные, а их гораздо больше, GitVerce например есть от отечественного производителя. Суть-то понятна, что я имею в виду - вряд ли речь идёт про консоль.


      1. artptr86
        23.06.2024 21:42

        MVC в названии подразумевает архитектурное разделение на модель, вид и контроллер. Были даже когда-то фронтендные фреймворки, основанные на таком паттерне, например, Backbone. Просто так употреблять эту аббревиатуру для отличия от FSD — неправильно. Точно так же, как неправильно называть произвольный сервис хранения исходного кода "гитом". Тем более, что вы учите других. А то ведь в Интернете известны примеры типа «Язык Джаваскрипт, для простоты называемый Джавой».


        1. Rikkster Автор
          23.06.2024 21:42

          MVC в названии подразумевает архитектурное разделение на модель, вид и контроллер

          Именно об этом и идёт речь в статье, а именно:

          Модель - глобальный и локальный стейт а также хуки (хранят модель данных, отображаемых на странице). Глобальный стейт по статье хранится в папке store

          Вид - компоненты React являются представлением, т.к. отвечают за отображение данных и реагирование на действия пользователей. По статье хранятся в папке React

          Контроллер - JS-логика как данные получаются, обрабатываются и обновляются. По статье подобные функции находятся в папке scripts, так же контроллерами можно считать экшены, по статье хранящиеся в store/actions

          Точно так же, как неправильно называть произвольный сервис хранения исходного кода "гитом"

          Я вижу не одного вас это сильно задело, ну так то вы в данном случае правы, отредактировал статью


          1. artptr86
            23.06.2024 21:42

            Вообще-то нет, MVC — вполне себе конкретный паттерн. Иначе тогда вообще размывается разница между MVC, MVP, MVVM и прочими подобными паттернами, а за разделение ответственности у нас и так отвечает S в SOLID.


            1. Rikkster Автор
              23.06.2024 21:42

              Я конкретно ответил каким образом реализуется MVC в моём примере. Аргументируйте, если не согласны.


  1. OneKek
    23.06.2024 21:42

    Используете ли вы в одном проекте tailwind и scss?


  1. OneKek
    23.06.2024 21:42
    +2

    Используете ли вы в одном проекте tailwind и scss?


    1. Rikkster Автор
      23.06.2024 21:42

      Да, но поскольку я использую для вёрстки adaptivepx.ru, я создаю кастомный файл tailwind.scss где размеры указаны в адаптивных пикселях, и есть только те атомарные стили, которые я использую в проекте. Т.е. я использую идею tailwind, но не саму библиотеку.


  1. novoselov
    23.06.2024 21:42
    +6

    К примеру это может быть компонент carcas - общая обёртка для всех страниц

    Вот такое именование вы выработали за 10+ лет опыта коммерческой разработки?


  1. Fanatos
    23.06.2024 21:42

    Спасибо, познавательно!