Ещё один пресет: создаём демо с помощью 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/