Ещё один пресет: создаём демо с помощью Vite и Express

Ещё один пресет: создаём демо с помощью Vite и Express

Olga Krylova

Какое-то время назад я писала про различные способы способы обеспечить двустороннюю связь между клиентом и сервером: Long Polling, WebSockets и Server-Sent Events. Во всех этих постах были небольшие Live Demo, где можно потрогать реализацию. В этой статье я расскажу, что интересного есть этих реализациях и как собрать с нуля подобный небольшой, но функциональный проект.

Требования

  • Легковесный и современный клиент
  • Простой сервер, который будет раздавать статику и предоставлять некоторое API для работы с данными
  • Решения для клиента и сервера должны иметь широкую поддержку
  • Клиент и сервер должны существовать вместе и поддерживать локально hot reload на любые изменения как в коде клиента, так и сервера

Решение

На основе первых трёх требований были выбраны библиотеки Vite и Express. Vite был выбран как самый новый и прогрессивный вариант, а Express как самое лёгкое решение для бэкенда на Node.js, для которого существует множество вспомогательных библиотек.

Реализация

Итак, для начала инициализируем наш проект, используя npm:

npm create vite@latest my-demo -- --template react-ts

У Vite есть много шаблонов, но в нашем проекте будем использовать React + TypeScript. После выполнения этой команды будет создана папка my-demo, в которой будет лежать исходный код клиентской части приложения. Далее все действия будут выполняться именно в этой директории.

Переходим к созданию сервера. Добавляем в зависимости необходимые пакеты для самого сервера, настройки CORS и логирования:

npm i express cors morgan

Создаём в корне проекта папку server, в которой будет лежать исходный код нашего сервера. Добавляем в неё файл index.mjs со следующим содержимым:

import express from 'express';
import cors from 'cors';
import path from 'path';
import morgan from'morgan';

const app = express(); // Инициализируем приложение

const port = 8001;

// Настраиваем cors
app.use(
  cors({
    origin: '*',
    methods: ['GET', 'POST'],
    allowedHeaders: ['Content-Type', 'Cache-Control', 'Connection'],
  }),
);

// Добавляем базовое логирование
app.use(morgan('combined'));

// Настраиваем раздачу статики
app.use(express.static(path.resolve('./dist')));

// Раздаём статику клиента
app.get('/', (_, res) => {
    res.sendFile(path.resolve('./dist/index.html'));
});

// Запускаем сервер
app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
});

На сервере не будет использоваться типизация, чтобы не усложнять проект, при желании её можно добавить. Если запустить этот скрипт командой

node server/index.mjs

то сервер будет доступен по адресу http://localhost:8001. Пока у нас нет никакого API, но настроена раздача статики клиента из папки dist, в которую по умолчанию собирается приложение Vite. Чтобы это увидеть, можно собрать клиент командой

npm run build

Затем запустить сервер и по адресу / будет доступна клиентская сборка со всеми стилями и скриптами.

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

Следующим шагом добавим возможность использовать hot reload на сервер при изменении любых его файлов. Для этого добавим в проект библиотеку nodemon:

npm i nodemon

Затем добавим в корень проекта файл с конфигурацией nodemon.json:

{
    "watch": ["server"],
    "exec": "node ./server/index.mjs"
}

И последним шагом добавим в package.json скрипт, который будет запускать сервер:

"scripts" {
  ...
  "start:server": "nodemon --config nodemon.json"
}

Теперь, если мы запустим сервер командой npm run start:server, то при каждом изменении в файлах сервера приложение будет перезапускаться. Однако остаются 2 проблемы: клиент всё ещё нужно собирать руками и перезапускать сервер, а также браузер никак не реагирует на эти изменения.

Чтобы решить первую проблему, достаточно использовать режим watch в сборке Vite, а также запускать клиент и сервер параллельно друг с другом. Для этого добавим установим пакет npm-run-all, который позволит параллельно запускать клиент и сервер:

npm i npm-run-all

И добавим ещё 2 скрипта в package.json:

"scripts": {
  ...
  "start:client": "vite build --watch",
  "start:dev": "run-p start:client start:server"
}

Если запустить команду npm run start:dev, то будут запущены и клиент и сервер, а также клиент будет пересобираться при любых его изменениях. Остается лишь добавить перезагрузку страницы в браузере при любых изменениях приложения. Для этого будем использовать browser-sync:

npm i browser-sync

Добавляем конфигурацию в файл browser-sync.json, которая будет создавать прокси для нашего сервера и указывать на то, что необходимо следить за изменениями клиента и сервера и обновлять страницу в браузере:

{
  "port": 8000,
  "proxy": "localhost:8001",
  "files": ["dist", "server"],
  "watchEvents": ["add", "change", "unlink", "addDir", "unlinkDir"],
  "open": false
}

Затем добавляем новый скрипт для запуска синхронизации и модифицируем общий скрипт для запуска приложения:

"scripts": {
  ...
  "start:sync": "browser-sync start --config browser-sync.json",
  "start:dev": "run-p start:client start:server start:sync"
}

Теперь после запуска команды npm run start:dev приложение будет доступно по адресу http://localhost:8000, а также будет происходить его перезагрузка при любых изменениях локальных файлов.

Чтобы сделать пресет ещё лучше, можно дополнительно настроить все необходимые линтеры. О том, как это сделать, можно прочитать в нашей предыдущей статье.

Недостатки решения

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

Код

Итоговый шаблон приложения можно найти в нашем репозитории.


Бонус: деплой приложения

Для деплоя будем использовать сервис render.com. В нём есть как платные тарифы, так и бесплатный, который в целом может подойти для небольших демо.

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

Далее необходимо создать новый проект, выбрав пункт "Web Service":

Создание нового проекта

К этому проекту нужно прикрепить репозиторий. Для этого нужно дать сервису доступ к необходимому репозиторию, выбрав пункт "Configure GitHub":

Конфигурация источника исходного кода

Затем в интерфейсе GitHub нужно выбрать репозиторий. После этого он появится в списке, нужно его выбрать для дальнейшей настройки деплоя и нажать кнопку "Connect":

Выбор репозитория

Далее необходимо указать команды для установки зависимостей и запуска проекта и в самом низу страницы запустить деплой кнопкой "Deploy Web Service".

Команды для установки зависимостей и сборки клиента
Команда для запуска сервера

После окончания деплоя наше приложение будет доступно по сгенерированному сервисом адресу:

Вот и всё, мы восхитительны! Приложение теперь доступно по адресу https://demo-template-mdeq.onrender.com/

Report Page