server.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import fs from 'fs'
  2. import path from 'path'
  3. import { fileURLToPath } from 'url'
  4. import express from 'express'
  5. import serverStatic from 'serve-static'
  6. import { createServer as createViteServer } from 'vite'
  7. import ssrManifest from './dist/client/.vite/ssr-manifest.json' assert { type: "json" }
  8. import * as PRODserver from './dist/server/entry-server.js'
  9. const isProd = process.env.NODE_ENV === 'production'
  10. // if(isProd){
  11. // import ssrManifest from './dist/client/.vite/ssr-manifest.json' assert { type: "json" }
  12. // import * as PRODserver from './dist/server/entry-server.js'
  13. // const manifest = ssrManifest
  14. // }
  15. // function importBasedOnCondition(condition) {
  16. // console.log(condition)
  17. // if (condition) {
  18. // const PRODserver = async(moduleName) =>{
  19. // const A = await import `./dist/server/${moduleName}`
  20. // return A
  21. // }
  22. // const ssrManifest = async(moduleName) =>{
  23. // const B = await import `./dist/client/.vite/${ssr-manifest}` assert { type: "json" }
  24. // return B
  25. // }
  26. // return { PRODserver("entry-server.js"), ssrManifest("ssr-manifest.json")}
  27. // // return { import ssrManifest from './dist/client/.vite/ssr-manifest.json' assert { type: "json" }, import * as PRODserver from './dist/server/entry-server.js'}
  28. // }
  29. // }
  30. // 使用函数
  31. // importBasedOnCondition(isProd); // 根据条件动态导入moduleA
  32. // const isProd = true
  33. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  34. async function createServer() {
  35. const app = express()
  36. // 以中间件模式创建 Vite 应用,并将 appType 配置为 'custom'
  37. // 这将禁用 Vite 自身的 HTML 服务逻辑
  38. // 并让上级服务器接管控制
  39. const vite = await createViteServer({
  40. server: { middlewareMode: 'ssr' },
  41. // appType: 'custom'
  42. })
  43. if(!isProd){
  44. // 使用 vite 的 Connect 实例作为中间件
  45. // 如果你使用了自己的 express 路由(express.Router()),你应该使用 router.use
  46. // 当服务器重启(例如用户修改了 vite.config.js 后),
  47. // `vite.middlewares` 仍将保持相同的引用
  48. // (带有 Vite 和插件注入的新的内部中间件堆栈)。
  49. // 即使在重新启动后,以下内容仍然有效。
  50. app.use(vite.middlewares)
  51. }else{
  52. app.use(serverStatic(path.resolve(__dirname,'dist/client')))
  53. }
  54. app.use('*', async(req, res, next) => {
  55. const url = req.originalUrl
  56. let template
  57. let render
  58. let html
  59. try {
  60. if(!isProd){
  61. console.log("ssr:dev")
  62. // 1. 读取 index.html
  63. template = fs.readFileSync(
  64. path.resolve(__dirname, 'index.html'),
  65. 'utf-8',
  66. )
  67. // 2. 应用 Vite HTML 转换。这将会注入 Vite HMR 客户端,
  68. // 同时也会从 Vite 插件应用 HTML 转换。
  69. // 例如:@vitejs/plugin-react 中的 global preambles
  70. template = await vite.transformIndexHtml(url, template)
  71. // 3a. 加载服务器入口。vite.ssrLoadModule 将自动转换
  72. // 你的 ESM 源码使之可以在 Node.js 中运行!无需打包
  73. // 并提供类似 HMR 的根据情况随时失效。
  74. render = (await vite.ssrLoadModule('/src/entry-server.ts')).render
  75. // 3b. 从 Vite 5.1 版本开始,你可以试用实验性的 createViteRuntime
  76. // API。
  77. // 这个 API 完全支持热更新(HMR),其工作原理与 ssrLoadModule 相似
  78. // 如果你想尝试更高级的用法,可以考虑在另一个线程,甚至是在另一台机器上,
  79. // 使用 ViteRuntime 类来创建运行环境。
  80. // const runtime = await vite.createViteRuntime(server)
  81. // const { render } = await runtime.executeEntrypoint('/src/entry-server.ts')
  82. }else{
  83. console.log("ssr:prod")
  84. template = fs.readFileSync(
  85. path.resolve(__dirname, 'dist/client/index.html'),
  86. 'utf-8',
  87. )
  88. render = PRODserver.render
  89. }
  90. // 4. 渲染应用的 HTML。这假设 entry-server.js 导出的 `render`
  91. // 函数调用了适当的 SSR 框架 API。
  92. // 例如 ReactDOMServer.renderToString()
  93. if(!isProd){
  94. const { appHtml, state } = await render(url)
  95. // 5. 注入渲染后的应用程序 HTML 到模板中。
  96. html = template
  97. .replace(`<!--ssr-outlet-->`, appHtml)
  98. .replace('\'<!--vuex-state-->\'', JSON.stringify(state))
  99. }else{
  100. const manifest = ssrManifest
  101. const { appHtml, state,preloadLinks } = await render(url,manifest)
  102. html = template
  103. .replace(`<!--preload-links-->`, preloadLinks)
  104. .replace(`<!--ssr-outlet-->`, appHtml)
  105. .replace('\'<!--vuex-state-->\'', JSON.stringify(state))
  106. }
  107. // 6. 返回渲染后的 HTML。
  108. res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
  109. } catch (e) {
  110. // 如果捕获到了一个错误,让 Vite 来修复该堆栈,这样它就可以映射回
  111. // 你的实际源码中。
  112. vite.ssrFixStacktrace(e)
  113. next(e)
  114. }
  115. })
  116. app.listen(5173, () => {
  117. console.log("node server.js",isProd ? `运行 生产环境` : `运行 开发环境`)
  118. })
  119. }
  120. createServer()