WebFactor Demo
by WebFactor.com
Home
Articles
Admin
Edit Article
Title
Author
Content
## Въведение в Rendering стратегиите Разбирането на различните rendering стратегии е критично за всеки frontend разработчик. В тази статия ще разгледаме детайлно как работят Server-Side Rendering (SSR), Static Site Generation (SSG) и Incremental Static Regeneration (ISR), с фокус върху това как Go backend-ът обработва заявките във всеки случай.  ## Архитектура на нашата система Преди да разгледаме различните стратегии, нека видим как е структурирана нашата система: ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ АРХИТЕКТУРА │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ │ │ Browser │ ──────► │ Nginx │ ──────► │ Astro │ │ │ │ Client │ ◄────── │ (Proxy) │ ◄────── │ (SSR) │ │ │ └─────────┘ └─────────┘ └────┬─────┘ │ │ │ │ │ │ │ │ API calls │ │ │ ▼ │ │ │ ┌──────────┐ │ │ └──────────► │ Go │ │ │ /api/* │ Backend │ │ │ └────┬─────┘ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ SQLite │ │ │ │ DB │ │ │ └──────────┘ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ ``` ## Server-Side Rendering (SSR) При SSR, HTML-ът се генерира на сървъра при всяка заявка. Това означава, че съдържанието винаги е актуално. ### Процес на SSR заявка ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ SSR REQUEST FLOW │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. Browser 2. Nginx 3. Astro 4. Go Backend │ │ │ │ │ │ │ │ │ GET /article/5 │ │ │ │ │ │─────────────────►│ │ │ │ │ │ │ proxy_pass │ │ │ │ │ │─────────────────►│ │ │ │ │ │ │ fetch API │ │ │ │ │ │─────────────────►│ │ │ │ │ │ │ Query DB │ │ │ │ │ │──────┐ │ │ │ │ │ │ │ │ │ │ │ │ │◄─────┘ │ │ │ │ │ JSON data │ │ │ │ │ │◄─────────────────│ │ │ │ │ Render HTML │ │ │ │ │ │◄─────────────────│ │ │ │ │ Full HTML │ │ │ │ │ │◄─────────────────│ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ~50-200ms ~1ms ~10-50ms ~5-20ms │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Go Backend код за SSR заявка ```go // Handler за вземане на статия func getArticleHandler(w http.ResponseWriter, r *http.Request) { // Взимаме ID от query параметрите idStr := r.URL.Query().Get("id") id, err := strconv.Atoi(idStr) if err != nil { jsonResponse(w, map[string]string{"error": "Invalid ID"}, 400) return } // Query към SQLite базата var article Article err = db.QueryRow(` SELECT id, title, content, author, published, render_mode, created_at, updated_at FROM articles WHERE id = ? AND published = true `, id).Scan( &article.ID, &article.Title, &article.Content, &article.Author, &article.Published, &article.RenderMode, &article.CreatedAt, &article.UpdatedAt, ) if err == sql.ErrNoRows { jsonResponse(w, map[string]string{"error": "Not found"}, 404) return } // Връщаме JSON отговор jsonResponse(w, article, 200) } ``` ### Astro SSR страница ```astro --- // Този код се изпълнява на сървъра при ВСЯКА заявка import Layout from '../layouts/Layout.astro'; import { getArticle } from '../lib/api'; const { id } = Astro.params; const article = await getArticle(parseInt(id)); const timestamp = new Date().toISOString(); --- <Layout title={article.title}> <article> <h1>{article.title}</h1> <p>Генерирано на: {timestamp}</p> <p>Render mode: {article.render_mode}</p> <div set:html={article.content} /> </article> </Layout> ``` ### Кога да използвате SSR - Персонализирано съдържание (user dashboard) - Реално-времеви данни - Динамични цени или наличности - Страници с чести актуализации ## Static Site Generation (SSG) При SSG, HTML-ът се генерира по време на build процеса. Резултатът е статичен файл, който се сервира директно. ### Процес на SSG заявка ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ SSG REQUEST FLOW │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ BUILD TIME (npm run build) │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ Astro Go Backend File System │ │ │ │ │ │ │ │ fetch all articles │ │ │ │ │───────────────────►│ │ │ │ │ │ Query DB │ │ │ │ JSON articles │ │ │ │ │◄───────────────────│ │ │ │ │ │ │ │ │ │ Generate HTML for each article │ │ │ │─────────────────────────────────────────►│ │ │ │ │ /article/1.html │ │ │ │ │ /article/2.html │ │ │ │ │ /article/3.html │ │ │ │ │ RUNTIME (user request) │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ Browser Nginx/CDN Static Files │ │ │ │ │ │ │ │ GET /article/1 │ │ │ │ │───────────────────►│ │ │ │ │ │ Read file │ │ │ │ │────────────────────►│ │ │ │ │ HTML content │ │ │ │ │◄────────────────────│ │ │ │ Full HTML │ │ │ │ │◄───────────────────│ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ~5-20ms ~1-5ms (pre-built) │ │ │ │ ⚡ Go Backend НЕ се вика при runtime! │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Важно: Контактната форма при SSG Когато статията е SSG, страницата е статична, но контактната форма все още работи чрез JavaScript на клиента: ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ SSG PAGE WITH CONTACT FORM │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Static HTML Page Browser JavaScript Go Backend │ │ (pre-rendered) │ │ │ │ │ │ │ │ Page loads instantly │ │ │ │ │◄──────────────────────────│ │ │ │ │ │ │ │ │ │ User fills form │ │ │ │ │ and clicks Submit │ │ │ │ │ │ │ │ │ │ JavaScript intercepts │ │ │ │ │─────────────────────────►│ │ │ │ │ │ POST /api/inquiry │ │ │ │ │────────────────────────►│ │ │ │ │ │ Save to DB │ │ │ │ {success: true} │ │ │ │ │◄────────────────────────│ │ │ │ Show success message │ │ │ │ │◄──────────────────────────│ │ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Client-side JavaScript за формата ```javascript // Този код се изпълнява в браузъра, не на сървъра contactForm.addEventListener('submit', async (e) => { e.preventDefault(); const inquiry = { name: formData.get('name'), email: formData.get('email'), message: formData.get('message'), source_page: window.location.pathname, // '/article/5' source_type: 'SSG', // Знаем че страницата е статична article_id: parseInt(articleId) }; // Директна заявка към Go backend през Nginx const response = await fetch('/api/inquiry', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(inquiry) }); if (response.ok) { showSuccessMessage(); } }); ``` ### Astro SSG конфигурация ```astro --- // export const prerender = true казва на Astro да генерира // тази страница статично при build export const prerender = true; import Layout from '../layouts/Layout.astro'; import { getArticle } from '../lib/api'; const { id } = Astro.params; const article = await getArticle(parseInt(id)); const buildTime = new Date().toISOString(); // Фиксирано при build! --- <Layout title={article.title}> <article> <h1>{article.title}</h1> <p>Генерирано при build: {buildTime}</p> <!-- Този timestamp НИКОГА няма да се промени без rebuild --> </article> <!-- Контактната форма работи с client-side JS --> <ContactForm articleId={id} renderMode="SSG" client:load /> </Layout> ``` ## Incremental Static Regeneration (ISR) ISR комбинира предимствата на SSG (бързина) и SSR (свежи данни). Страницата е статична, но се регенерира периодично. ### ISR процес ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ ISR REQUEST FLOW │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ First Request (cache empty) │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ │ │ Same as SSR - generate and cache │ │ ▼ │ │ │ │ Subsequent Requests (within TTL) │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ Browser Cache Go Backend │ │ │ │ │ │ │ │ GET /article/1 │ │ │ │ │──────────────────►│ │ │ │ │ │ Cache HIT │ │ │ │ Cached HTML │ (no backend call) │ │ │ │◄──────────────────│ │ │ │ │ │ After TTL expires (stale-while-revalidate) │ │ ───────────────────────────────────────────────────────────────────────── │ │ │ │ Browser Cache Go Backend │ │ │ │ │ │ │ │ GET /article/1 │ │ │ │ │──────────────────►│ │ │ │ │ Stale HTML │ │ │ │ │◄──────────────────│ │ │ │ │ │ Background refresh │ │ │ │ │─────────────────────►│ │ │ │ │ Fresh data │ │ │ │ │◄─────────────────────│ │ │ │ │ Update cache │ │ │ │ │ Next request gets fresh HTML │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Astro ISR имплементация ```typescript // В Astro няма native ISR, но можем да го симулираме: const CACHE_TTL = 30; // секунди const cache = new Map(); async function getArticleWithCache(id: number) { const cacheKey = `article_${id}`; const cached = cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < CACHE_TTL * 1000) { return cached.data; } // Fetch fresh data const article = await fetch(`${API_BASE}/api/article?id=${id}`) .then(r => r.json()); cache.set(cacheKey, { data: article, timestamp: Date.now() }); return article; } ``` ## Сравнение на стратегиите ``` ┌────────────────┬─────────────┬─────────────┬─────────────┬─────────────────┐ │ Критерий │ SSR │ SSG │ ISR │ Препоръка │ ├────────────────┼─────────────┼─────────────┼─────────────┼─────────────────┤ │ TTFB │ Бавен │ Много бърз│ Бърз │ SSG за статични │ │ │ (50-200ms) │ (5-20ms) │ (5-50ms) │ │ ├────────────────┼─────────────┼─────────────┼─────────────┼─────────────────┤ │ Данни │ Винаги │ От build │ Периодично │ SSR за динам. │ │ актуалност │ свежи │ момента │ обновявани │ │ ├────────────────┼─────────────┼─────────────┼─────────────┼─────────────────┤ │ Сървърно │ Високо │ Нулево │ Ниско │ SSG за трафик │ │ натоварване │ │ │ │ │ ├────────────────┼─────────────┼─────────────┼─────────────┼─────────────────┤ │ CDN кеширане │ Трудно │ Лесно │ Лесно │ SSG/ISR за CDN │ ├────────────────┼─────────────┼─────────────┼─────────────┼─────────────────┤ │ SEO │ Отлично │ Отлично │ Отлично │ Всички са добри │ ├────────────────┼─────────────┼─────────────┼─────────────┼─────────────────┤ │ Персонализ. │ Да │ Не │ Не │ SSR за auth │ └────────────────┴─────────────┴─────────────┴─────────────┴─────────────────┘ ``` ## Автоматичен Rebuild при SSG В нашата система, когато се промени SSG статия, Go backend-ът автоматично тригерира rebuild: ```go // triggerRebuild се вика когато SSG статия се промени func triggerRebuild() error { log.Println("Triggering Astro rebuild...") // Изпълняваме npm run build cmd := exec.Command("bash", "-c", "cd /var/www/go-astro-1.pasifora.com/frontend && npm run build") output, err := cmd.CombinedOutput() if err != nil { log.Printf("Rebuild error: %v\nOutput: %s", err, string(output)) return err } log.Println("Rebuild completed successfully") // Рестартираме Node.js сървъра exec.Command("bash", "-c", "pkill -f 'entry.mjs'").Run() go func() { time.Sleep(1 * time.Second) exec.Command("bash", "-c", "cd /var/www/.../frontend && "+ "HOST=127.0.0.1 PORT=4322 "+ "nohup node ./dist/server/entry.mjs &").Run() }() return nil } ``` ## Заключение Изборът между SSR, SSG и ISR зависи от конкретните нужди: - **SSG**: Блогове, документация, маркетингови страници - **SSR**: Dashboards, e-commerce с динамични цени, персонализирано съдържание - **ISR**: Новинарски сайтове, продуктови каталози с редки обновявания Важното е да разбирате как работи всяка стратегия и как данните текат от браузъра през различните слоеве до базата данни и обратно. --- **Брой думи: 3,412**
Render Mode
SSR (Server-Side Rendering)
SSG (Static Site Generation)
Published
Cancel
Save Changes