Как задеплоить Vue-приложение и выложить в интернет

Vue-приложение можно выложить в интернет, не арендуя сервер: после сборки это обычный набор статических файлов, поэтому нужен хостинг, который сам соберёт проект и отдаст результат. Загружаете исходники архивом или импортируете репозиторий, платформа читает package.json, ставит зависимости, запускает npm run build и выдаёт публичную HTTPS-ссылку. node_modules и папку dist везти не нужно. Но у Vue есть две ловушки: значения переменных VITE_ запекаются в готовый бандл, а роутер в history mode требует SPA-fallback. Ниже — путь от папки с кодом до рабочей ссылки на Dockbox.

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

Привычный путь такой: собрать проект командой, загрузить папку dist на хостинг, настроить веб-сервер так, чтобы любой URL возвращал index.html, и отдельно озаботиться сертификатом. Сам фреймворк тут ни при чём — вся сложность в этих трёх-четырёх шагах вокруг.

Сборка перед каждой загрузкой. Vue 3 с Vite, Vue CLI и Vue 2 собираются по-разному, но результат один: браузер не умеет исполнять .vue-файлы, ему нужны готовые JS и CSS.

SPA-fallback. Без правила «любой путь → index.html» обновление страницы на /about отдаёт 404, хотя переход по ссылке внутри приложения работает.

Секреты в бандле. Всё с префиксом VITE_ компилируется в готовые файлы и видно любому посетителю в исходнике страницы.

Сервер и сертификат. Нужен веб-сервер, выпуск и продление сертификата, автозапуск после падения. Всё это — вместо работы над самим приложением.

Платформа деплоя берёт эту часть на себя: вы отдаёте исходники, она находит package.json, ставит зависимости, собирает проект и отдаёт публичную HTTPS-ссылку. Ни nginx, ни ручных правил rewrite.

Шаг 1. Убедитесь, что проект собирается локально

В корне проекта должен лежать package.json со скриптом build. Для Vue 3 с Vite это npm run build, для Vue CLI — та же команда. Запустите её один раз на своей машине:

npm install
npm run build

Если сборка падает локально, она упадёт и на платформе, причём по той же причине. Сначала почините её, потом загружайте код. Версия фреймворка не важна: платформа смотрит на скрипт сборки, а не на Vue 2 или Vue 3 в зависимостях.

Шаг 2. Отдайте исходники, а не node_modules и dist

Сборку платформа делает сама, поэтому node_modules и dist только мешают: они тяжёлые и содержат пути с вашего компьютера. Загружайте исходный код — тот же набор файлов, что лежит в репозитории. Важно, чтобы среди них был файл блокировки зависимостей (package-lock.json, yarn.lock или pnpm-lock.yaml): по нему версии пакетов встанут те же, что и у вас. Если импортируете проект из GitHub, чистить вообще ничего не нужно.

Плохой вариант:

dist/
node_modules/

Правильный — package.json и lock-файл в архиве, остальное восстанавливается из них.

Шаг 3. Разберитесь с переменными VITE_

Это главная ловушка Vue. Всё, что начинается с VITE_, подставляется в код на этапе сборки и попадает в готовые файлы. То есть значение уезжает в браузер и видно любому посетителю в исходнике страницы.

Что можно класть в VITE_: адрес API, название сайта, публичный ключ карт. Что нельзя: токен базы, ключ платёжного провайдера, секрет платного API. Настоящие секреты живут на бэкенде, который возвращает приложению только результат. Вариант «фронтенд просто ходит в базу напрямую» тоже не годится: в браузере негде спрятать её пароль.

Шаг 4. Роутер в history mode — и правило для любого URL

С роутером в history mode ссылки выглядят обычно: /about, /projects/1. Проблема в том, что при прямом заходе или обновлении страницы сервер ищет файл по этому пути и не находит его — Vue рисует все страницы сам из index.html. На обычном хостинге для этого пишут правило rewrite; без него вы получаете 404 на /about.

На стороне платформы этот fallback уже настроен: открывайте вложенные адреса, обновляйте страницу — отдаётся тот же index.html. Поэтому переезжать на hash-routing с решёткой в URL не нужно, history mode работает как есть.

Шаг 5. Решите, где живёт бэкенд

Vue-приложение выполняется в браузере, поэтому хранить секреты и ходить в базу напрямую оно не может: это работа бэкенда. Вариантов два. Первый — выложить бэкенд отдельным проектом со своей ссылкой и обращаться к нему с фронтенда. Второй — держать всё в одном проекте со своим docker-compose: фронтенд, бэкенд и база как соседние сервисы. Адрес бэкенда секретом не является, поэтому для него VITE_-переменная подходит идеально: локальная и опубликованная сборки отличаются значением, а не кодом.

Шаг 6. Загрузите код и получите ссылку

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

Сколько это стоит и как работают контейнеры

Здесь принципиальная разница с VPS: администрировать сервер не нужно. Контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама, и приложение возвращается в строй без вашего участия.

Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, один проект. Первый месяц бесплатный. Для одного Vue-приложения этого достаточно, и лимита по времени работы у тарифа нет.

Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов.

Team — 4 990 ₽/мес — для команд, которым нужен запас ресурсов и SLA.

Свой Dockerfile поддерживается, но не обязателен: стек платформа определяет по коду сама. Если Dockerfile нужен, собирайте бандл отдельной стадией и отдавайте готовую статику лёгким nginx:

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html

Важная деталь: переменные окружения на этапе запуска на уже собранный бандл не влияют — значения VITE_ должны быть заданы до npm run build.

Частые вопросы

Нужно ли собирать проект самому и загружать папку dist?

Нет, достаточно исходного кода. Платформа ставит зависимости и запускает скрипт сборки из package.json, а готовые файлы отдаёт по вашей ссылке. Загрузка собственного dist только усложняет обновления: каждую новую версию пришлось бы собирать вручную.

Почему при обновлении страницы /about получается 404?

Так ведёт себя обычный хостинг: он ищет файл по этому пути и ничего не находит, потому что Vue рисует страницы сам из index.html. Лечится правилом, которое возвращает index.html для любого URL. На нашей стороне оно уже настроено, настраивать ничего не нужно.

Можно ли положить ключ API в переменную VITE_?

Только если это не настоящий секрет. Значения VITE_-переменных компилируются в готовые файлы, и любой посетитель видит их в исходнике страницы. Ключи, которые открывают доступ к деньгам или приватным данным, должны оставаться на бэкенде, а приложение обращается к нему.

Как подключить бэкенд к Vue-приложению?

Бэкенд — отдельная программа. Выложить его можно отдельным проектом со своей ссылкой либо соседним сервисом в вашем docker-compose. В самом приложении адрес бэкенда держите в переменной окружения, чтобы локальная и опубликованная сборки отличались значением, а не кодом.

Проект на Vue 2 или Vue CLI тоже опубликуется?

Да. Платформа смотрит на package.json и скрипт сборки, а не на версию фреймворка, поэтому Vue 2 и проекты Vue CLI публикуются так же, как Vue 3 с Vite. Единственное требование — сборка должна проходить без ошибок.