前端渲染方式

前端除了 SSRCSR 以外,常见还有这些渲染方式:

  1. SSG:静态站点生成
  2. ISR:增量静态再生成
  3. 预渲染 Prerender
  4. MPA:多页应用渲染
  5. Streaming SSR:流式服务端渲染
  6. Islands Architecture:岛屿架构 / 局部水合
  7. Partial Hydration:局部水合
  8. Resumability:可恢复式渲染
  9. Server Components:服务端组件
  10. Edge Rendering:边缘渲染

下面按「是什么、适合什么、怎么用」展开讲。

先放一个总览 CSRSSR 不是所有方案的两端,而是两个基础形态。

  • CSR:浏览器拿到空壳 HTML,再下载 JS,由 JS 渲染页面。
  • SSR:服务器每次请求实时生成 HTML,浏览器拿到可见页面,再水合交互。
  • SSG:构建时提前生成 HTML。
  • ISR:先静态生成,过期后后台重新生成。
  • Prerender:针对 SPA 预先跑一遍页面,产出静态 HTML。
  • Streaming SSR:HTML 不等全部算完,边生成边发送。
  • Islands:页面大部分是静态 HTML,只有局部组件变成交互岛。
  • Server Components:组件在服务端执行,只把结果发给客户端。
  • Edge Rendering:在 CDN 边缘节点完成动态渲染或个性化拼装。

它们实际项目里经常混用,不是互斥关系。

SSG:Static Site Generation SSG 是构建时渲染。页面 HTML 在 npm run build 时就生成好了,请求来了直接返回静态文件。

典型流程:

text 复制代码
数据源 / Markdown / CMS / 接口
        ↓
构建阶段执行页面逻辑
        ↓
生成 HTML / CSS / JS
        ↓
部署到 CDN
        ↓
用户访问时直接命中静态 HTML

适合:

  • 官网
  • 文档站
  • 博客
  • 活动页
  • 商品详情中变化不频繁的部分
  • SEO 要求高,但实时性不强的页面

优点:

  • 首屏快
  • CDN 友好
  • 服务器压力小
  • SEO 好
  • 稳定性高

缺点:

  • 数据更新需要重新构建
  • 页面数量很多时构建慢
  • 不适合强实时、强个性化页面

Next.js 里可以这样用:

tsx 复制代码
export async function getStaticProps() {
    const product = await fetchProduct();

    return {
        props: {
            product
        }
    };
}

export default function ProductPage({product}) {
    return <div>{product.name}</div>;
}

如果是 Next.js App Router

tsx 复制代码
export const dynamic = 'force-static';

export default async function Page() {
    const product = await getProduct();

    return <div>{product.name}</div>;
}

Nuxt 里类似:

bash 复制代码
nuxi generate

然后把生成出来的静态产物部署到 CDN。

ISR:Incremental Static Regeneration ISR 可以理解成「可自动刷新的 SSG」。

它不是每次请求都 SSR,也不是永远静态。它会先返回已有的静态 HTML,超过缓存时间后,后台重新生成新 HTML。

典型流程:

text 复制代码
第一次请求
  → 没有缓存
  → 生成 HTML
  → 存起来

后续请求
  → 直接返回缓存 HTML

超过 revalidate 时间
  → 先返回旧 HTML
  → 后台重新生成
  → 下次请求拿到新 HTML

适合:

  • 商品详情页
  • 资讯详情页
  • 店铺页
  • 列表聚合页
  • 页面很多,但内容不是秒级变化的业务

优点:

  • 接近静态站的性能
  • 又能定期更新数据
  • 比 SSR 压力小
  • 比纯 SSG 更灵活

缺点:

  • 用户可能短时间看到旧数据
  • 对部署平台有要求
  • 缓存失效策略要设计清楚

Next.js Pages Router 示例:

tsx 复制代码
export async function getStaticProps() {
    const product = await fetchProduct();

    return {
        props: {
            product
        },
        revalidate: 60
    };
}

这表示页面最多缓存 60 秒,过期后会触发重新生成。

App Router 示例:

tsx 复制代码
export const revalidate = 60;

export default async function Page() {
    const product = await fetchProduct();

    return <div>{product.name}</div>;
}

也可以对单个请求设置:

tsx 复制代码
const res = await fetch('https://api.example.com/product', {
    next: {
        revalidate: 60
    }
});

Prerender:预渲染 Prerender 常用于已有 SPA 项目。它会在构建阶段用无头浏览器或构建插件访问指定路由,把最终 HTML 保存下来。

