Zpět na domů

SSR pro SPA: nastavení indexování

Článek rozebírá nastavení Server-Side Rendering pro indexování SPA aplikací vyhledávači. Jsou zde popsány Next.js, Nuxt.js, Angular Universal s příklady kódu. Zlepšení Core Web Vitals a organického provozu.

SSR pro indexování SPA: React, Vue, Angular
Advertisement 728x90

Server-Side Rendering pro SPA: Jak zajistit indexaci ve vyhledávačích

Vyhledávačové roboty jako Googlebot nebo YandexBot často u SPA aplikací založených na Client-Side Rendering (CSR) vidí prázdný HTML kód. Bez Server-Side Renderingu (SSR) nečekají na dokončení spuštění JavaScriptu a opouštějí stránku, aniž by ji zařadily do indexu. SSR řeší tento problém tím, že kompletní HTML generuje na serveru ještě před odesláním klientovi. To urychluje FCP, LCP i TTFB, zlepšuje Core Web Vitals a zvyšuje viditelnost ve výsledcích vyhledávání.

Porovnání metod vykreslování

| Parametr | CSR | SSR | Předvykreslení (Prerendering) | Statické generování (SSG) |

|----------|-----|-----|------------------------------|-----------------------------|

Google AdInline article slot

| Místo vykreslování | Prohlížeč | Server (pro každý požadavek) | Předem pro konkrétní URL | Předem pro všechny stránky |

| FCP/LCP | Pomalé | Rychlé | Rychlé | Velmi rychlé |

| SEO | Slabé | Vynikající | Dobré | Vynikající |

Google AdInline article slot

| Aktualita dat | V reálném čase | V reálném čase | Zastaralá | Zastaralá |

| Zátěž serveru | Minimální | Vysoká | Nízká | Minimální |

SSR je ideální pro dynamické stránky — např. katalogy produktů nebo novinky. Předvykreslení se hodí pro málo měnící se URL. SSG je nejvhodnější pro statický obsah.

Google AdInline article slot

React + Next.js: Postupná implementace

Vytvořte projekt:

npx create-next-app@latest my-ssr-app
cd my-ssr-app
npm run dev

Pro dynamické stránky použijte getServerSideProps:

// pages/product/[id].jsx
export async function getServerSideProps(context) {
  const { id } = context.params;
  const res = await fetch(`https://api.example.com/products/${id}`);
  const product = await res.json();

  if (!res.ok) {
    return { notFound: true };
  }

  return {
    props: { product },
  };
}

export default function ProductPage({ product }) {
  return (
    <main>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </main>
  );
}

Přidejte meta-tagy:

import Head from 'next/head';

export default function ProductPage({ product }) {
  return (
    <> 
      <Head>
        <title>{product.name} | E-shop</title>
        <meta name="description" content={product.shortDescription} />
        <meta property="og:title" content={product.name} />
        <meta property="og:image" content={product.imageUrl} />
      </Head>
      <main>
        <h1>{product.name}</h1>
      </main>
    </>
  );
}

Nasazení: Vercel, Netlify nebo Node.js s PM2.

Hydratace bez chyb

Hydratace je proces, při němž klientní React „oživuje“ HTML vygenerované na serveru. Abyste se vyhnuli nesouladu (mismatch), dodržujte tyto zásady:

  • Synchronizujte data mezi serverem a klientem.
  • Před použitím window nebo document ověřte prostředí.
  • Sledujte konzoli na varování.
const isBrowser = typeof window !== 'undefined';

Vue + Nuxt.js: Serverové vykreslování

Vytvořte projekt:

npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev

Získání dat na serveru:

<!-- pages/product/[id].vue -->
<script setup>
const route = useRoute();
const { data: product, error } = await useFetch(
  `/api/products/${route.params.id}`
);

if (error.value) {
  throw createError({ statusCode: 404, message: 'Stránka nenalezena' });
}

useHead({
  title: product.value?.name,
  meta: [
    { name: 'description', content: product.value?.shortDescription },
  ],
});
</script>

<template>
  <main>
    <h1>{{ product?.name }}</h1>
    <p>{{ product?.description }}</p>
  </main>
</template>

Konfigurace nuxt.config.ts:

export default defineNuxtConfig({
  ssr: true,
  app: {
    head: {
      htmlAttrs: { lang: 'cs' },
      link: [{ rel: 'canonical', href: 'https://example.com' }],
    },
  },
  nitro: {
    preset: 'node-server',
  },
});

Angular Universal: Integrace SSR

Pro Angular 17 a novější:

ng add @angular/ssr

Serverový endpoint v server.ts:

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
// ... ostatní importy

const server = express();
// ...

server.get('*', (req, res, next) => {
  commonEngine
    .render({
      bootstrap: AppServerModule,
      documentFilePath: join(browserDistFolder, 'index.html'),
      url: req.originalUrl,
      publicPath: browserDistFolder,
      providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
    })
    .then((html) => res.send(html))
    .catch(next);
});

Sestavení:

npm run build:ssr
npm run serve:ssr

Ověření platformy:

import { isPlatformBrowser } from '@angular/common';
import { inject, PLATFORM_ID } from '@angular/core';

const platformId = inject(PLATFORM_ID);

if (isPlatformBrowser(platformId)) {
  console.log(window.location.href);
}

Integrace s Headless CMS

Pro Next.js a Contentful pomocí GraphQL:

export async function getServerSideProps({ params }) {
  const query = `
    query GetArticle($slug: String!) {
      articleCollection(where: { slug: $slug }) {
        items {
          title
          body
          seo {
            title
            description
          }
        }
      }
    }
  `;

  const res = await fetch(
    `https://graphql.contentful.com/content/v1/spaces/${process.env.CONTENTFUL_SPACE_ID}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.CONTENTFUL_TOKEN}`,
      },
      body: JSON.stringify({ query, variables: { slug: params.slug } }),
    }
  );

  const { data } = await res.json();
  // ...
}

Klíčové body

  • SSR generuje plnohodnotný HTML pro vyhledávačové roboty a tak řeší hlavní nedostatky indexace CSR.
  • Next.js, Nuxt.js a Angular Universal jsou ověřené frameworky pro snadnou implementaci SSR.
  • Nesoulad při hydrataci (hydration mismatch) způsobuje přepracování DOMu — vždy ověřujte kontext spuštění.
  • Core Web Vitals (FCP, LCP, TTFB) se po přechodu na SSR zlepší o 50–80 %.
  • Pro vysokou zátěž doporučujeme cachování přes Redis, aby byl TTFB pod 200 ms.

— Editorial Team

Advertisement 728x90

Číst dál