使用Vite初始化项目模板
安装pnpm:
npm install pnpm -g
创建Vue3 + Ts模版
pnpm create vite my-vite-vue3-ts-ssr-template --template vue-ts
进入my-vite-vue3-ts-ssr-template目录执行pnpm install
安装npm包,然后执行pnpm dev
运行开发环境,可以看到如下页面:

设置开发服务器
一个典型的SSR服务目录结构是这样的:
- index.html
- server.ts # main application server
- src/
- main.ts# 导出环境无关的(通用的)应用代码
- entry-client.ts# 将应用挂载到一个 DOM 元素上
- entry-server.ts# 使用某框架的 SSR API 渲染该应用
我们首先来实现server.ts
参照官网,我们的开发服务器使用express,首先进行安装:
pnpm install express
pnpm install @types/express -D
然后因为我们的是ts文件,服务端代码需要用到node,我们需要安装node的类型文件:
pnpm install @types/node -D
简单的开发服务器
我们在src目录下创建一个server.ts目录,输入如下代码:
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import express from "express";
// 在ts文件里不能直接使用__dirname,所以需要使用这种方法
const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function createServer() {const app = express();app.use("*", async (req, res) => {const html = fs.readFileSync(path.resolve(__dirname, "index.html"));res.status(200).set({ "Content-Type": "text/html" }).end(html);});app.listen(8900);console.log("Server is start at http://127.0.0.1:8900");
}
createServer();
这个服务的功能就是读取项目里的html文件然后返回。
使用ts-node启动服务
然后我们在package.json里添加启动脚本,启动脚本需要使用ts-node,先安装ts-node
pnpm install ts-node
在package.json里添加:
"scripts": {"dev": "ts-node server.ts","build": "vue-tsc && vite build","preview": "vite preview"},
然后执行pnpm run dev
会发现如下报错
TypeError [ERR_UNKNOWN_FILE_EXTENSION]:
Unknown file extension ".ts" for /Users/sunjunwei/Documents/mygithub/my-vite-vue3-ts-ssr-template/src/server.ts
这时候我们将启动命令改为如下方式:
"scripts": {"dev": "node --loader ts-node/esm server.ts",},
然后我们执行pnpm dev
可以正确启动服务,然后访问http://127.0.0.1:8900
可以拿到我们的html文件。
设置占位标记
我们在index.html文件里设置占位标记<!--ssr-outlet-->
,这时的html如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Vite + Vue + TS</title></head><body><div id="app"><!--ssr-outlet--></div><script type="module" src="/src/main.ts"></script></body>
</html>
我们要做的就是将ssr要渲染的内容填充到占位标记这里,我们更改server.ts如下:
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import express from "express";
// 在ts文件里不能直接使用__dirname,所以需要使用这种方法
const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function createServer() {const app = express();app.use("*", async (req, res) => {const template = fs.readFileSync(path.resolve(__dirname, "index.html")).toString();const html = template.replace("<<img src="http://127.0.0.1:8900")" style="margin: auto" />
}
createServer();
这时候重启服务看到页面输出如下:

这就是SSR的基本原理,在此基础上我们来扩展功能。
使用Vite服务
我们在server.ts里加上vite提供的服务:
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import express from "express";
import { createServer as createViteServer } from "vite";
// 在ts文件里不能直接使用__dirname,所以需要使用这种方法
const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function createServer() {const app = express();// 以中间件模式创建 Vite 应用,这将禁用 Vite 自身的 HTML 服务逻辑/