返回首页

SPA 的 SSR:索引设置

本文介绍了为搜索引擎索引 SPA 应用设置服务器端渲染。涵盖 Next.js、Nuxt.js、Angular Universal 的代码示例。改善 Core Web Vitals 和自然流量。

SPA 索引的 SSR:React、Vue、Angular
Advertisement 728x90

单页应用的服务器端渲染:解决搜索引擎索引难题

Googlebot、YandexBot 等搜索引擎爬虫在抓取基于客户端渲染(CSR)的单页应用(SPA)时,常仅看到空 HTML。由于不等待 JavaScript 执行,它们会跳过动态内容,导致页面无法被索引。服务器端渲染(SSR)通过在服务端预先生成完整 HTML 再发送至浏览器,彻底解决该问题。此举显著提升首屏内容绘制(FCP)、最大内容绘制(LCP)和首字节时间(TTFB),从而优化核心网页指标(Core Web Vitals)并增强自然搜索可见性。

渲染方案对比

| 参数 | 客户端渲染(CSR) | 服务器端渲染(SSR) | 预渲染(Prerendering) | 静态站点生成(SSG) |

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

Google AdInline article slot

| 渲染位置 | 浏览器端 | 服务端(按请求) | 构建时针对特定 URL 预生成 | 构建时为全部页面预生成 |

| FCP/LCP 速度 | 较慢 | 快 | 快 | 极快 |

| SEO 效果 | 差 | 优秀 | 良好 | 优秀 |

Google AdInline article slot

| 数据实时性 | 实时更新 | 实时更新 | 数据陈旧 | 数据陈旧 |

| 服务端负载 | 极低 | 高 | 低 | 极低 |

SSR 最适合动态内容页面——如商品目录、新闻流、数据看板;预渲染适用于更新频率低的页面(例如营销落地页);而 SSG 则是文档、官网首页等静态内容的理想选择。

Google AdInline article slot

React + Next.js:手把手实现 SSR

创建新项目:

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

对于动态页面,使用 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>
  );
}

添加 SEO 友好的元标签:

import Head from 'next/head';

export default function ProductPage({ product }) {
  return (
    <> 
      <Head>
        <title>{product.name} | 在线商城</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>
    </>
  );
}

部署建议:Vercel、Netlify 或搭配 PM2 的 Node.js 服务。

无错误水合(Hydration)

水合是指客户端 React “激活”服务端已渲染 HTML 的过程。避免水合失败的关键点:

  • 始终保持服务端与客户端数据同步;
  • 在调用 windowdocument 前,务必检测运行环境;
  • 密切关注浏览器控制台警告信息。
const isBrowser = typeof window !== 'undefined';

Vue + Nuxt.js:服务端渲染实践

初始化项目:

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

服务端获取数据:

<!-- 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: '页面未找到' });
}

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>

配置 nuxt.config.ts

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

Angular Universal:SSR 集成方案

Angular 17+ 版本:

ng add @angular/ssr

server.ts 中定义服务端入口:

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
// ... 其他导入

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);
});

构建并启动服务:

npm run build:ssr
npm run serve:ssr

安全检测运行平台:

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);
}

与无头 CMS 集成

Next.js + Contentful 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();
  // ...
}

核心要点总结

  • SSR 向搜索引擎爬虫交付完整 HTML,从根本上解决 CSR 应用的索引失效问题;
  • Next.js、Nuxt.js 和 Angular Universal 是经过大规模生产验证的 SSR 框架;
  • 水合不一致将触发整页重绘——务必严格校验运行时环境;
  • 切换 SSR 后,核心网页指标(FCP、LCP、TTFB)平均提升 50–80%;
  • 建议集成 Redis 缓存,在流量高峰期间将 TTFB 稳定控制在 200 毫秒以内。

— Editorial Team

Advertisement 728x90

继续阅读