server.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import fs from 'fs'
  2. import path from 'path'
  3. import { fileURLToPath } from 'url'
  4. import express from 'express'
  5. import { createServer as createViteServer } from 'vite'
  6. const __dirname = path.dirname(fileURLToPath(
  7. import.meta.url))
  8. async function createServer() {
  9. const app = express()
  10. // 以中间件模式创建 Vite 应用,并将 appType 配置为 'custom'
  11. // 这将禁用 Vite 自身的 HTML 服务逻辑
  12. // 并让上级服务器接管控制
  13. const vite = await createViteServer({
  14. server: { middlewareMode: true },
  15. appType: 'custom'
  16. })
  17. // 使用 vite 的 Connect 实例作为中间件
  18. // 如果你使用了自己的 express 路由(express.Router()),你应该使用 router.use
  19. // 当服务器重启(例如用户修改了 vite.config.js 后),
  20. // `vite.middlewares` 仍将保持相同的引用
  21. // (带有 Vite 和插件注入的新的内部中间件堆栈)。
  22. // 即使在重新启动后,以下内容仍然有效。
  23. app.use(vite.middlewares)
  24. app.use('*', async(req, res, next) => {
  25. const url = req.originalUrl
  26. try {
  27. // 1. 读取 index.html
  28. let template = fs.readFileSync(
  29. path.resolve(__dirname, 'index.html'),
  30. 'utf-8',
  31. )
  32. // 2. 应用 Vite HTML 转换。这将会注入 Vite HMR 客户端,
  33. // 同时也会从 Vite 插件应用 HTML 转换。
  34. // 例如:@vitejs/plugin-react 中的 global preambles
  35. template = await vite.transformIndexHtml(url, template)
  36. // 3a. 加载服务器入口。vite.ssrLoadModule 将自动转换
  37. // 你的 ESM 源码使之可以在 Node.js 中运行!无需打包
  38. // 并提供类似 HMR 的根据情况随时失效。
  39. const { render } = await vite.ssrLoadModule('/src/entry-server.ts')
  40. // 3b. 从 Vite 5.1 版本开始,你可以试用实验性的 createViteRuntime
  41. // API。
  42. // 这个 API 完全支持热更新(HMR),其工作原理与 ssrLoadModule 相似
  43. // 如果你想尝试更高级的用法,可以考虑在另一个线程,甚至是在另一台机器上,
  44. // 使用 ViteRuntime 类来创建运行环境。
  45. const runtime = await vite.createViteRuntime(server)
  46. const { render } = await runtime.executeEntrypoint('/src/entry-server.js')
  47. // 4. 渲染应用的 HTML。这假设 entry-server.js 导出的 `render`
  48. // 函数调用了适当的 SSR 框架 API。
  49. // 例如 ReactDOMServer.renderToString()
  50. const appHtml = await render(url)
  51. // 5. 注入渲染后的应用程序 HTML 到模板中。
  52. const html = template.replace(`<!--ssr-outlet-->`, appHtml)
  53. // 6. 返回渲染后的 HTML。
  54. res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
  55. } catch (e) {
  56. // 如果捕获到了一个错误,让 Vite 来修复该堆栈,这样它就可以映射回
  57. // 你的实际源码中。
  58. vite.ssrFixStacktrace(e)
  59. next(e)
  60. }
  61. })
  62. app.listen(5173)
  63. }
  64. createServer()