Vue3 渲染模式全解析:CSR、预渲染、SSG、SSR 如何选择?

客户端渲染(CSR,Client‑Side Rendering)

使用 npm create vue@latestnpm create vite@latest 创建的 Vue3 项目,默认就是客户端渲染 。服务器返回的 HTML 文件几乎为空,只包含一个 <div id="app"></div> 和若干 <script> 标签。浏览器下载并执行 JavaScript 后,Vue 才会动态生成 DOM 并填充内容。

优点

  • 前后端分离,开发简单。
  • 页面切换无需刷新,体验接近原生应用。
  • 服务器压力小(只托管静态文件)。

缺点

  • 首屏加载慢:必须完成 JS 下载、解析、执行后才能看到内容。
  • SEO 不友好:搜索引擎爬虫可能无法执行 JS,导致抓取不到内容。
  • 需要额外处理 meta 标签(如标题、描述)。

预渲染 (Prerendering)

预渲染在构建阶段 启动无头浏览器(如 Puppeteer),访问你指定的路由,执行 Vue 代码,然后将渲染好的完整 HTML 保存为静态文件。用户请求这些路由时,直接返回预先生成的 HTML,无需在客户端执行 Vue 逻辑。

优点

  • 保留 CSR 开发体验,同时解决首屏白屏和部分 SEO 问题。
  • 部署简单(静态托管,如 Nginx、CDN)。
  • 适合页面数量较少、内容相对固定的站点。

缺点

  • 如果路由很多(成百上千),构建时间会非常长。
  • 无法处理动态数据(如用户评论、实时价格),因为 HTML 在构建时就固定了。
  • 对于参数化路由(/user/:id)需要枚举所有可能的 ID,不现实。

示例

插件 @seresweb/vite-plugin-seo-prerender

vue3-mananger-prerender/vite.config.ts

js 复制代码
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
// import VueDevTools from 'vite-plugin-vue-devtools'
import seoPrerender from "@seresweb/vite-plugin-seo-prerender";
// import { createMpaPlugin } from "vite-plugin-virtual-mpa";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
// @ts-ignore
import prerenderFallback from "./plugin/vite-plugin-prerender-fallback.js";

// 预渲染路由列表(从 router 中提取需要预渲染的静态路由)
// const prerenderRoutes = ["/", "/home", "/dashboard", "/about", "/404"];
// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
  // const isBuild = command === "build";

  return {
    plugins: [
      vue(),
      vueJsx(),
      AutoImport({
        resolvers: [ElementPlusResolver()],
        // 推荐:生成类型声明文件,提升 TypeScript 支持
        dts: "src/auto-imports.d.ts",
      }),
      Components({
        resolvers: [ElementPlusResolver()],
        // 推荐:生成组件类型声明文件,提升 TypeScript 支持
        dts: "src/components.d.ts",
      }),
      // VueDevTools(),

      prerenderFallback({
        distDir: "dist", // 可选,默认就是 'dist'
      }),
      seoPrerender({
        routes: ["/yoy/dashboard"],
        headless: "new",
        executablePath: undefined,
      }),
    ],
    resolve: {
      alias: {
        "@": fileURLToPath(new URL("./src", import.meta.url)),
      },
    },
    build: {
      outDir: "dist",
      target: "es2015",
      minify: true,
    },
  };
});

自定义插件,实现导航栏路径变化先请求目录下的html文件,没有再回退。

vue3-mananger-prerender/plugin/vite-plugin-prerender-fallback.js

js 复制代码
// vite-plugin-prerender-fallback.js
import fs from 'node:fs';
import path from 'node:path';

/**
 * Vite 插件:为预览服务器添加预渲染目录回退 + SPA fallback
 * @param {Object} options
 * @param {string} options.distDir - 构建输出目录,默认 'dist'
 */
