vue3入门45 - Vite 进阶 - 服务端渲染SSR

305 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

Nodejs 集成 Vite 开发时的 SSR

归档.zip

  • 初始化一个 vite-react 项目

index.html 中添加一行注释,供替换模版字符串使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div id="root">
      <!-- APP_HTML -->
    </div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

在 src 下添加 server-entry.jsx 文件

import ReactDOMServer from "react-dom/server";
import { App } from "./App";

export function render(url, context) {
  return ReactDOMServer.renderToString(<App />);
}
  • 安装 express
yarn add express
  • server.js
const express = require("express");
const fs = require("fs");
const app = express();

const { createServer: createViteServer } = require("vite");

createViteServer({
  server: {
    middlewareMode: "ssr", // 启动 ssr
  },
}).then((vite) => {
  app.use(vite.middlewares); // vite 中间件

  app.get("*", async (req, res) => {
    // 读取文件
    let template = fs.readFileSync("index.html", "utf-8");
    template = await vite.transformIndexHtml(req.url, template);
    // 获取渲染函数
    const { render } = await vite.ssrLoadModule("/src/server-entry.jsx");
    // 渲染路由对应的 html
    const html = await render(req.url);
    // 替换字符串为 html 模版
    const responseHtml = template.replace("<!-- APP_HTML -->", html);
    res.set("content-type", "text/html").send(responseHtml);
  });

  app.listen(4000);
});
  • 启动 node 服务

Node 集成正式 build 的 Vite 应用的 SSR

归档.zip 修改 package.json 脚本

"scripts": {
  "dev": "vite",
  "build": "npm run build:client && npm run build:server",
  "build:client": "vite build --outDir dist/client",
  "build:server": "vite build --outDir dist/server --ssr src/server-entry.jsx",
  "serve": "vite preview"
},

执行 npm run buid , 打包出两份文件 image.png 修改 server.js , 作为生产环境使用

const express = require("express");
const fs = require("fs");
const app = express();

app.use(express.static("dist/client")); // 静态资源目录映射
// 获取 html 模版
const template = fs.readFileSync("dist/client/index.html", "utf-8");
app.get("*", async (req, res) => {
  // 获取渲染函数
  const { render } = require("./dist/server/server-entry");
  const context = {};
  // 渲染路由对应的 html
  const html = await render(req.url, context);
  if (context.url) {
    // 如果有此路由,重定向此路由即可
    res.redirect(301, context.url);
    return;
  }
  // 替换字符串为 html 模版
  const responseHtml = template.replace("<!-- APP_HTML -->", html);
  res.set("content-type", "text/html").send(responseHtml);
});
app.listen(4000);
  • 启动 node server.js

通过 SSR 功能实现静态站点导出

将源码解析,生成路由对应的 html 页面

const path = require("path");
const fs = require("fs");
const template = fs.readFileSync("dist/client/index.html", "utf-8");
const { render } = require("./dist/server/server-entry");

const routesToRender = fs.readdirSync("src/pages").map((file) => {
  return file.replace(".jsx", "").toLowerCase();
});

for (const route of routesToRender) {
  const context = {};
  // 渲染路由对应的 html
  const html = render(route, context);
  // 替换字符串为 html 模版
  const responseHtml = template.replace("<!-- APP_HTML -->", html);
  const filePath = `dist/static/${route}.html`;
  fs.writeFileSync(filePath, responseHtml);
  console.log(`prerender ${filePath}`);
}