Автор материала, перевод которого мы сегодня публикуем, говорит, что GitHub-репозиторий, над которым работал он и ещё несколько фрилансеров, получил, по разным причинам, около 8200 звёзд за 3 дня. Этот репозиторий попал на первое место в HackerNews и в GitHub Trending, за него отдали голоса 20000 пользователей Reddit.
В данном репозитории отражена методика разработки фулстек-приложений, которой посвящена эта статья.
Я собирался написать этот материал уже довольно давно. Полагаю, что лучшего момента, чем этот, когда наш репозиторий пользуется серьёзной популярностью, мне не найти.
№ 1 в GitHub Trending
Я работаю в команде фрилансеров. В наших проектах используется React/React Native, NodeJS и GraphQL. Этот материал предназначен для тех, кто хочет узнать о том, как мы разрабатываем приложения. Кроме того, он будет полезен тем, кто в будущем присоединится к нашей команде.
Сейчас я расскажу об основных принципах, которые мы используем при разработке проектов.
«Чем проще — тем лучше», — это легче сказать, чем сделать. Большинство разработчиков отдают себе отчёт в том, что простота — это важный принцип разработки ПО. Но этому принципу не всегда легко следовать. Если код устроен просто — это облегчает поддержку проекта и упрощает командную работу над этим проектом. Кроме того, соблюдение этого принципа помогает в работе с кодом, который был написан, скажем, полгода назад.
Вот какие ошибки, касающиеся рассматриваемого принципа, мне приходилось встречать:
Многие из идей, о которых речь пойдёт ниже, направлены на то, чтобы сделать кодовую базу как можно более простой и поддерживать её в таком состоянии.
Этот принцип, «принцип колокации», применим ко многим частям приложения. Это и структура папок, в которых хранится код клиента и сервера, это и хранение кода проекта в одном репозитории, это и принятие решений о том, какой именно код оказывается в некоем файле.
Рекомендуется держать код клиента и сервера в одном и том же репозитории. Это просто. Не стоит усложнять то, что усложнять не нужно. При таком подходе удобно организовать согласованную командную работу над проектом. Я работал над проектами, для хранения материалов которых использовались различные репозитории. Это — не катастрофа, но монорепозитории делают жизнь гораздо легче.
Мы пишем фулстек-приложения. То есть — и код клиента, и код сервера. В структуре папки типичного клиентского проекта предусмотрены отдельные директории для компонентов, контейнеров, действий, редьюсеров и маршрутов.
Действия и редьюсеры присутствуют в тех проектах, в которых используется Redux. Я стремлюсь к тому, чтобы обходиться без этой библиотеки. Я уверен в том, что существуют качественные проекты, в которых используется такая же структура. В некоторых из моих проектов имеются отдельные папки для компонентов и контейнеров. В папке компонентов может храниться нечто вроде файлов с кодом таких сущностей, как
Это — рабочая структура. Она, по крайней мере, однородна, а это очень важно. Это ведёт к тому, что разработчик, присоединившийся к работе над проектом, быстро поймёт то, что в нём происходит, и то, какую роль играют его отдельные части. Минус этого подхода, из-за которого я в последнее время стараюсь им не пользоваться, заключается в том, что он заставляет программиста постоянно перемещаться по кодовой базе. Например, у сущностей
Я с некоторых пор стремлюсь к тому, чтобы размещать файлы, содержимое которых планируется использовать совместно, в одних и тех же папках. Такой подход структурирования проекта основан на группировке файлов на основании реализуемых ими возможностей. Благодаря этому подходу можно значительно облегчить себе жизнь, если, например, поместить в одну папку родительский компонент и его «глупый» дочерний компонент.
Обычно мы используем папки
В пределах папки маршрута можно создавать дополнительные папки, в которых сгруппирован код, ответственный за формирование разных частей страницы. Это имеет смысл в тех случаях, когда маршрут представлен большим объёмом кода. Тут, однако, мне хотелось бы предупредить читателя о том, что не стоит создавать структуры из папок с очень большим уровнем вложенности. Это усложняет перемещение по проекту. Глубокие вложенные структуры папок — это один из признаков чрезмерного усложнения проекта. Надо отметить, что использование специализированных инструментов, вроде команд поиска, даёт программисту удобные средства для работы с кодом проекта и для поиска того, что ему нужно. Но структура файлов проекта также оказывает влияние на удобство работы с ним.
Структурируя код проекта, можно группировать файлы, опираясь не на маршрут, а на реализуемые этими файлами возможности проекта. В моём случае этот подход отлично показывает себя на проектах-одностраничниках, реализующих множество возможностей на своей единственной странице. Но надо отметить, что группировка материалов проекта по маршрутам проще. Этот подход не требует особенных умственных усилий для того, чтобы принимать решения о том, какие именно сущности следует размещать рядом друг с другом, и для того, чтобы что-то искать.
Если пойти по пути группировки кода дальше, то можно решить, что код контейнеров и компонентов оправданно будет поместить в один и тот же файл. А можно пойти и ещё дальше — положить в один файл код двух компонентов. Полагаю, вы вполне можете думать сейчас о том, что рекомендовать такие вещи — это прямо-таки кощунство. Но в реальности всё далеко не так плохо. На самом деле, такой подход вполне себя оправдывает. И если вы используете хуки React, или сгенерированный код (или — и то и другое), я бы порекомендовал вам именно такой подход.
На самом деле, вопрос о том, как именно разложить код по файлам, имеет не первостепенную важность. Настоящий вопрос заключается в том, почему вообще может понадобиться делить компоненты на «умные» и «глупые». Какие выгоды можно извлечь от такого разделения? На этот вопрос можно дать несколько ответов:
Всё это — реальные доводы в пользу разделения компонентов на «умные» и «глупые», но они применимы далеко не ко всем ситуациям. Например, мы часто, при создании проектов, используем Apollo Client с хуками. Для того чтобы такие проекты тестировать, можно либо создавать моки ответов Apollo, либо моки хуков. То же самое касается и Storybook. Если говорить о смешивании и совместном использовании «умных» и «глупых» компонентов, то я, на самом деле, никогда этого на практике не встречал. В том, что касается кроссплатформенного использования кода, был один проект, в котором я собирался сделать нечто подобное, но так и не сделал. Это должен был быть монорепозиторий Lerna. В наши дни вместо этого подхода вполне можно выбрать React Native Web.
В результате можно сказать, что в разделении компонентов на «умные» и «глупые» есть определённый смысл. Это — важная концепция, о которой стоит знать. Но часто о ней не нужно особенно сильно беспокоиться, особенно учитывая недавнее появление хуков React.
Сильная сторона совмещения в одной сущности возможностей «умных» и «глупых» компонентов заключается в том, что это ускоряет разработку, и в том, что это упрощает структуру кода.
Более того, если возникнет такая необходимость, некий компонент всегда можно разделить на два отдельных компонента — «умный» и «глупый».
Мы используем для стилизации приложений emotion / styled components. Всегда есть соблазн выделить стили в отдельный файл. Я видел, как некоторые разработчики так и поступают. Но, после того как я испробовал оба подхода, я в итоге не нашёл причин для перемещения стилей в отдельный файл. Как и в случае со многим другим, о чём мы тут говорим, разработчик может облегчить себе жизнь, совмещая в одном файле стили и компоненты, к которым они относятся.
Всё вышесказанное справедливо и в отношении структурирования кода серверной части приложения. Типичная структура, которой лично я стараюсь избегать, может выглядеть примерно так:
Мы в своих проектах обычно применяем GraphQL. Поэтому в них используются файлы, в которых хранятся модели, сервисы и распознаватели. Вместо того чтобы разбрасывать их по разным местам проекта, я собираю их в одной папке. Чаще всего эти файлы будут использоваться совместно, и с ними будет легче работать в том случае, если они будут храниться в одной и той же папке.
Мы используем в своих проектах множество решений, так или иначе имеющих отношение к типам данных. Это TypeScript, GraphQL, схемы баз данных, и иногда MobX. В результате может оказаться так, что типы для одних и тех же сущностей описывают по 3-4 раза. Подобных вещей стоит избегать. Надо стремиться к использованию инструментов, автоматически генерирующих описания типов.
На сервере для этой цели можно воспользоваться комбинацией TypeORM/Typegoose и TypeGraphQL. Этого хватит для описания всех используемых типов. TypeORM/Typegoose позволит описать схему базы данных и соответствующие типы TypeScript. TypeGraphQL поможет в создании типов GraphQL и TypeScript.
Вот пример определения типов TypeORM (MongoDB) и TypeGraphQL в одном файле:
GraphQL Code Generator также умеет генерировать множество различных типов. Мы используем этот инструмент для создания типов TypeScript на клиенте, а так же — хуков React, выполняющих обращения к серверу.
Если вы используете MobX для управления состоянием приложения, то вы, воспользовавшись парой строк кода, можете получить автоматически сгенерированные TS-типы. Если же вы, к тому же, пользуетесь и GraphQL, то вам стоит взглянуть на новый пакет — MST-GQL, который генерирует дерево состояния из GQL-схемы.
Совместное использование этих инструментов убережёт вас от переписывания больших объёмов кода и поможет избежать типичных ошибок.
Другие решения, такие как Prisma, Hasura и AWS AppSync, тоже могут помочь избежать дублирования объявлений типов. У использования подобных инструментов, конечно, есть свои плюсы и минусы. В создаваемых нами проектах подобные средства используются не всегда, так как нам нужно развёртывать код на собственных серверах организаций.
Если взглянуть на код, который создают без использования вышеописанных средств для автоматического генерирования кода, то окажется, что программистам постоянно приходится писать одно и тоже. Главный совет, который я могу дать по этому поводу, заключается в том, что нужно создавать сниппеты для всего, чем вы часто пользуетесь. Если вы часто вводите команду
Существует множество пакетов со сниппетами, но несложно и создавать собственные сниппеты. Например — с помощью Snippet generator.
Вот код, который позволяет добавить некоторые из моих любимых сниппетов в VS Code:
Сэкономить время, помимо сниппетов, могут помочь генераторы кода. Их можно создавать самостоятельно. Мне для этого нравится использовать plop.
В Angular есть собственные встроенные генераторы кода. С помощью инструментов командной строки можно создать новый компонент, состоящий из 4 файлов, в которых представлено всё то, что можно ожидать найти в компоненте. Жаль, что в React нет такой вот стандартной возможности, но нечто подобное можно создать и самостоятельно, используя plop. Если каждый новый создаваемый вами компонент должен быть представлен в виде папки, содержащей файл с кодом компонента, файл с тестом и файл Storybook, генератор поможет создать всё это одной командой. Это во многих случаях значительно облегчает жизнь разработчика. Например, при добавлении новой возможности на сервер достаточно выполнить одну команду в командной строке. После этого автоматически будут созданы файлы сущности, сервисов и распознавателей, содержащие все необходимые базовые конструкции.
Ещё одна сильная сторона генераторов кода заключается в том, что они способствуют единообразию в командной разработке. Если все используют один и тот же plop-генератор, то код у всех будет получаться весьма однородным.
Форматирование кода — простая задача, но её, к сожалению, не всегда решают правильно. Не тратьте время, вручную выравнивая код или вставляя в него точки с запятой. Используйте Prettier для автоматического форматирования кода при выполнении коммитов.
В этом материале я рассказал вам о некоторых вещах, о которых мы узнали за годы работы, за годы проб и ошибок. Существует множество подходов к структурированию кодовой базы проектов. Но среди них нет такого, который можно назвать единственно правильным.
Самое главное, что я хотел до вас донести, заключается в том, что программисту стоит стремиться к простоте организации проектов, к их однородности, к использованию понятной структуры, с которой легко работать. Это упрощает командную разработку проектов.
Уважаемые читатели! Что вы думаете об идеях, касающихся разработки фулстек-приложений на JavaScript, изложенных в этом материале?
В данном репозитории отражена методика разработки фулстек-приложений, которой посвящена эта статья.
Предыстория
Я собирался написать этот материал уже довольно давно. Полагаю, что лучшего момента, чем этот, когда наш репозиторий пользуется серьёзной популярностью, мне не найти.
№ 1 в GitHub Trending
Я работаю в команде фрилансеров. В наших проектах используется React/React Native, NodeJS и GraphQL. Этот материал предназначен для тех, кто хочет узнать о том, как мы разрабатываем приложения. Кроме того, он будет полезен тем, кто в будущем присоединится к нашей команде.
Сейчас я расскажу об основных принципах, которые мы используем при разработке проектов.
Чем проще — тем лучше
«Чем проще — тем лучше», — это легче сказать, чем сделать. Большинство разработчиков отдают себе отчёт в том, что простота — это важный принцип разработки ПО. Но этому принципу не всегда легко следовать. Если код устроен просто — это облегчает поддержку проекта и упрощает командную работу над этим проектом. Кроме того, соблюдение этого принципа помогает в работе с кодом, который был написан, скажем, полгода назад.
Вот какие ошибки, касающиеся рассматриваемого принципа, мне приходилось встречать:
- Неоправданное стремление к выполнению принципа DRY. Иногда копирование и вставка кода — это вполне нормально. Не нужно абстрагировать каждые 2 фрагмента кода, которые чем-то похожи друг на друга. Я и сам совершал эту ошибку. Все, пожалуй, её совершали. DRY — это хороший подход к программированию, но выбор неудачной абстракции способен лишь ухудшить ситуацию и усложнить кодовую базу. Если вы хотите узнать подробности об этих идеях — рекомендую почитать материал «AHA Programming» Кента Доддса.
- Отказ от использования имеющихся инструментов. Один из примеров этой ошибки — использование
reduce
вместоmap
илиfilter
. Конечно, с помощьюreduce
можно воспроизвести поведениеmap
. Но это, вероятно, приведёт к росту размера кода, и к тому, что другим людям будет сложнее понять этот код, учитывая то, что «простота кода» — понятие субъективное. Иногда может понадобиться использовать именноreduce
. А если сравнить скорость обработки набора данных с использованием объединённых в цепочку вызововmap
иfilter
, и с использованиемreduce
, то окажется, что второй вариант работает быстрее. В варианте сreduce
набор значений приходится просматривать один раз, а не два. Перед нами — спор производительности и простоты. Я, в большинстве случаев, отдал бы предпочтение простоте и стремился бы к тому, чтобы избежать преждевременной оптимизации кода, то есть, выбрал бы паруmap
/filter
вместоreduce
. А если бы оказалось так, что конструкция изmap
иfilter
стала узким местом системы, перевёл бы код наreduce
.
Многие из идей, о которых речь пойдёт ниже, направлены на то, чтобы сделать кодовую базу как можно более простой и поддерживать её в таком состоянии.
Держите схожие сущности рядом друг с другом
Этот принцип, «принцип колокации», применим ко многим частям приложения. Это и структура папок, в которых хранится код клиента и сервера, это и хранение кода проекта в одном репозитории, это и принятие решений о том, какой именно код оказывается в некоем файле.
▍Репозиторий
Рекомендуется держать код клиента и сервера в одном и том же репозитории. Это просто. Не стоит усложнять то, что усложнять не нужно. При таком подходе удобно организовать согласованную командную работу над проектом. Я работал над проектами, для хранения материалов которых использовались различные репозитории. Это — не катастрофа, но монорепозитории делают жизнь гораздо легче.
▍Структура проекта клиентской части приложения
Мы пишем фулстек-приложения. То есть — и код клиента, и код сервера. В структуре папки типичного клиентского проекта предусмотрены отдельные директории для компонентов, контейнеров, действий, редьюсеров и маршрутов.
Действия и редьюсеры присутствуют в тех проектах, в которых используется Redux. Я стремлюсь к тому, чтобы обходиться без этой библиотеки. Я уверен в том, что существуют качественные проекты, в которых используется такая же структура. В некоторых из моих проектов имеются отдельные папки для компонентов и контейнеров. В папке компонентов может храниться нечто вроде файлов с кодом таких сущностей, как
BlogPost
и Profile
. В папке контейнеров имеются файлы, хранящие код контейнеров BlogPostContainer
и ProfileContainer
. Контейнер получает данные с сервера и передаёт их «глупому» дочернему компоненту, задача которого заключается в том, чтобы вывести эти данные на экран.Это — рабочая структура. Она, по крайней мере, однородна, а это очень важно. Это ведёт к тому, что разработчик, присоединившийся к работе над проектом, быстро поймёт то, что в нём происходит, и то, какую роль играют его отдельные части. Минус этого подхода, из-за которого я в последнее время стараюсь им не пользоваться, заключается в том, что он заставляет программиста постоянно перемещаться по кодовой базе. Например, у сущностей
ProfileContainer
и BlogPostContainer
нет ничего общего, но их файлы находятся рядом друг с другом и при этом далеко от тех файлов, в которых они по-настоящему используются.Я с некоторых пор стремлюсь к тому, чтобы размещать файлы, содержимое которых планируется использовать совместно, в одних и тех же папках. Такой подход структурирования проекта основан на группировке файлов на основании реализуемых ими возможностей. Благодаря этому подходу можно значительно облегчить себе жизнь, если, например, поместить в одну папку родительский компонент и его «глупый» дочерний компонент.
Обычно мы используем папки
routes
/ screens
и папку components
. В папке для компонентов обычно хранится код таких элементов, как Button
или Input
. Этот код может быть использован на любой странице приложения. Каждая папка, находящаяся в папке для маршрутов, представляет собой отдельную страницу приложения. При этом файлы с кодом компонентов и с кодом логики приложения, относящиеся к данному маршруту, находятся внутри той же самой папки. А код компонентов, которые используются на нескольких страницах, попадает в папку components
.В пределах папки маршрута можно создавать дополнительные папки, в которых сгруппирован код, ответственный за формирование разных частей страницы. Это имеет смысл в тех случаях, когда маршрут представлен большим объёмом кода. Тут, однако, мне хотелось бы предупредить читателя о том, что не стоит создавать структуры из папок с очень большим уровнем вложенности. Это усложняет перемещение по проекту. Глубокие вложенные структуры папок — это один из признаков чрезмерного усложнения проекта. Надо отметить, что использование специализированных инструментов, вроде команд поиска, даёт программисту удобные средства для работы с кодом проекта и для поиска того, что ему нужно. Но структура файлов проекта также оказывает влияние на удобство работы с ним.
Структурируя код проекта, можно группировать файлы, опираясь не на маршрут, а на реализуемые этими файлами возможности проекта. В моём случае этот подход отлично показывает себя на проектах-одностраничниках, реализующих множество возможностей на своей единственной странице. Но надо отметить, что группировка материалов проекта по маршрутам проще. Этот подход не требует особенных умственных усилий для того, чтобы принимать решения о том, какие именно сущности следует размещать рядом друг с другом, и для того, чтобы что-то искать.
Если пойти по пути группировки кода дальше, то можно решить, что код контейнеров и компонентов оправданно будет поместить в один и тот же файл. А можно пойти и ещё дальше — положить в один файл код двух компонентов. Полагаю, вы вполне можете думать сейчас о том, что рекомендовать такие вещи — это прямо-таки кощунство. Но в реальности всё далеко не так плохо. На самом деле, такой подход вполне себя оправдывает. И если вы используете хуки React, или сгенерированный код (или — и то и другое), я бы порекомендовал вам именно такой подход.
На самом деле, вопрос о том, как именно разложить код по файлам, имеет не первостепенную важность. Настоящий вопрос заключается в том, почему вообще может понадобиться делить компоненты на «умные» и «глупые». Какие выгоды можно извлечь от такого разделения? На этот вопрос можно дать несколько ответов:
- Приложения, устроенные таким образом, легче тестировать.
- При разработке таких приложений легче использовать инструменты наподобие Storybook.
- «Глупые» компоненты можно использовать с множеством разных «умных» компонентов (и наоборот).
- «Умные» компоненты можно использовать на разных платформах (например — на платформах React и React Native).
Всё это — реальные доводы в пользу разделения компонентов на «умные» и «глупые», но они применимы далеко не ко всем ситуациям. Например, мы часто, при создании проектов, используем Apollo Client с хуками. Для того чтобы такие проекты тестировать, можно либо создавать моки ответов Apollo, либо моки хуков. То же самое касается и Storybook. Если говорить о смешивании и совместном использовании «умных» и «глупых» компонентов, то я, на самом деле, никогда этого на практике не встречал. В том, что касается кроссплатформенного использования кода, был один проект, в котором я собирался сделать нечто подобное, но так и не сделал. Это должен был быть монорепозиторий Lerna. В наши дни вместо этого подхода вполне можно выбрать React Native Web.
В результате можно сказать, что в разделении компонентов на «умные» и «глупые» есть определённый смысл. Это — важная концепция, о которой стоит знать. Но часто о ней не нужно особенно сильно беспокоиться, особенно учитывая недавнее появление хуков React.
Сильная сторона совмещения в одной сущности возможностей «умных» и «глупых» компонентов заключается в том, что это ускоряет разработку, и в том, что это упрощает структуру кода.
Более того, если возникнет такая необходимость, некий компонент всегда можно разделить на два отдельных компонента — «умный» и «глупый».
Стилизация
Мы используем для стилизации приложений emotion / styled components. Всегда есть соблазн выделить стили в отдельный файл. Я видел, как некоторые разработчики так и поступают. Но, после того как я испробовал оба подхода, я в итоге не нашёл причин для перемещения стилей в отдельный файл. Как и в случае со многим другим, о чём мы тут говорим, разработчик может облегчить себе жизнь, совмещая в одном файле стили и компоненты, к которым они относятся.
▍Структура проекта серверной части приложения
Всё вышесказанное справедливо и в отношении структурирования кода серверной части приложения. Типичная структура, которой лично я стараюсь избегать, может выглядеть примерно так:
src
│ app.js # Точка входа в приложение
└───api # Контроллер маршрутов Express для всех конечных точек приложения
└───config # Переменные среды и средства конфигурирования
└───jobs # Объявление заданий для agenda.js
└───loaders # Разделение кода на модули
└───models # Модели баз данных
└───services # Бизнес-логика
└───subscribers # Обработчики событий для асинхронных задач
└───types # Файлы объявлений типов (d.ts) для Typescript
Мы в своих проектах обычно применяем GraphQL. Поэтому в них используются файлы, в которых хранятся модели, сервисы и распознаватели. Вместо того чтобы разбрасывать их по разным местам проекта, я собираю их в одной папке. Чаще всего эти файлы будут использоваться совместно, и с ними будет легче работать в том случае, если они будут храниться в одной и той же папке.
Не переписывайте по многу раз определения типов
Мы используем в своих проектах множество решений, так или иначе имеющих отношение к типам данных. Это TypeScript, GraphQL, схемы баз данных, и иногда MobX. В результате может оказаться так, что типы для одних и тех же сущностей описывают по 3-4 раза. Подобных вещей стоит избегать. Надо стремиться к использованию инструментов, автоматически генерирующих описания типов.
На сервере для этой цели можно воспользоваться комбинацией TypeORM/Typegoose и TypeGraphQL. Этого хватит для описания всех используемых типов. TypeORM/Typegoose позволит описать схему базы данных и соответствующие типы TypeScript. TypeGraphQL поможет в создании типов GraphQL и TypeScript.
Вот пример определения типов TypeORM (MongoDB) и TypeGraphQL в одном файле:
import { Field, ObjectType, ID } from 'type-graphql'
import {
Entity,
ObjectIdColumn,
ObjectID,
Column,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm'
@ObjectType()
@Entity()
export default class Policy {
@Field(type => ID)
@ObjectIdColumn()
_id: ObjectID
@Field()
@CreateDateColumn({ type: 'timestamp' })
createdAt: Date
@Field({ nullable: true })
@UpdateDateColumn({ type: 'timestamp', nullable: true })
updatedAt?: Date
@Field()
@Column()
name: string
@Field()
@Column()
version: number
}
GraphQL Code Generator также умеет генерировать множество различных типов. Мы используем этот инструмент для создания типов TypeScript на клиенте, а так же — хуков React, выполняющих обращения к серверу.
Если вы используете MobX для управления состоянием приложения, то вы, воспользовавшись парой строк кода, можете получить автоматически сгенерированные TS-типы. Если же вы, к тому же, пользуетесь и GraphQL, то вам стоит взглянуть на новый пакет — MST-GQL, который генерирует дерево состояния из GQL-схемы.
Совместное использование этих инструментов убережёт вас от переписывания больших объёмов кода и поможет избежать типичных ошибок.
Другие решения, такие как Prisma, Hasura и AWS AppSync, тоже могут помочь избежать дублирования объявлений типов. У использования подобных инструментов, конечно, есть свои плюсы и минусы. В создаваемых нами проектах подобные средства используются не всегда, так как нам нужно развёртывать код на собственных серверах организаций.
Прибегайте всегда, когда это возможно, к средствам автоматического генерирования кода
Если взглянуть на код, который создают без использования вышеописанных средств для автоматического генерирования кода, то окажется, что программистам постоянно приходится писать одно и тоже. Главный совет, который я могу дать по этому поводу, заключается в том, что нужно создавать сниппеты для всего, чем вы часто пользуетесь. Если вы часто вводите команду
console.log
— создайте сниппет, вроде cl
, который автоматически превращается в console.log()
. Если вы этого не сделаете и попросите меня помочь вам с отладкой кода, меня это сильно расстроит.Существует множество пакетов со сниппетами, но несложно и создавать собственные сниппеты. Например — с помощью Snippet generator.
Вот код, который позволяет добавить некоторые из моих любимых сниппетов в VS Code:
{
"Export default": {
"scope": "javascript,typescript,javascriptreact,typescriptreact",
"prefix": "eid",
"body": [
"export { default } from './${TM_DIRECTORY/.*[\\/](.*)$$/$1/}'",
"$2"
],
"description": "Import and export default in a single line"
},
"Filename": {
"prefix": "fn",
"body": ["${TM_FILENAME_BASE}"],
"description": "Print filename"
},
"Import emotion styled": {
"prefix": "imes",
"body": ["import styled from '@emotion/styled'"],
"description": "Import Emotion js as styled"
},
"Import emotion css only": {
"prefix": "imec",
"body": ["import { css } from '@emotion/styled'"],
"description": "Import Emotion css only"
},
"Import emotion styled and css only": {
"prefix": "imesc",
"body": ["import styled, { css } from ''@emotion/styled'"],
"description": "Import Emotion js and css"
},
"Styled component": {
"prefix": "sc",
"body": ["const ${1} = styled.${2}`", " ${3}", "`"],
"description": "Import Emotion js and css"
},
"TypeScript React Function Component": {
"prefix": "rfc",
"body": [
"import React from 'react'",
"",
"interface ${1:ComponentName}Props {",
"}",
"",
"const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = props => {",
" return (",
" <div>",
" ${1:ComponentName}",
" </div>",
" )",
"}",
"",
"export default ${1:ComponentName}",
""
],
"description": "TypeScript React Function Component"
}
}
Сэкономить время, помимо сниппетов, могут помочь генераторы кода. Их можно создавать самостоятельно. Мне для этого нравится использовать plop.
В Angular есть собственные встроенные генераторы кода. С помощью инструментов командной строки можно создать новый компонент, состоящий из 4 файлов, в которых представлено всё то, что можно ожидать найти в компоненте. Жаль, что в React нет такой вот стандартной возможности, но нечто подобное можно создать и самостоятельно, используя plop. Если каждый новый создаваемый вами компонент должен быть представлен в виде папки, содержащей файл с кодом компонента, файл с тестом и файл Storybook, генератор поможет создать всё это одной командой. Это во многих случаях значительно облегчает жизнь разработчика. Например, при добавлении новой возможности на сервер достаточно выполнить одну команду в командной строке. После этого автоматически будут созданы файлы сущности, сервисов и распознавателей, содержащие все необходимые базовые конструкции.
Ещё одна сильная сторона генераторов кода заключается в том, что они способствуют единообразию в командной разработке. Если все используют один и тот же plop-генератор, то код у всех будет получаться весьма однородным.
Автоматическое форматирование кода
Форматирование кода — простая задача, но её, к сожалению, не всегда решают правильно. Не тратьте время, вручную выравнивая код или вставляя в него точки с запятой. Используйте Prettier для автоматического форматирования кода при выполнении коммитов.
Итоги
В этом материале я рассказал вам о некоторых вещах, о которых мы узнали за годы работы, за годы проб и ошибок. Существует множество подходов к структурированию кодовой базы проектов. Но среди них нет такого, который можно назвать единственно правильным.
Самое главное, что я хотел до вас донести, заключается в том, что программисту стоит стремиться к простоте организации проектов, к их однородности, к использованию понятной структуры, с которой легко работать. Это упрощает командную разработку проектов.
Уважаемые читатели! Что вы думаете об идеях, касающихся разработки фулстек-приложений на JavaScript, изложенных в этом материале?