export default function prerenderFallback(options = {}) {
  const { distDir = 'dist' } = options;
  const distPath = path.resolve(process.cwd(), distDir);

  return {
    name: 'vite-plugin-prerender-fallback',
    apply: 'serve', // 仅在预览/开发服务器时生效(预览模式也是 serve)
    configurePreviewServer(server) {
      // 在所有中间件之前插入我们的处理逻辑
      server.middlewares.use((req, res, next) => {
        // 1. 跳过根路径、带扩展名的文件请求、API 等(避免不必要的文件查找)
        const url = req.url;
        if (
          url === '/' ||
          path.extname(url) !== '' ||      // 有扩展名,如 .js, .css
          url.startsWith('/@') ||          // Vite 内部资源
          url.startsWith('/node_modules/')
        ) {
          return next();
        }

        // 2. 构造预渲染 HTML 的完整路径:dist + url + /index.html
        //    例如 url = '/yoy/dashboard' → dist/yoy/dashboard/index.html
        const htmlPath = path.join(distPath, url, 'index.html');

        // 3. 同步检查文件是否存在(也可以使用 fs.promises.access 异步)
        if (fs.existsSync(htmlPath)) {
          // 存在预渲染文件,修改请求路径指向该文件,让后续静态中间件处理
          req.url = path.posix.join(url, 'index.html');
          return next();
        }

        // 4. 不存在预渲染文件,请求原样放行,最终会 fallback 到根 index.html
        return next();
      });
    },
  };
}

测试demo

静态站点生成 (SSG)

SSG 是预渲染的升级版 ,它不局限于几个路由,而是将整个站点构建为静态 HTML 文件。它支持动态路由(通过 getStaticPaths 枚举所有可能参数),还能在构建时从 API 或本地文件获取数据(getStaticProps)。生成的每个页面都是完全独立的静态 HTML。

优点

  • 极致的首屏速度(HTML 已包含完整内容)。
  • SEO 完美(爬虫直接看到内容)。
  • 依然保留 Vue 组件的开发体验。
  • 部署成本低(CDN 即可)。

缺点

  • 构建时间随页面数量线性增长,不适合超大规模站点(百万级)。
  • 每次内容更新都需要重新构建全站。
  • 无法处理用户个性化内容(除非配合客户端 JavaScript 二次获取)。

服务端渲染 (SSR)

SSR 下,当用户请求一个页面时,服务器动态运行 Vue 组件,生成完整的 HTML 字符串并返回给浏览器。客户端接收到 HTML 后立即显示,然后"激活"(hydrate)Vue 组件使其可交互。每次请求都重新渲染,因此可以展示最新数据。

优点

  • 完美的 SEO,任何爬虫都能看到完整内容。
  • 首屏速度快(HTML 已就绪,无需等待 JS 执行)。
  • 支持实时数据(用户登录态、最新评论、个性化推荐)。

缺点

  • 服务器压力大(每次请求都需要 CPU 渲染组件)。
  • 复杂度高:需要处理 Node.js 环境、内存泄漏、缓存策略、防止双端状态不一致。
  • TTFB(首字节时间)可能比静态文件高。
相关推荐
小救星小杜、13 小时前
new Router base的作用
前端·javascript·vue.js
ct97814 小时前
Object.defineProperty/Proxy与 vue2 + vue3 响应式原理
前端·javascript·vue.js
存在的五月雨14 小时前
Vue中的nextTick
javascript·vue.js·ecmascript
肉肉不吃 肉14 小时前
watch中为什么不能直接侦听响应式对象的属性
前端·javascript·vue.js
喵了几个咪14 小时前
吃透后台权限系统:从架构设计到 Vue3/React 双框架完整落地
前端·vue.js·react.js·权限系统
喵了几个咪14 小时前
统一范式:中后台Admin项目标准化API分层开发方案(Vue/React通用)
前端·vue.js·react.js·protobuf
我有满天星辰14 小时前
【那些年踩过的坑-前端篇- Mac版本】Mac 从零搭建 Node 环境:nvm + Node + Vue 实战(避坑终极版)
前端·vue.js·macos
仰望.14 小时前
vxe-table 导出 Excel 进阶教程:自定义样式与高级功能
前端·javascript·vue.js·excel·vxe-table·vxe-ui
我叫张土豆15 小时前
从 0 到 1 搭一个可用的 Vue Flow 工作流编排器(含下载/加载/自动布局)
前端·javascript·vue.js