Создание сайтов в Белгороде. Как организовать большое приложение React и сделать его масштабируемым

В этой статье я расскажу о подходе, который использую при создании и структурировании больших приложений React. Одной из лучших особенностей React является то, что он не мешает вам и не является описательным, когда дело доходит до файловой структуры. Поэтому на Stack Overflow и подобных сайтах вы найдете множество вопросов о том, как структурировать приложения. Это очень самоуверенная тема, и нет единственно правильного пути. В этой статье я расскажу вам о решениях, которые я принимаю при создании приложений React: выбор инструментов, структурирование файлов и разбиение компонентов на более мелкие части.

Астронавт строит космическую колонию в форме логотипа React.

Инструменты сборки и линтинг

Некоторых из вас не удивит, что я большой поклонник webpack для создания своих проектов. Несмотря на то, что это сложный инструмент, большая работа, проделанная командой над версией 5, и новый сайт документации делают его намного проще. Как только вы войдете в веб-пакет и у вас появятся концепции в голове, у вас действительно появится невероятная сила, которую можно использовать. Я использую Babel для компиляции своего кода, включая специфичные для React преобразования, такие как JSX, и сервер webpack-dev-server для локального обслуживания моего сайта. Я лично не обнаружил, что горячая перезагрузка дает мне такую ​​​​большую пользу, поэтому я более чем доволен webpack-dev-server и его автоматическим обновлением страницы.

Я использую модули ES, впервые представленные в ES2015 (которые транспилируются через Babel) для импорта и экспорта зависимостей. Этот синтаксис существует уже некоторое время, и хотя webpack может поддерживать CommonJS (также известный как импорт в стиле Node), для меня имеет смысл начать использовать самые последние и лучшие. Кроме того, webpack может удалять мертвый код из пакетов с помощью модулей ES2015, что, хотя и не идеально, является очень удобной функцией, и она станет более полезной по мере того, как сообщество переходит к публикации кода в npm в ES2015. Большая часть веб-экосистемы перешла на модули ES, так что это очевидный выбор для каждого нового проекта, который я начинаю. Это также то, что ожидается от большинства инструментов, включая другие сборщики, такие как Rollup, если вы не хотите использовать webpack.

Структура папок

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

Код живет вsrc

Чтобы все было организовано, я буду помещать весь код приложения в папку с именем src. Он содержит только код, который попадает в ваш окончательный пакет, и ничего более. Это полезно, потому что вы можете указать Babel (или любому другому инструменту, работающему с кодом вашего приложения) просто просмотреть один каталог и убедиться, что он не обрабатывает код, который ему не нужен. Другой код, такой как файлы конфигурации веб-пакета, находится в папке с соответствующим названием. Например, моя структура папок верхнего уровня часто содержит:

— src => app code here

— webpack => webpack configs

— scripts => any build scripts

— tests => any test specific code (API mocks, etc.)

Как правило, единственными файлами, которые будут находиться на верхнем уровне, являются index.html, package.jsonи любые точечные файлы, такие как.babelrc. Некоторые предпочитают включать конфигурацию Babel package.jsonв...eslintrc.babelrc

Реагировать Компоненты

Когда у вас есть srcпапка, самое сложное — решить, как структурировать ваши компоненты. Раньше я помещал все компоненты в одну большую папку, например src/components, но обнаружил, что в больших проектах это очень быстро становится перегруженным.

Общей тенденцией является наличие папок для «умных» и «тупых» компонентов (также известных как «контейнерные» и «презентационные» компоненты), но лично я никогда не находил, чтобы явные папки работали на меня. Хотя у меня есть компоненты, которые условно делятся на «умные» и «глупые» (я расскажу об этом подробнее ниже), у меня нет конкретных папок для каждого из них.

Мы сгруппировали компоненты в зависимости от областей приложения, в которых они используются, вместе с coreпапкой для общих компонентов, которые используются повсюду (кнопки, заголовки, нижние колонтитулы — компоненты, которые являются общими и очень многоразовыми). Остальные папки сопоставляются с определенной областью приложения. Например, у нас есть папка с именем cart, которая содержит все компоненты, относящиеся к представлению корзины покупок, и папка с именем listings, которая содержит код для перечисления вещей, которые пользователи могут купить на странице.

