React / Vite frontend — build va Nginx bilan berish
Muammodan boshlaylik
React (Vite) ilovasi. Dasturchi serverda shunday ishga tushirdi:
npm run dev
Natija: 400 MB xotira, sekin sahifalar (har bir modul alohida yuklanadi), dev server ochiq — kod va source map lar ko'rinadi. npm run dev — ishlab chiqish uchun.
Production da frontend — bu shunchaki statik fayllar: npm run build yasagan HTML, JS va CSS. Ularni berish uchun Node.js umuman kerak emas — har qanday veb-server yetarli.
Multi-stage: Node da build, Nginx da berish
# syntax=docker/dockerfile:1
# ===== build =====
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
ARG VITE_API_URL
ENV VITE_API_URL=${VITE_API_URL}
RUN npm run build # → /app/dist
# ===== production =====
FROM nginx:1.27-alpine
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
Yakuniy image — Nginx + bir necha megabayt statik fayl: ~50 MB, Node siz, manba kodsiz (16-dars).
docker build --build-arg VITE_API_URL=https://api.dokon.uz -t dokon-web:1.0 .
docker run -d -p 8080:80 dokon-web:1.0
Nginx — SPA uchun konfiguratsiya
docker/nginx.conf:
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# SPA: mavjud bo'lmagan yo'l → index.html (React Router)
location / {
try_files $uri $uri/ /index.html;
}
# Hash li fayllar — uzoq keshlash
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# index.html — keshlanmasin (yangi versiya darhol ko'rinsin)
location = /index.html {
add_header Cache-Control "no-cache";
}
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
}
try_files — eng muhim qator
React Router da /mahsulot/42 — brauzerdagi yo'l; serverda bunday fayl yo'q. Busiz: foydalanuvchi sahifani yangilasa yoki havolani to'g'ridan-to'g'ri ochsa — 404. try_files ... /index.html — mavjud bo'lmagan har qanday yo'l uchun index.html qaytariladi, React Router esa kerakli sahifani o'zi chizadi. (Bu DevDocs sayti ham Vercel da xuddi shu qoida bilan ishlaydi — vercel.json dagi rewrites.)
Keshlash
Vite fayl nomlariga hash qo'shadi: assets/index-a3f9c2b1.js. Kod o'zgarsa — nom ham o'zgaradi. Shuning uchun /assets/ ni bir yilga keshlash xavfsiz. index.html esa doim yangi bo'lishi kerak — u yangi hash li fayllarga ishora qiladi.
Muhit o'zgaruvchilari muammosi
const API = import.meta.env.VITE_API_URL;
Bu qiymat build paytida kodga "yopishtiriladi". docker run -e VITE_API_URL=... — hech narsa qilmaydi: JS fayllar allaqachon yasalgan.
Natija: test va production uchun alohida image yasash kerak — bu "bitta image hamma joyda" tamoyilini buzadi (10-dars).
Yechim 1: nisbiy yo'l
const API = '/api';
Frontend va API bitta domenda, Nginx /api ni backend ga yo'naltiradi (37-dars). Manzil umuman kerak emas — eng sodda va eng yaxshi yechim.
Yechim 2: runtime konfiguratsiya
<!-- index.html -->
<script src="/config.js"></script>
// config.js — konteyner ishga tushganda yaratiladi
window.__CONFIG__ = { API_URL: "https://api.dokon.uz" };
Rasmiy nginx image /docker-entrypoint.d/ dagi skriptlarni ishga tushirishdan oldin bajaradi:
#!/bin/sh
# docker/40-config.sh
cat > /usr/share/nginx/html/config.js <<EOF
window.__CONFIG__ = { API_URL: "${API_URL}" };
EOF
COPY docker/40-config.sh /docker-entrypoint.d/40-config.sh
RUN chmod +x /docker-entrypoint.d/40-config.sh
docker run -e API_URL=https://test-api.dokon.uz dokon-web:1.0
Bitta image — har bir muhitda o'z manzili.
[!WARNING] Frontend dagi har qanday "o'zgaruvchi" — ommaviy. Brauzer uni ko'radi. Maxfiy kalitlarni frontend ga hech qachon qo'ymang — ular faqat backend da.
Next.js — boshqacha
Next.js — faqat statik fayllar emas (server komponentlar, API routes, SSR). Unga Node kerak:
// next.config.js
module.exports = { output: 'standalone' };
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
USER node
EXPOSE 3000
CMD ["node", "server.js"]
output: 'standalone' — Next.js faqat kerakli fayllar va node_modules qismini alohida papkaga yig'adi: image 1 GB dan ~150 MB ga tushadi.
Ishlab chiqish
services:
web:
build:
context: ./frontend
target: build
command: npm run dev -- --host 0.0.0.0
ports:
- "5173:5173"
develop:
watch:
- action: sync
path: ./frontend/src
target: /app/src
--host 0.0.0.0 — 9 va 30-darslar.
Nima uchun shunday?
Nima uchun frontend uchun Nginx, Node.js server emas?
Chunki build dan keyin frontend — oddiy fayllar, va ularni berish — veb-serverning eng sodda va eng tez ishi. Nginx bunga mo'ljallangan: minimal xotira (bir necha MB), minglab bir vaqtdagi ulanishlar, gzip, keshlash sarlavhalari, sendfile — hammasi tayyor. Node.js serveri (masalan, serve paketi) ham ishlaydi, lekin ko'proq xotira va hech qanday qo'shimcha foydasiz. Bundan tashqari, yakuniy image da JavaScript runtime yo'q — hujum yuzasi kichikroq.
Xulosa
Eslab qoling:
npm run dev— production da emas- Multi-stage: Node da
npm run build→ Nginx dadist/; ~50 MB - SPA:
try_files $uri $uri/ /index.html— sahifani yangilashda 404 bo'lmasin /assets/— uzoq kesh,index.html—no-cache; gzipVITE_*— build vaqtida; runtime uchun — nisbiy/apiyokiconfig.js+ entrypoint- Frontend dagi hech narsa maxfiy emas
- Next.js —
output: 'standalone', Node bilan
Keyingi dars
37-dars: Reverse proxy — Nginx / Caddy, domen va HTTPS