Привет, Хабр!
Давным-давно мы выпускали пилотный проект о стеке MEAN (Mongo, Express, Angular, Node), который нас в целом не разочаровал, однако, допечаток и обновлений мы в свое время решили не делать — в отличие от издательства Manning, которое эту книгу обновило. Тем не менее, мы продолжаем поиски в данном направлении и сегодня хотели бы поговорить с вами о родственном стеке MERN, где на клиенте располагается не Angular, а React. Слово предоставляется Тиму Смиту.
![](https://habrastorage.org/r/w780q1/webt/1d/hf/l7/1dhfl7yw9am620o13r-nz-i4il8.jpeg)
Преуведомление: весь код к этой статье находится здесь.
В этом руководстве по стеку MERN мы напишем простой блог, воспользовавшись React.js, Node.js, Express.js и MongoDB, чтобы расширить наш фулстек-арсенал.
Пожалуй, первым делом следует обсудить идею «стека» как таковую. Существует множество разных стеков, и все они – просто разные пути для достижения одной и той же цели. Создавая фулстек-приложение, мы предусматриваем в нем клиентскую часть, с которой будет взаимодействовать пользователь, и эта же клиентская часть будет опосредовать работу с сервером и базой данных – делая весь этот процесс как можно более простым и управляемым.
Можно сказать, что стек – это некоторый набор технологий, используемых для решения этой задачи.
Хотя, существует немало стеков, о которых стоило бы поговорить, некоторые из них сегодня популярнее других. Один из таких востребованных стеков называется MEAN, и он состоит из:
Сегодня речь пойдет о стеке MERN, который очень похож на MEAN за исключением того, что Angular.js здесь заменяется на React.js. Таким образом, мы сможем использовать MongoDB в качестве базы данных, Node.js и Express соответственно для сервера и маршрутизации, а React.js – для создания клиентской части, с которой будет взаимодействовать пользователь.
Прежде, чем перейти ко всяким тонкостям, давайте в общем виде рассмотрим, как сочетаются при работе все эти элементы. Лично мне потребовалось некоторое время, чтобы вникнуть в это, поскольку мой бэкграунд связан с PHP, где клиентская и серверная часть несколько перемешиваются.
1. Серверная часть (Node и Express.js) и клиентская часть (React.js)
Первым делом нужно понять, что серверная часть и клиентская часть – это отдельные сущности. Клиентская часть может находиться в одном репозитории с серверной, а может – в совершенно другом.
2. Для коммуникации используются терминалы API
Если вы уже задумываетесь, как подружить клиентскую и серверную часть – отвечу: это делается через API. API (интерфейс прикладных программ) создается на сервере, где у нас будут выведены «терминалы», через которые расположенное в клиентской части приложение сможет взаимодействовать с сервером.
Объясню на пальцах: представьте, что ваша левая рука – это серверная часть, а правая рука – клиентская часть.
Теперь сложите руки и переплетите пальцы так, словно пожимаете руку сами себе. Именно так и работают языки-шаблонизаторы. Они позволяют попросту отобразить некоторую разметку вместе с данными, сброшенными с серверами – и в них клиентская и серверная составляющая в значительной степени перекрывают друг друга.
Теперь разомкните руки. Растопырьте пальцы как можно шире и сделайте так, чтобы левая и правая рука соприкасались лишь кончиками пальцев. Вот так работает стек MERN. Серверная часть предоставляет терминалы (кончики пальцев с левой руки) для доступа к серверу, к которому клиентская часть направляет вызовы (через кончики пальцев правой руки) и через эти точки соприкосновения обменивается информацией с сервером (левой рукой).
Надеюсь, стало немного понятнее, а если нет – забудьте всю эту метафору, как будто я о ней и не упоминал.
Хотя, я и не собираюсь давать здесь пошаговых инструкций о сборке этого стека (это тема для отдельной статьи), я хотел бы рассмотреть различные элементы стека, которые в нем обычно используются или могут использоваться. Сам читал несколько руководств, где рассказывалось, как настроить сервер, но не объяснялось, почему для этого используются именно данные конкретные библиотеки.
После того, как мы создадим файл app.js, потребуется установить ряд пакетов. Ниже перечислены наиболее распространенные пакеты, которые мне ранее доводилось применять в моих проектах с Express.js – возможно, они пригодятся и вам.
Разумеется, существует и множество других пакетов, но, по моему опыту, именно эти библиотеки используются чаще всего.
Итак, разобравшись с некоторыми наиболее активно используемыми пакетами, давайте рассмотрим код. Для начала – наш сервер:
![](https://habrastorage.org/r/w1560/webt/xk/g7/f_/xkg7f_3zeltvgqsj-knjstppros.png)
Это простой API-сервер. Как видите, он оснащен базовым функционалом CRUD (Создать-Прочитать-Обновить-Удалить) – ничего сверхъестественного. Присмотревшись к нему внимательнее, увидим, что здесь используется
Также вы могли заметить, что я только что указал mongoose на мой собственный сервер mongodb, установленный у меня на компьютере. Чтобы такой механизм работал правильно, MongoDB должна быть установлена у вас на компьютере и работать. Если она не работает – просто откройте окно консоли и введите следующую команду:
![](https://habrastorage.org/r/w1560/webt/yz/fr/o2/yzfro2ns9az9yqno2kc522rdnfa.png)
Она запустит сервер MongoDB на вашей локальной машине. Поскольку все это делается локально, вы не сможете увидеть моих постов, если запустите код из репозитория. Придется самим писать новый контент. Если вам нужен контент-заглушка, рекомендую отличный генератор Fillerama.io, нарезающий текст из некоторых моих любимых фильмов и телешоу.
Если вам интересно самим протестировать сервер, то можете запустить его следующей командой:
![](https://habrastorage.org/r/w1560/webt/83/fr/1z/83fr1z4cgcdnjrx9up44nk5enxe.png)
После того, как сервер запустится и сообщит нам, что работает на порту 3333, а также что он подключился к MongoDB, можно будет открыть Postman и протестировать там наши маршруты. Что касается вариантов GET, можно просто вставить маршрут и нажать «Send» (отправить). В случае с POST придется выбрать «Body» (Тело) и заполнить поля для заголовка и основного содержимого.
Теперь, когда мы настроили и запустили наш сервер, можно приступать к работе над клиентом, с которым будут взаимодействовать наши пользователи. Клиент будет написан на React.js, и это можно сделать несколькими различными способами.
Первый – просто добавить все необходимые библиотеки для клиентской части (react, react-dom, react-router, т.д.) все в тот же файл
Второй и (на мой взгляд) более оптимальный подход – создать отдельный репозиторий для серверной части и отдельный – для клиентской. Мы по-прежнему без каких-либо проблем вполне можем клонировать репозиторий клиентской части в директорию с нашим проектом, только надо удостовериться, что клиентская часть указана у нас в файле
![](https://habrastorage.org/r/w1560/webt/o8/sy/h4/o8syh4xj4rxbc5te5wwk_ax_7bc.png)
Добавляя папку
Как именно будет спроектировано ваше фулстек-приложение – зависит исключительно от вас. Просто мне кажется, что, если вести отдельные репозитории для клиентской и серверной части, структура приложения будет чуть более аккуратной.
Теперь, разобравшись с организацией проекта, поговорим, собственно, о клиентском коде. Ниже приведен мой файл
А вот как будет выглядеть скриншот главной страницы нашего приложения:
![](https://habrastorage.org/r/w1560/webt/rp/j-/yz/rpj-yzfnehkwotobufkxqxf3zzi.png)
Как видите, в
Мы используем Axios для выполнения http-вызовов к терминалам API, а затем пускаем в ход React.js для отображения данных так, как нам это угодно. В этом посте я приведу код Index.js, чтобы было понятнее, как все это работает вместе.
![](https://habrastorage.org/r/w1560/webt/9j/dp/wo/9jdpwo-gh8ifomloyx0cibju1mi.png)
В вышеприведенном коде задействован компонент класса, позволяющий нам использовать состояние и методы жизненного цикла. Это необходимо, поскольку вызовы Axios обязательно должны совершаться в методе жизненного цикла
Давайте убедимся, что поисковые роботы нормально читают наше приложение React.js.
Закругляясь, я бы хотел вкратце поговорить о рендеринге. Если запустить наш сайт и перейти непосредственно к какому-нибудь посту в блоге, то возможны некоторые проблемы с отображением контента. В таком случае просматривать сайт будет неудобно не только пользователям, но и поисковым роботам, индексирующим контент. Чтобы обойти данную проблему, рекомендую воспользоваться инструментами вроде Gatsby js или Next js. Два этих решения отличаются друг от друга, но оба могут пригодиться, в зависимости от того, что именно вам требуется.
Gatsby js – это генератор статических сайтов. Можете написать сайт на React.js, а затем Gatsby превратит его в статические файлы во время сборки, в результате чего сайт станет супербыстрым. К Gatsby прилагается множество полезных плагинов, благодаря которым инструмент становится практически универсальным. Кстати, мой сайт сделан именно при помощи Gatsby.js! Поскольку статические файлы создаются во время сборки, сайт необходимо пересобирать всякий раз, когда исходный контент изменяется.
Next.js, в свою очередь – это серверный компонент для отображения сайтов React.js. В него встроено множество полезных возможностей, в частности, маршрутизация, разделение кода (code splitting), оформленные компоненты и многое другое. Серверный рендеринг означает, что данные будут обновляться автоматически, как это делается на сервере, но, перед выводом в окне браузера будут проходить этап рендеринга. Именно поэтому никаких проблем с отображением данных пользователю быть не должно, и поисковые роботы также выполнят свою работу без проблем.
Существует и множество других решений такого рода, но об этих двух мне приходилось слышать больше всего, и при работе над этим проектом я использовал именно их. Оба превосходно документированы, поэтому не составляет труда быстро разобраться с обоими и приступать к делу.
Надеюсь, эта статья помогла вам немного точнее представить, как работает стек MERN. В нем мы всего лишь берем MongoDB, Express.js и Node.js и создаем из них сервер, который уже предоставляет терминалы API, через которые наше приложение на React.js может обращаться к данным. Итак, теперь вы многое понимаете, настало время делать великие дела!
Давным-давно мы выпускали пилотный проект о стеке MEAN (Mongo, Express, Angular, Node), который нас в целом не разочаровал, однако, допечаток и обновлений мы в свое время решили не делать — в отличие от издательства Manning, которое эту книгу обновило. Тем не менее, мы продолжаем поиски в данном направлении и сегодня хотели бы поговорить с вами о родственном стеке MERN, где на клиенте располагается не Angular, а React. Слово предоставляется Тиму Смиту.
![](https://habrastorage.org/webt/1d/hf/l7/1dhfl7yw9am620o13r-nz-i4il8.jpeg)
Преуведомление: весь код к этой статье находится здесь.
В этом руководстве по стеку MERN мы напишем простой блог, воспользовавшись React.js, Node.js, Express.js и MongoDB, чтобы расширить наш фулстек-арсенал.
Что такое «стек»?
Пожалуй, первым делом следует обсудить идею «стека» как таковую. Существует множество разных стеков, и все они – просто разные пути для достижения одной и той же цели. Создавая фулстек-приложение, мы предусматриваем в нем клиентскую часть, с которой будет взаимодействовать пользователь, и эта же клиентская часть будет опосредовать работу с сервером и базой данных – делая весь этот процесс как можно более простым и управляемым.
Можно сказать, что стек – это некоторый набор технологий, используемых для решения этой задачи.
Хотя, существует немало стеков, о которых стоило бы поговорить, некоторые из них сегодня популярнее других. Один из таких востребованных стеков называется MEAN, и он состоит из:
- MongoDb
- Express.js
- Angular.js
- Node.js
Сегодня речь пойдет о стеке MERN, который очень похож на MEAN за исключением того, что Angular.js здесь заменяется на React.js. Таким образом, мы сможем использовать MongoDB в качестве базы данных, Node.js и Express соответственно для сервера и маршрутизации, а React.js – для создания клиентской части, с которой будет взаимодействовать пользователь.
Как ведется разработка при помощи этого стека?
Прежде, чем перейти ко всяким тонкостям, давайте в общем виде рассмотрим, как сочетаются при работе все эти элементы. Лично мне потребовалось некоторое время, чтобы вникнуть в это, поскольку мой бэкграунд связан с PHP, где клиентская и серверная часть несколько перемешиваются.
1. Серверная часть (Node и Express.js) и клиентская часть (React.js)
Первым делом нужно понять, что серверная часть и клиентская часть – это отдельные сущности. Клиентская часть может находиться в одном репозитории с серверной, а может – в совершенно другом.
2. Для коммуникации используются терминалы API
Если вы уже задумываетесь, как подружить клиентскую и серверную часть – отвечу: это делается через API. API (интерфейс прикладных программ) создается на сервере, где у нас будут выведены «терминалы», через которые расположенное в клиентской части приложение сможет взаимодействовать с сервером.
Объясню на пальцах: представьте, что ваша левая рука – это серверная часть, а правая рука – клиентская часть.
Теперь сложите руки и переплетите пальцы так, словно пожимаете руку сами себе. Именно так и работают языки-шаблонизаторы. Они позволяют попросту отобразить некоторую разметку вместе с данными, сброшенными с серверами – и в них клиентская и серверная составляющая в значительной степени перекрывают друг друга.
Теперь разомкните руки. Растопырьте пальцы как можно шире и сделайте так, чтобы левая и правая рука соприкасались лишь кончиками пальцев. Вот так работает стек MERN. Серверная часть предоставляет терминалы (кончики пальцев с левой руки) для доступа к серверу, к которому клиентская часть направляет вызовы (через кончики пальцев правой руки) и через эти точки соприкосновения обменивается информацией с сервером (левой рукой).
Надеюсь, стало немного понятнее, а если нет – забудьте всю эту метафору, как будто я о ней и не упоминал.
Наша серверная часть из Node.js и Express.js
Хотя, я и не собираюсь давать здесь пошаговых инструкций о сборке этого стека (это тема для отдельной статьи), я хотел бы рассмотреть различные элементы стека, которые в нем обычно используются или могут использоваться. Сам читал несколько руководств, где рассказывалось, как настроить сервер, но не объяснялось, почему для этого используются именно данные конкретные библиотеки.
После того, как мы создадим файл app.js, потребуется установить ряд пакетов. Ниже перечислены наиболее распространенные пакеты, которые мне ранее доводилось применять в моих проектах с Express.js – возможно, они пригодятся и вам.
- Express.js – фреймворк для создания веб-приложений. В него встроен функционал для решения многих проблем, в частности, для налаживания маршрутизации.
- Mongoose – это администратор объектных данных (ODM), обеспечивающий взаимодействия между приложением express.js и базой данных MongoDB.
- BodyParser – библиотека, позволяющая приложению express.js читать тело (то есть, содержание) входящих запросов.
- DotENV – позволяет использовать файлы с расширением .env для работы с конфиденциальными данными.
- Passport.js – обеспечивает в нашем приложении аутентификацию, причем, предоставляет несколько разных способов аутентификации.
- Validator – простая валидация многих типов данных
- bCrypt– шифрование конфиденциальных данных, например, паролей
- Nodemon — «горячая перезагрузка» для нашего node-сервера на случай изменения ситуации; благодаря Nodemon, можно не останавливать и не перезапускать сервер после внесения любых изменений.
Разумеется, существует и множество других пакетов, но, по моему опыту, именно эти библиотеки используются чаще всего.
Итак, разобравшись с некоторыми наиболее активно используемыми пакетами, давайте рассмотрим код. Для начала – наш сервер:
![](https://habrastorage.org/webt/xk/g7/f_/xkg7f_3zeltvgqsj-knjstppros.png)
Это простой API-сервер. Как видите, он оснащен базовым функционалом CRUD (Создать-Прочитать-Обновить-Удалить) – ничего сверхъестественного. Присмотревшись к нему внимательнее, увидим, что здесь используется
res.json()
для предоставления данных вывода по конкретному URL – то есть, для вывода не применяется HTML или другой шаблон. Именно так мы строим наши API, открывая доступ к данным для React.js.Также вы могли заметить, что я только что указал mongoose на мой собственный сервер mongodb, установленный у меня на компьютере. Чтобы такой механизм работал правильно, MongoDB должна быть установлена у вас на компьютере и работать. Если она не работает – просто откройте окно консоли и введите следующую команду:
![](https://habrastorage.org/webt/yz/fr/o2/yzfro2ns9az9yqno2kc522rdnfa.png)
Она запустит сервер MongoDB на вашей локальной машине. Поскольку все это делается локально, вы не сможете увидеть моих постов, если запустите код из репозитория. Придется самим писать новый контент. Если вам нужен контент-заглушка, рекомендую отличный генератор Fillerama.io, нарезающий текст из некоторых моих любимых фильмов и телешоу.
Если вам интересно самим протестировать сервер, то можете запустить его следующей командой:
![](https://habrastorage.org/webt/83/fr/1z/83fr1z4cgcdnjrx9up44nk5enxe.png)
После того, как сервер запустится и сообщит нам, что работает на порту 3333, а также что он подключился к MongoDB, можно будет открыть Postman и протестировать там наши маршруты. Что касается вариантов GET, можно просто вставить маршрут и нажать «Send» (отправить). В случае с POST придется выбрать «Body» (Тело) и заполнить поля для заголовка и основного содержимого.
Замечание о клиентской части
Теперь, когда мы настроили и запустили наш сервер, можно приступать к работе над клиентом, с которым будут взаимодействовать наши пользователи. Клиент будет написан на React.js, и это можно сделать несколькими различными способами.
Первый – просто добавить все необходимые библиотеки для клиентской части (react, react-dom, react-router, т.д.) все в тот же файл
package.json
, куда мы записывали серверные библиотеки. В данном проекте я поступил именно так, но должен отметить, что не считаю такой вариант оптимальным. Думаю, что по мере роста нашего проекта база кода будет становиться все более запутанной, и, если воспользоваться именно таким методом, то в перспективе работа с ней усложнится. Я предпочел этот путь в описываемом приложении именно потому, что точно знаю: оно не вырастет и вообще совершенно не изменится. Данное приложение пишется исключительно в демонстрационных целях.Второй и (на мой взгляд) более оптимальный подход – создать отдельный репозиторий для серверной части и отдельный – для клиентской. Мы по-прежнему без каких-либо проблем вполне можем клонировать репозиторий клиентской части в директорию с нашим проектом, только надо удостовериться, что клиентская часть указана у нас в файле
.gitignore
. Например, в файловой структуре для данного приложения есть директория client
, где лежит весь код клиентской части. Мы могли бы вынести его в совершенно отдельный репозиторий, а затем просто добавить следующую запись в файл .gitignore
нашего репозитория с серверной частью:![](https://habrastorage.org/webt/o8/sy/h4/o8syh4xj4rxbc5te5wwk_ax_7bc.png)
Добавляя папку
client
в файл .gitignore
, мы гарантируем, что система не будет воспринимать эту папку как второй репозиторий в проекте. Кроме того, такой подход облегчает работу по перепроектиованию или замене клиентской части, так как серверная часть при этом абсолютно не затрагивается.Как именно будет спроектировано ваше фулстек-приложение – зависит исключительно от вас. Просто мне кажется, что, если вести отдельные репозитории для клиентской и серверной части, структура приложения будет чуть более аккуратной.
Создаем клиентскую часть на React.js
Теперь, разобравшись с организацией проекта, поговорим, собственно, о клиентском коде. Ниже приведен мой файл
app.js
для приложения на React.js, и я не стану вставлять в этот пост код для каждого компонента, а просто оставлю ссылку на репозиторий и объясню, что делает каждый из компонентов React.А вот как будет выглядеть скриншот главной страницы нашего приложения:
![](https://habrastorage.org/webt/rp/j-/yz/rpj-yzfnehkwotobufkxqxf3zzi.png)
Как видите, в
app.js
абсолютно ничего сложного. Здесь есть <Router>, позволяющий задавать маршруты в React.js, по которым отображаются различные компоненты на основе их URL. А вот и другие компоненты, которые будут использоваться в нашем приложении React.js:- Header – Навигационная панель, расположенная в верхней части экрана
- Index – Списки доступных записей из блогов
- New – Форма, через которую пользователь может создать новый пост
- Single – Отображает конкретную запись блога на основе ее
id
- Edit – Форма, через которую пользователь может обновить запись блога, находимую по
id
Мы используем Axios для выполнения http-вызовов к терминалам API, а затем пускаем в ход React.js для отображения данных так, как нам это угодно. В этом посте я приведу код Index.js, чтобы было понятнее, как все это работает вместе.
![](https://habrastorage.org/webt/9j/dp/wo/9jdpwo-gh8ifomloyx0cibju1mi.png)
В вышеприведенном коде задействован компонент класса, позволяющий нам использовать состояние и методы жизненного цикла. Это необходимо, поскольку вызовы Axios обязательно должны совершаться в методе жизненного цикла
componentDidMount()
. Следует отметить, что я получал ошибку CORS, когда пытался делать вызовы к моему локальному API. Для решения этой проблемы я добавил несколько заголовков к файлу server.js в моем сервере Express – и все заработало. Этот код отмечен в комментариях к файлу server.js.Давайте убедимся, что поисковые роботы нормально читают наше приложение React.js.
Закругляясь, я бы хотел вкратце поговорить о рендеринге. Если запустить наш сайт и перейти непосредственно к какому-нибудь посту в блоге, то возможны некоторые проблемы с отображением контента. В таком случае просматривать сайт будет неудобно не только пользователям, но и поисковым роботам, индексирующим контент. Чтобы обойти данную проблему, рекомендую воспользоваться инструментами вроде Gatsby js или Next js. Два этих решения отличаются друг от друга, но оба могут пригодиться, в зависимости от того, что именно вам требуется.
Gatsby js – это генератор статических сайтов. Можете написать сайт на React.js, а затем Gatsby превратит его в статические файлы во время сборки, в результате чего сайт станет супербыстрым. К Gatsby прилагается множество полезных плагинов, благодаря которым инструмент становится практически универсальным. Кстати, мой сайт сделан именно при помощи Gatsby.js! Поскольку статические файлы создаются во время сборки, сайт необходимо пересобирать всякий раз, когда исходный контент изменяется.
Next.js, в свою очередь – это серверный компонент для отображения сайтов React.js. В него встроено множество полезных возможностей, в частности, маршрутизация, разделение кода (code splitting), оформленные компоненты и многое другое. Серверный рендеринг означает, что данные будут обновляться автоматически, как это делается на сервере, но, перед выводом в окне браузера будут проходить этап рендеринга. Именно поэтому никаких проблем с отображением данных пользователю быть не должно, и поисковые роботы также выполнят свою работу без проблем.
Существует и множество других решений такого рода, но об этих двух мне приходилось слышать больше всего, и при работе над этим проектом я использовал именно их. Оба превосходно документированы, поэтому не составляет труда быстро разобраться с обоими и приступать к делу.
Заключительные мысли о стеке MERN
Надеюсь, эта статья помогла вам немного точнее представить, как работает стек MERN. В нем мы всего лишь берем MongoDB, Express.js и Node.js и создаем из них сервер, который уже предоставляет терминалы API, через которые наше приложение на React.js может обращаться к данным. Итак, теперь вы многое понимаете, настало время делать великие дела!
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
А вы как думаете?
73.41% MERN127
8.67% MEAN15
15.03% MEVN26
8.67% LAMP15
3.47% Только Крокфорд!6
Проголосовали 173 пользователя. Воздержались 35 пользователей.