Категоризация по папкам также означает, что вы можете избежать префикса компонентов с областью приложения, для которого они используются. Например, если бы у нас был компонент, который отображает общую стоимость корзины пользователя, а не вызывать его, CartTotalя бы предпочел использовать Total, потому что я импортирую его из cartпапки:

import Total from '.../cart/total’

// vs

import CartTotal from '.../cart/cart-total

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

Предпочитайте jsxрасширение заглавным буквам

Многие люди называют компоненты React с заглавной буквы в файле, чтобы отличить их от обычных файлов JavaScript. Таким образом, в приведенном выше импорте файлы будут CartTotal.js, или Total.js. Я предпочитаю использовать строчные буквы с дефисами в качестве разделителей, поэтому, чтобы различать, я использую.jsxрасширение для компонентов React. Поэтому я бы придерживался cart-total.jsx.

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

Какое бы соглашение об именовании вы ни выбрали, важно, чтобы вы его придерживались. Наличие комбинации соглашений в вашей кодовой базе быстро станет кошмаром по мере ее роста, и вам придется ориентироваться в ней. Вы можете применить это.jsxсоглашение, используя правило из eslint-plugin-react.

Один компонент React на файл

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

Обычно наши файлы React выглядят так:

import React from ‘react’

export default function Total (props) {

...

}

Например, в случае, если нам нужно обернуть компонент, чтобы подключить его к хранилищу данных Redux, полностью обернутый компонент становится экспортом по умолчанию:

import React, { Component, PropTypes } from ‘react’

import { connect } from ‘react-redux

export default function Total (props) {

...

}

export default connect (() => {... }) (Total)

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

Сохраняя компонент в качестве экспорта по умолчанию, легко импортировать компонент и знать, как его получить, вместо того, чтобы искать точное имя. Недостатком этого подхода является то, что человек, выполняющий импорт, может называть компонент как угодно. Еще раз, у нас есть соглашение для этого: импорт должен быть назван в честь файла. Поэтому, если вы импортируете total.jsx, компонент должен быть импортирован как Total. user-header.jsxстановится UserHeaderи так далее.

Стоит отметить, что правило «один компонент на файл» не всегда соблюдается. Если вы в конечном итоге создадите небольшой компонент, который поможет вам визуализировать часть ваших данных, и он будет использоваться только в одном месте, часто проще оставить его в том же файле, что и компонент, который его использует. Хранение компонентов в отдельных файлах обходится дорого: есть больше файлов, больше импорта и, как правило, больше возможностей для разработчика, так что подумайте, стоит ли оно того. Как и большинство предложений в этой статье, это правила с исключениями.

«Умные» и «тупые» компоненты React

Я кратко упомянул о разделении «умных» и «тупых» компонентов, и это то, чего мы придерживаемся в нашей кодовой базе. Хотя мы не распознаем его, разбивая их на папки, вы можете разделить наше приложение на два типа компонентов:

«умные» компоненты, которые манипулируют данными, подключаются к Redux и взаимодействуют с пользователем.

«тупые» компоненты, которым дается набор реквизитов и которые отображают некоторые данные на экране.

Вы можете прочитать больше о том, как мы стремимся к «тупым» компонентам, в моем блоге о функциональных компонентах без состояния в React. Эти компоненты составляют большую часть нашего приложения, и вы всегда должны отдавать предпочтение этим компонентам, если это возможно. С ними проще работать, меньше ошибок и их легче тестировать.

Даже когда нам приходится создавать «умные» компоненты, мы стараемся хранить всю логику JavaScript в отдельном файле. В идеале компоненты, которые должны манипулировать данными, должны передавать эти данные некоторому JavaScript, который может ими манипулировать. Делая это, код манипуляции можно протестировать отдельно от React, и вы можете смоделировать его по мере необходимости при тестировании вашего компонента React.

Избегайте больших renderметодов

Хотя этот пункт используется для обозначения renderметода, определенного в компонентах класса React, он остается в силе, когда речь идет о функциональных компонентах, поскольку вы должны следить за компонентом, отображающим необычно большой фрагмент HTML.

Одна вещь, к которой мы стремимся, — это иметь много небольших компонентов React, а не меньшее количество более крупных компонентов. Хорошим ориентиром, когда ваш компонент становится слишком большим, является размер функции рендеринга. Если он становится громоздким или вам нужно разделить его на множество более мелких функций рендеринга, возможно, пришло время подумать об абстрагировании функции.

