pin_drop当前位置:知识文库 ❯ 图文

框架集成:22 SSR服务端渲染与HTM - 详细教程与实战指南

一、教程简介

服务端渲染(SSR)是在服务器上生成HTML并发送给浏览器的技术,解决了SPA首屏加载慢和SEO不友好的问题。SSR让浏览器接收到完整的HTML内容,用户可以立即看到页面,同时搜索引擎爬虫也能正确索引页面内容。本教程将介绍SSR原理、Next.js、Nuxt.js、Angular Universal、SvelteKit的SSR实现、hydration过程以及SSR的性能考虑。


二、核心概念

SSR原理与Hydration

SSR的核心流程是:服务端渲染HTML发送完整HTML浏览器直接显示Hydration注水交互式应用

特性 CSR(客户端渲染) SSR(服务端渲染)
首屏HTML 空壳(只有JS) 完整内容
首屏速度 慢(需等JS加载) 快(直接显示)
SEO 差(爬虫看不到内容) 好(完整HTML)
服务器负载 低(静态文件) 高(每次渲染)
TTFB 较慢(渲染耗时)
交互时间 慢(等JS执行) 快(HTML已显示)
适用场景 后台管理、工具 内容站、电商、营销

Hydration(注水)是SSR的关键步骤:服务器渲染HTML后浏览器显示静态页面,浏览器下载JavaScript后框架接管DOM,绑定事件监听器后页面变为可交互。

代码示例

// Hydration是SSR的关键步骤:
// 1. 服务器渲染HTML → 浏览器显示静态页面
// 2. 浏览器下载JavaScript → 框架接管DOM
// 3. 事件监听器绑定 → 页面变为可交互

// SSR输出的HTML示例:
<div id="app">
  <h1>Hello, World!</h1>
  <p>Count: 5</p>
  <button>Increment</button>  <!-- 此时按钮不可点击 -->
</div>
<script src="/bundle.js"></script>

// bundle.js加载后执行hydration:
// - React: hydrateRoot() 而非 createRoot()
// - Vue: createSSRApp() + hydrate()
// - Angular: bootstrapApplication() with SSR
// - Svelte: 自动检测并hydrate

// Hydration的注意事项:
// 1. 服务端和客户端渲染结果必须一致
//    不一致会导致hydration mismatch警告
// 2. 避免在SSR中使用浏览器API(window/document)
// 3. 生命周期钩子只在客户端执行
//    React: useEffect不执行于SSR
//    Vue: onMounted不执行于SSR
//    Angular: ngAfterViewInit不执行于SSR

三、各框架SSR实现

Next.js(React SSR)

代码示例

// Next.js App Router (Next.js 13+)
// app/layout.tsx - 根布局
export default function RootLayout({ children }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

// app/page.tsx - 首页(默认SSR)
// 默认是服务端组件(Server Component)
export default async function Page() {
  const data = await fetch('https://api.example.com/posts');
  const posts = await data.json();
  return (
    <div>
      {posts.map(post => <article key={post.id}>{post.title}</article>)}
    </div>
  );
}

// 客户端组件(需要交互时使用)
'use client';
import { useState } from 'react';
export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

// 动态路由
// app/posts/[id]/page.tsx
export default async function Post({ params }) {
  const post = await getPost(params.id);
  return <article>{post.content}</article>;
}

// 生成静态路径
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map(post => ({ id: post.id }));
}

// 数据获取
async function getPost(id) {
  const res = await fetch(`https://api.example.com/posts/${id}`, {
    cache: 'force-cache',     // SSG
    // cache: 'no-store',    // SSR(每次请求)
    // next: { revalidate: 60 }  // ISR(60秒重新验证)
  });
  return res.json();
}

Nuxt.js(Vue SSR)

代码示例

// Nuxt.js (Vue SSR)
// pages/index.vue - 自动路由
<script setup>
const { data: posts } = await useFetch('/api/posts');
</script>
<template>
  <div v-for="post in posts" :key="post.id">
    {{ post.title }}
  </div>
</template>

// 服务端API
// server/api/posts.ts
export default defineEventHandler(async () => {
  return await db.posts.findMany();
});

// 渲染模式配置
// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },      // SSG
    '/api/**': { cors: true },     // API
    '/admin/**': { ssr: false },   // CSR
  }
});

Angular Universal

代码示例

// Angular Universal
// ng add @nguniversal/express-engine
// 或使用SSR内置支持 (Angular 17+)

// server.ts
import { APP_BASE_HREF } from '@angular/common';
server.get('*', (req, res) => {
  res.render(indexHtml, {
    req,
    providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
  });
});

