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注水 → 交互式应用。
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)五、浏览器兼容性
六、注意事项与最佳实践
-
避免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策略。
本文涉及AI创作
内容由AI创作,请仔细甄别