Содержание

  • Предисловие

  • Идея сервиса

  • Стек

  • Архитектура приложения

  • Заключение


Предисловие

Добрейшего времени суток %HABRUSER% и проходящим мимо, в первую очередь я очень рад что ты открыл эту статью, без шуток :)

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


Идея сервиса

Было 2 мотивации для создания приложения. Спорим что почти каждый из нас когда то говорил себе:

Уже завтра начну заниматься спортом (С)

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

Мотивация #2 была технического характера, давно хотелось разобраться в SSR на ReactJS ( если быть точней в создании гибридного приложения SSR & SPA ).

Посидев немного в среде этих мотиваций родилась идея создания сервиса, в котором всяк сюда входящий найдет компаньона для бега в real-time.

Так как проект некоммерческий (а значит времени и средств на его реализацию не слишком много) решил что стоит начать с MVP и допиливать уже в продакшне функциональность.

На момент создания MVP приложение должно уметь

  1. Находить напарника (или напарников до 10 участников) для бега в реальном времени (то есть здесь и сейчас).

  2. Давать возможность для поиска напарника(ов) по выбранному району, а так же выбирать начальную и конечную точку забега.

  3. Отображать список активных комнат.

  4. Уметь хорошо проиндексировать себя в поисковике.

  5. Делать серверный роутинг

  6. Иметь 2 страницы (Главная и новости для ознакомления с тем, что поменялось на проекте для пользователя)


Стек

Так как я фронтенд реактус разрабус, стек будет соответствовать:

  • ReactJS (Последних версий, будет писать на хуках)

    • Typescript (Да это увеличит время разработки из-за дополнительной разработки типов, но приложение может сильно расширяться, поэтому типизация нам пригодится во избежания 1 + "1" = 11 и других подобных приколов слабой типизации JS )

    • Sass (Был выбор между LESS и PCSS, PCSS собирается быстрей из-за того что плагины ставятся разработчиком и нет ничего лишнего, но sass как то родней как по мне скорость компиляции не столь критична на данных момент)

    • react-router-dom (Роутинг на клиенте)

  • NodeJS (Наш сервер)

    • express (Для упрощения серверной разработки)

    • Socket.IO (Наше все, создание комнат, real-time поиск, отображение комнат на клиенте)

    • react-router (Роутинг на сервере)

  • Webpack (Собственно собирает наш проект)

    • cross-env (Нам нужны будут системные переменные, для того чтобы определять как собирать проект для разработки или прода и другие)

    • nodemon (Перезапускает наш сервер при пересборке, чтобы ручками не делать каждый раз при каждом чихе)

Так же другие плагины которые есть в package.json нашего проекта. От статью к статье плагины могут добавляться | удаляться.

Почему не хочу использовать NextJS

NextJS это фреймворк, а любой фреймворк навязывает много правил в разработке и проблемы при каких то кастомных решений (это неплохо но на данный момент противоречит моей 2 мотивации), мне хотелось бы чтобы проект был максимально гибкий, а так же разобраться в работе под капотом гибридного приложения.


Архитектура приложения

Как ты знаешь React это JS библиотека для создания пользовательских интерфейсов и работает она по принципу SPA, то генерация нашей страницы происходит в браузере после получения JS бандла, тк нам нужна наилучшая индексация поисковыми роботами мы будем использовать рендеринг на стороне сервера (и да сейчас 2021 год и многие поисковые роботы уже умеют собирать контент в браузере, однако не все и JS бандл может быть тяжелый, что может повлиять на качество индексации)

Принцип работы SSR (гибридного) - Сервер собирает у себя на стороне React приложение и при запросе отдает в виде строки, при получении браузером происходит восстановление нод и обработчиков и вставка в DOM (если по умному гидратация, делать за нас это будет метод hydrate).

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