它和 SSG 的区别是:

  • SSG 通常是框架原生支持,数据和路由在构建阶段有明确模型。
  • Prerender 更像是「跑一遍页面,把结果拍下来」。

适合:

  • 已经是 Vue SPA / React SPA,不想大改 SSR
  • 只想让少数页面 SEO 更好
  • 首页、活动页、落地页需要静态 HTML

Vue SPA 常见方式:

js 复制代码
// vue.config.js
const PrerenderSPAPlugin = require('prerender-spa-plugin');

module.exports = {
    configureWebpack: {
        plugins: [
            new PrerenderSPAPlugin({
                staticDir: path.join(__dirname, 'dist'),
                routes: ['/', '/about', '/product']
            })
        ]
    }
};

产物会包含这些路由对应的 HTML。

优点:

  • 改造成本比 SSR 小
  • 能改善 SEO 和首屏
  • 对少量页面很实用

缺点:

  • 动态路由多时不好维护
  • 个性化内容不适合
  • 构建阶段可能依赖接口稳定性
  • 水合后仍然会加载 SPA JS

MPA:Multi Page Application MPA 是传统多页应用。每个页面都是独立 HTML,请求新页面时浏览器整页跳转。

这其实也是一种渲染方式,只是现在大家讨论 SPA 多了,容易忽略它。

适合:

  • 页面之间关联弱
  • 后台系统的独立模块
  • 内容站
  • 老项目
  • 对首屏和 SEO 有要求,但不想引入复杂 SSR 的场景

典型结构:

text 复制代码
/index.html
/search.html
/detail.html
/user.html

或者后端模板:

html 复制代码
<html>
    <body>
        <h1>{{ product.name }}</h1>
    </body>
</html>

优点:

  • 简单直观
  • SEO 友好
  • 首屏稳定
  • 单页 JS 体积小

缺点:

  • 页面切换体验不如 SPA
  • 公共状态管理麻烦
  • 多页面构建配置复杂一些

在 Vite 里可以这样配置多入口:

ts 复制代码
import {defineConfig} from 'vite';

export default defineConfig({
    build: {
        rollupOptions: {
            input: {
                index: 'index.html',
                search: 'search.html',
                detail: 'detail.html'
            }
        }
    }
});

Streaming SSR:流式 SSR 传统 SSR 是服务器把整个页面都渲染完,再一次性返回 HTML。

Streaming SSR 是边渲染边返回。页面中快的部分先到浏览器,慢的部分后续补上。

适合:

  • 页面模块多
  • 部分接口慢
  • 希望尽快让用户看到主体内容
  • React 18 / Vue 3 SSR / Next.js App Router 类项目

传统 SSR:

text 复制代码
请求 → 等所有数据 → 渲染完整 HTML → 返回

流式 SSR:

text 复制代码
请求 → 返回页面骨架和快模块 → 慢模块 ready 后继续推送

React 示例:

tsx 复制代码
import {renderToPipeableStream} from 'react-dom/server';

const stream = renderToPipeableStream(<App />, {
    onShellReady() {
        stream.pipe(response);
    }
});

Next.js App Router 里通常通过 Suspense 使用:

tsx 复制代码
import {Suspense} from 'react';

export default function Page() {
    return (
        <>
            <Header />
            <Suspense fallback={<div>Loading...</div>}>
                <SlowProductList />
            </Suspense>
        </>
    );
}

优点:

  • 首字节更快
  • 慢接口不阻塞整页
  • 用户感知更好

缺点:

  • 服务端复杂度更高
  • 错误处理更复杂
  • 对框架和运行环境要求更高

Islands Architecture:岛屿架构 岛屿架构的核心思想是:页面大部分内容是静态 HTML,只有需要交互的组件加载 JS。

比如一个商品详情页:

text 复制代码
商品标题:静态 HTML
商品描述:静态 HTML
相关推荐:静态 HTML
收藏按钮:交互岛
规格选择器:交互岛
客服浮层:交互岛

只有「收藏按钮」「规格选择器」「客服浮层」这些需要交互的组件会变成客户端 JS。

Astro 是典型代表:

astro 复制代码
---
import ProductInfo from '../components/ProductInfo.astro';
import FavoriteButton from '../components/FavoriteButton.jsx';
---

<ProductInfo />
<FavoriteButton client:load />

也可以按需激活:

