Vue 3 服务端渲染实战:用 Vite SSR 模式做首屏直出与客户端注水

test12026-10-041 次阅读

为什么上 SSR

SPA 首屏靠 JS 在浏览器里现画,白屏时间受包体积与设备性能影响。对内容型页面(官网、商品详情、博客),服务端先把 HTML 画好返回,浏览器拿到即可见内容,SEO 与首屏都更稳。Vue 3 的 createSSRApp 与客户端 createApp 共用同一套组件,差异只在入口。

项目结构

src/
  App.vue
  entry-client.ts   # 浏览器入口,做注水
  entry-server.ts   # 服务端入口,渲染成字符串
  main.ts           # 导出工厂函数,两端复用

共享的工厂函数

main.ts 导出一个创建 app 的工厂,客户端与服务端各自调用,避免状态跨请求污染:

import { createSSRApp } from "vue";
import App from "./App.vue";

export function createApp() {
  const app = createSSRApp(App);
  return { app };
}

服务端入口

entry-server.ts 把 app 渲染成 HTML 字符串,并挂上用于客户端恢复的状态:

import { renderToString } from "vue/server-renderer";
import { createApp } from "./main";

export async function render(url: string) {
  const { app } = createApp();
  const html = await renderToString(app);
  return { html };
}

客户端注水

entry-client.ts 用服务端返回的 HTML 进行 hydrate,接管已有 DOM 而不是重画:

import { createApp } from "./main";

const { app } = createApp();
app.mount("#app");

用 Vite 串起两端

vite.config.ts 里启用 SSR 构建,开发时用 ssrLoadModule 直接加载 server 入口,免去打包:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [vue()],
  ssr: {
    // 这些依赖在 Node 端需要特殊处理
    noExternal: ["some-ssr-only-dep"],
  },
});

服务端脚本

真正起服务的脚本读取模板,调用 render 拿到 HTML 再插值返回:

import { createServer } from "vite";

const vite = await createServer({ server: { middlewareMode: true } });
const { render } = await vite.ssrLoadModule("/src/entry-server.ts");

app.use(async (req, res) => {
  const { html } = await render(req.url);
  const template = fs.readFileSync("index.html", "utf-8");
  const out = template.replace("", html);
  res.end(out);
});

容易踩的坑

  • 不要在模块顶层放可变的单例状态,否则会被不同请求共享,工厂函数每次新建是前提。
  • 浏览器专用 API(window、document)不能在服务端执行,需要包在 onMounted 或判断环境。
  • 注水要求服务端与客户端的 DOM 结构一致,否则 hydrate 会告警并重渲染。
T

test1

文章作者

为什么上 SSR SPA 首屏靠 JS 在浏览器里现画,白屏时间受包体积与设备性能影响。对内容型页面(官网、商品详情、...

分类
技术
发布时间
2026-10-04
字数
约 1771 字
阅读
1 次