// Директория клиентской стороны приложения
{
	client: {
  	src: {
    	components: "Директория для переиспользуемых компонентов",
      layouts: `
      	Директория с какими то общими частями приложения,
        пригодится когда вместо того, чтобы для каждой страницы приложения,
        вставлять каждый раз компоненты Header, Footer и т.д
        Мы просто вызовем нашу тему и передадим композицией наш контент
        для данной страницы, типа следующего:
        <LayoutDefault>
        	<div>Привет, я контент для страницы о нас</div>
        </LayoutDefault>
      `,
      pages: {
      	MainPage: "Директория главной страницы",
        NewsPage: "Директория страницы с новостями",
        index.ts: `
        	Файл, в который импортируются все страницы приложения
          для более удобного последующего использования где-либо,
          через именованный импорт, содержимое файла следующее:
          export {default as MainPage} from "./MainPage"
        `
      }
    },
    stylesCommon: {
    	app.css: "Общие стили нашего приложения"
    },
    App.tsx: `
    	Файл в котором лежит React приложение
      и который будет собираться сервером
    `,
    index.tsx: `
    	Корневой файл в котором импортируется
      вышеописанный App.tsx, в нем вызывается
      метод hydrate, для которого необходимо указать
      элемент в DOM в который будет отрендерено приложение,
      поэтому этот файл для сборки с сервера нам не подойдет,
      тк на сервере нет DOM
    `
  },
}

Не устал читать структуру клиентской стороны?) Я вот очень устал ее писать, однако давай перейдем к архитектуре сервера:

Тут все максимально просто :)

index.tsx

// Директория сервера
{
  server: {
  	index.tsx: "Тадааам, пока нам хватит"
  }
}

Теперь вебпак конфиги (именно конфиг(И), потому что мы должны собрать как клиент, так и сервер)

// Директория вебпак конфигов
{
	webpackConfs: {
  	webpack.client.js: "Конфиг под сборку клиента",
    webpack.server.js: "Конфиг под сборку сервера",
    webpack.common.js: `
    	Тк мы собираем наше приложение на сервере,
      конфиги должны быть максимально схожи,
      поэтому более общие части вынесем сюда
      и будем импортировать в клиентский и серверный конфиг
    `
  }
}

Еще есть файлы корневой директории, такие как конфиг .TS, package.json, однако расскажу о них в следующей статье когда мы перейдем уже к более детальному процессу разработки.

Заключение и источники

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

Если у вас остались какие то вопросы или допустил какие то неточности или статью можно как то улучшить, пожалуйста пишите, буду рад почитать ваши комментарии :)

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


  1. korsetlr473
    13.11.2021 14:49

    Добрый день как отличить nodejs пакеты которые работают только с ssr+nodejs от обычных?


    1. FrogFog Автор
      13.11.2021 18:57

      Доброго времени суток )

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

      rules: [
      		...config.module.rules,
      
       {
      	  test: /\.s[ac]ss$/i,
      		use: 'null-loader'
      	}
      ]

      PS То есть сделали так, чтобы в нашу серверную сборку не попали исходники css, которые генерируются из SCSS.

      Если для вашей серверной сборки нужны эти пакеты вы их оставляете, если нет, то делаете для них null-loader. Надеюсь ответил на ваш вопрос, однако если нет, то более подробно о настройке webpack конфигах я постараюсь более подробно написать в следующей, где то на этой неделе!


  1. TretyakovDV
    13.11.2021 18:44
    +1

    начало заинтриговало) жду продолжения. так держать!


    1. FrogFog Автор
      14.11.2021 00:12
      +1

      Спасибо большое, постараюсь следующую часть сделать более информативной )


  1. pinkskin
    13.11.2021 18:57

    А чем next не устроил?


    1. FrogFog Автор
      14.11.2021 00:14

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


    1. FrogFog Автор
      14.11.2021 00:15

      Слабое интернет соединение задублировало комментарий (этот комментарий лишний)


  1. JanKaban2505
    14.11.2021 00:04

    • поэтому типизация нам пригодится во избежания 1 + "1" = 11 и других подобных приколов слабой типизации JS - ???, это как TS защитит от 1 + "1" = 11 и других подобных приколов???, наверно имелось ввиду от невнимательности защитит, TS приносит аннотацию типов при написании кода, но никакой "типизации" не приносит в код JS, как в старой сказке карета превращалась в тыкву в полночь, так же и TS превращается в JS...


    1. FrogFog Автор
      14.11.2021 00:11

      @JanKaban2505

      Ну насчет защитит от невнимательности спорно, тк .TS проверяет типы только на этапе компиляции кода в JS (однако если в реалтайме кто то изменит тип переменной, TS от этого не защитит), хотя возможно вы и имели это ввиду )

      Попытался максимально просто написать о TS )