astro 复制代码
<FavoriteButton client:idle />
<CommentBox client:visible />
<SearchInput client:only="react" />

常见激活方式:

  • client:load:页面加载后立即激活
  • client:idle:浏览器空闲时激活
  • client:visible:组件进入视口后激活
  • client:media:满足媒体查询后激活
  • client:only:只在客户端渲染

优点:

  • JS 体积小
  • 首屏快
  • SEO 好
  • 很适合内容型页面加少量交互

缺点:

  • 全局状态共享麻烦
  • 不适合高度交互的复杂应用
  • 技术栈组合要设计好

Partial Hydration:局部水合 SSR 之后,浏览器需要把静态 HTML 变成可交互页面,这个过程叫 Hydration,也就是水合。

普通 SSR 的问题是:整棵组件树都要水合。

局部水合是:只水合需要交互的部分。

它和岛屿架构很接近,但侧重点不同:

  • Islands 更像架构模式。
  • Partial Hydration 更强调水合策略。

普通水合:

text 复制代码
整个页面 HTML → 整个 App 下载 JS → 整个组件树水合

局部水合:

text 复制代码
整个页面 HTML → 只给交互组件下载 JS → 只水合局部

适合:

  • 内容多、交互少的页面
  • 新闻、文档、商品详情
  • 首屏性能敏感页面

Astro、Qwik、Marko 等框架对这类能力支持较多。

Resumability:可恢复式渲染 这是 Qwik 主推的模型。

传统 SSR 的水合成本很高,因为浏览器拿到 HTML 后,还要重新执行组件逻辑、绑定事件、恢复状态。

Resumability 的想法是:服务端渲染时把状态、事件边界、组件信息序列化到 HTML 里,客户端不需要重新跑完整应用,只在用户真正交互时恢复对应部分。

传统水合:

text 复制代码
HTML 到浏览器
下载 JS
重新执行组件
绑定事件
页面可交互

可恢复式渲染:

text 复制代码
HTML 到浏览器
页面已经带有恢复信息
用户点击某按钮
只加载这个按钮需要的 JS
恢复并执行

Qwik 示例:

tsx 复制代码
import {component$} from '@builder.io/qwik';

export default component$(() => {
    return (
        <button onClick$={() => console.log('clicked')}>
            Click
        </button>
    );
});

注意这里是 onClick$,它表示这个事件处理函数可以被懒加载和恢复。

优点:

  • 极低首屏 JS
  • 交互按需加载
  • 对弱网、移动端友好

缺点:

  • 技术心智和生态相对小众
  • 需要接受框架特定写法
  • 迁移成本较高

Server Components:服务端组件 Server Components 不是传统 SSR,但经常和 SSR 一起出现。

它的核心是:某些组件只在服务端执行,不打包进客户端 JS。

比如 React Server Components:

tsx 复制代码
export default async function ProductList() {
    const products = await db.product.findMany();

    return (
        <ul>
            {products.map(product => (
                <li key={product.id}>{product.name}</li>
            ))}
        </ul>
    );
}

这个组件:

  • 可以直接访问数据库或服务端 API
  • 不会进入浏览器 JS 包
  • 不能使用浏览器 API
  • 不能使用 useStateuseEffect 等客户端交互能力

如果需要交互,要拆成客户端组件:

tsx 复制代码
'use client';

import {useState} from 'react';

export default function Counter() {
    const [count, setCount] = useState(0);

    return (
        <button onClick={() => setCount(count + 1)}>
            {count}
        </button>
    );
}

适合:

  • 数据读取型组件
  • 大量静态展示内容
  • 想减少客户端 JS
  • 服务端数据依赖较重的页面

优点:

  • 减少客户端包体积
  • 服务端数据访问更自然
  • 安全性更好,敏感逻辑不下发

缺点:

  • 服务端组件和客户端组件边界要清楚
  • 心智成本高
  • 目前主要在 React / Next.js 生态成熟

Edge Rendering:边缘渲染 Edge Rendering 是把渲染逻辑放到 CDN 边缘节点,而不是中心服务器。

传统 SSR:

text 复制代码
用户 → 源站服务器 → 渲染 HTML → 返回

边缘渲染:

text 复制代码
用户 → 最近的 CDN 边缘节点 → 渲染 / 拼装 HTML → 返回

适合:

  • 全球用户访问
  • 个性化内容
  • A/B 实验
  • 根据地区、设备、Cookie 做页面差异
  • 对延迟敏感的页面

示例:

ts 复制代码
export default async function handler(request) {
    const country = request.headers.get('x-country');

    if (country === 'CN') {
        return new Response(renderChinaPage(), {
            headers: {
                'content-type': 'text/html'
            }
        });
    }

    return new Response(renderGlobalPage(), {
        headers: {
            'content-type': 'text/html'
        }
    });
}

优点:

  • 延迟低
  • 个性化能力强
  • 可结合缓存策略

缺点:

  • 边缘运行环境受限
  • Node API 不一定完整
  • 调试复杂
  • 数据源离边缘节点远时收益会下降

怎么选 可以按页面类型来选:

text 复制代码
强交互后台系统
→ CSR / CSR + 局部 SSR

内容站、文档、博客
→ SSG / Islands / Partial Hydration

商品详情、店铺页、资讯页
→ SSG + ISR / SSR + 缓存

搜索结果页、用户相关页面
→ SSR / Streaming SSR / Edge Rendering

已有 SPA 想改善 SEO
→ Prerender

全球访问、个性化入口页
→ Edge Rendering

首屏性能极致敏感且交互少
→ Islands / Resumability

更工程化一点,可以这么判断:

text 复制代码
数据是否每次请求都不同?
是 → SSR / Edge Rendering
否 → SSG / ISR

页面是否需要 SEO?
是 → SSR / SSG / ISR / Prerender / Islands
否 → CSR 也可以

交互是否非常多?
是 → CSR / SSR + Hydration
否 → SSG / Islands / Partial Hydration

页面数量是否很多?
是 → ISR / SSR + 缓存
否 → SSG 可以接受

首屏 JS 是否需要极致压缩?
是 → Islands / Server Components / Resumability

结合你当前项目的语境 你这个项目里已经有 SSR 相关页面,比如移动端搜索页这类场景,通常看重:

  • 首屏速度
  • SEO / 抓取
  • 弱网体验
  • 服务端兜底
  • 客户端继续接管交互

如果要在现有 SSR/CSR 之外考虑其他方案,比较现实的是:

  1. Prerender

    适合活动页、静态营销页、规则说明页等,不太适合搜索结果这种动态页面。

  2. SSG

    适合内容固定、构建时能拿到数据的页面。

  3. ISR

    如果平台支持,适合商品详情、类目页、店铺页这类「数据会变,但不用秒级实时」的页面。

  4. Streaming SSR

    适合搜索结果页、复杂聚合页,尤其当某些模块接口慢,不想阻塞整页输出。

  5. Partial Hydration / Islands

    适合「展示内容多,交互组件少」的页面,可以显著减少客户端 JS。

一句话总结 除了 SSRCSR,前端渲染还包括 SSGISRPrerenderMPAStreaming SSRIslandsPartial HydrationResumabilityServer ComponentsEdge Rendering

如果按实际落地优先级看,最常用、最值得掌握的是:

text 复制代码
SSG:构建时生成,适合静态内容
ISR:静态页面按周期自动刷新
Prerender:给已有 SPA 补静态 HTML
Streaming SSR:服务端边算边吐,提高首屏体验
Islands / Partial Hydration:只让局部组件带 JS,减少水合成本
Server Components:服务端执行组件,减少客户端包体积

真正做技术选型时,不要问「哪个最先进」,而要问这几个问题:页面数据是否实时、是否需要 SEO、交互复杂度高不高、页面数量大不大、能不能接受旧数据、部署平台支不支持。答案出来后,渲染方式基本也就出来了。

相关推荐
京东云开发者2 小时前
全球首个!京东全栈开源JoyAI-VL-Interaction,让大模型从“一问一答”走向“边看边说”
前端
京东云开发者2 小时前
正式上线!京东云AI智能渗透测试服务
前端
AprChell2 小时前
低代码设计器和低代码设计引擎架构综述
前端·vue.js·低代码
Hilaku2 小时前
Node.js 还能再战十年?给你一个不换引擎的理由
前端·javascript·程序员
颜进强3 小时前
AI性能参数-截断、延迟与流式输出
前端·后端·ai编程
spmcor3 小时前
React 架构师之路:Next.js 全栈革命(第八篇)
前端·react.js
英勇无比的消炎药3 小时前
TinyRobot 源码深度分析:OpenTiny 的 AI 对话组件库
前端·vue.js·github
假如让我当三天老蒯3 小时前
React基础、进阶(学习用)
前端·react.js·面试
风骏时光牛马3 小时前
HTML十大经典实战代码案例合集
前端