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 次