Это не жесткое правило; вы и ваша команда должны получить представление о размере компонента, которым вы довольны, прежде чем извлекать больше компонентов, но размер функции компонента renderявляется хорошим мерилом. Вы также можете использовать количество реквизита или предметов в состоянии в качестве еще одного хорошего индикатора. Если компонент принимает семь разных реквизитов, это может быть признаком того, что он делает слишком много.

Всегда использоватьprop-type

React позволяет вам документировать имена и типы свойств, которые вы ожидаете от компонента, используя его пакет prop-types.

Объявляя имена и типы ожидаемых свойств, а также указывая, являются ли они необязательными, вы можете быть более уверены в том, что у вас есть правильные свойства при работе с компонентами, и вы можете тратить меньше времени на отладку, если вы забыли имени свойства или присвоили ему неправильный тип. Вы можете применить это с помощью правила PropTypes eslint-plugin-react.

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

Редукс

Мы также используем Redux во многих наших приложениях для управления данными в нашем приложении, и как структурировать приложения Redux — еще один очень распространенный вопрос, на который есть много разных мнений.

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

Вместо того, чтобы иметь reducers.jsи actions.js, где каждый содержит биты кода, связанные друг с другом, система Ducks утверждает, что имеет смысл сгруппировать связанный код вместе в один файл. Допустим, у вас есть магазин Redux с двумя ключами верхнего уровня userи файлом posts. Ваша структура папок будет выглядеть так:

ducks

— index.js

— user.js

— posts.js

index.jsбудет содержать код, который создает основной редуктор — возможно, используя combineReducersдля этого Redux — user.jsи posts.jsвы поместите весь код для тех, которые обычно выглядят так:

// user.js

const LOG_IN = ‘LOG_IN’

export const logIn = name => ({ type: LOG_IN, name })

export default function reducer (state = {}, action) {

...

}

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

Автономные модули JavaScript

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

Каждый раз, когда вы обнаружите, что ваш компонент переполнен бизнес-логикой, которую можно было бы вынести из компонента, я рекомендую это сделать. По моему опыту, мы обнаружили, что папка с именем libили servicesработает здесь хорошо. Конкретное имя не имеет значения, но папка, полная «компонентов, не связанных с React», — это то, что вам нужно.

Эти сервисы иногда экспортируют группу функций или объект связанных функций. Например, у нас есть services/local-storage.js, который предлагает небольшую оболочку для нативного window.localStorageAPI:

// services/local-storage.js

const LocalStorage = {

get () {},

set () {},

...

}

export default LocalStorage

Хранение вашей логики вне компонентов, подобных этому, имеет несколько действительно больших преимуществ:

вы можете протестировать этот код изолированно, без необходимости рендеринга каких-либо компонентов React.

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

Тесты

Как упоминалось выше, мы очень тщательно тестируем наш код и стали полагаться на платформу Jest от Facebook как на лучший инструмент для работы. Он очень быстрый, хорошо справляется с большим количеством тестов, быстро запускается в режиме просмотра и дает вам быструю обратную связь, а также поставляется с некоторыми удобными функциями для тестирования React из коробки. Ранее я много писал об этом на SitePoint, поэтому не буду вдаваться в подробности здесь, но расскажу о том, как мы структурируем наши тесты.

В прошлом я стремился иметь отдельную testsпапку, в которой хранились бы все тесты для всего. Так что если бы у вас было src/app/foo.jsx, вы бы tests/app/foo.test.jsxтоже. На практике по мере того, как приложение становится больше, становится сложнее найти нужные файлы, а если вы перемещаете файлы в src, вы часто забываете переместить их в test, и структуры теряют синхронизацию. Кроме того, если у вас есть файл tests, который нужно импортировать src, вы получите очень длинный импорт. Я уверен, что мы все сталкивались с этим:

import Foo from '.../... /... /src/app/foo’

С ними трудно работать и трудно исправить, если вы измените структуру каталогов.

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

— cart

— total.jsx

— total.spec.jsx

— services

— local-storage.js

— local-storage.spec.js

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

Вывод

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

Делитесь нашими материалами с друзьями!

 

 

Заказать разработку сайта