SvelteKit(默认SSR)

代码示例

// SvelteKit(默认SSR)
// src/routes/+page.ts
export async function load({ fetch }) {
  const res = await fetch('/api/posts');
  const posts = await res.json();
  return { posts };
}

// src/routes/+page.svelte
<script>
  export let data;
</script>
{#each data.posts as post}
  <article>{post.title}</article>
{/each}

// 禁用SSR(CSR模式)
// +page.ts
export const ssr = false;

// 预渲染(SSG)
// +page.ts
export const prerender = true;

四、SSR性能考虑

代码示例

// 1. 缓存策略
// - 页面级缓存:缓存渲染后的HTML
// - 数据级缓存:缓存API响应
// - CDN缓存:边缘节点缓存

// 2. 流式SSR(Streaming SSR)
// - 逐步发送HTML,不等所有数据加载完
// - React 18: renderToPipeableStream()
// - Next.js 13+ 自动支持流式渲染

// 3. 选择性hydration
// - 优先hydrate用户可见/交互的部分
// - React 18: <Suspense> 实现选择性hydration

// 4. 避免SSR的常见问题
// - 不要在SSR中使用window/document
// - 使用onMounted/useEffect等客户端钩子
// - 第三方库可能不支持SSR,需要动态导入

// 动态导入(仅客户端)
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('./Chart'), { ssr: false });

// Vue中
<ClientOnly>
  <Chart />
</ClientOnly>

// 5. TTFB优化
// - 减少服务端数据获取时间
// - 使用缓存
// - 并行获取数据
// - 使用边缘计算(Edge Runtime)

五、浏览器兼容性

特性 Chrome Firefox Safari Edge 说明
SSR HTML 全部 全部 全部 全部 标准HTML
Hydration 60+ 60+ 12+ 79+ 需框架运行时
Streaming SSR 73+ 69+ 13.1+ 79+ ReadableStream
Selective Hydration 60+ 60+ 12+ 79+ React 18+
Edge Runtime 80+ 80+ 14+ 80+ 需要Edge API

六、注意事项与最佳实践

  • 避免hydration mismatch:服务端和客户端渲染结果必须一致

  • 客户端API只在客户端使用:window、document等在SSR中不可用

  • 合理选择渲染模式:内容页面用SSR/SSG,交互页面用CSR

  • 使用缓存减少服务器负载:页面缓存、数据缓存、CDN缓存

  • 流式SSR提升体验:逐步发送HTML,不等所有数据加载完

  • 第三方库兼容性:不支持SSR的库使用动态导入


七、常见问题与解决方案

常见问题

SSR和SSG怎么选?

内容不频繁变化的页面用SSG(如博客、文档),内容实时变化的页面用SSR(如电商、社交)。Next.js支持混合渲染,不同页面可以使用不同策略。

hydration mismatch怎么解决?

确保服务端和客户端使用相同的数据和渲染逻辑。避免在渲染中使用Date.now()、Math.random()等不确定值。使用suppressHydrationWarning属性忽略特定元素的不匹配。

SSR对服务器有什么要求?

SSR需要Node.js服务器运行,每次请求都要渲染页面,CPU和内存消耗较高。建议使用缓存、CDN和边缘计算减轻服务器压力。

如何在SSR中处理认证?

通过Cookie传递认证状态,服务端读取Cookie验证用户身份。Next.js中使用middleware处理认证,Nuxt.js中使用服务端中间件。

SSR的首屏一定比CSR快吗?

不一定。SSR的TTFB(首字节时间)可能比CSR慢,因为服务器需要渲染HTML。但SSR的FP(首次绘制)和FCP(首次内容绘制)通常比CSR快,因为浏览器可以直接渲染HTML。


八、总结

本教程介绍了SSR服务端渲染与HTML的关系。SSR在服务器上生成完整HTML,解决了SPA首屏慢和SEO差的问题。Hydration是SSR的关键步骤,将静态HTML变为可交互的应用。Next.js、Nuxt.js、Angular Universal、SvelteKit都提供了SSR支持,各有特色。SSR的性能优化包括缓存策略、流式渲染和选择性hydration。选择SSR应根据页面类型和业务需求,合理混合SSR/SSG/CSR策略。

标签: SSR 服务端渲染 Hydration Next.js Nuxt.js SvelteKit

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 框架集成:21 框架状态管理与HTML - 详细教程与实战指南 下一篇: 框架集成:23 SSG静态生成与HTML - 详细教程与实战指南

poll相关推荐