React Server API + Vite 简单实现SSR【根据文档提供的案例进行分析】

文档:cn.vite.dev/guide/ssr.h...

版本:react 19。 vite 5.4.19 (vite 7中一些api行为不太一样)

关键步骤:

第一步:server返回html

  1. 一个server.js -- 使用类似模板语法,替换index.html里一些特定"模板"为React组件,组合成html dom
js 复制代码
import {createServer} from 'vite';
import express from 'express';
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'

import path from 'node:path'

const __dirname = path.dirname(fileURLToPath(import.meta.url))


const vite = await createServer({
   server:{middlewareMode:true},
   appType:'custom'
})


const app = express()


app.use(vite.middlewares)

app.use("*all", async (req,res)=>{
   const template= await vite.transformIndexHtml(
       req.url,
       await fs.readFileSync(
       path.resolve(__dirname, 'index.html'),
       'utf-8',
       )    
   )
   
   const  {getMyRender} = await vite.ssrLoadModule(path.resolve(__dirname, 'server-entry.jsx'))  
   //注意这里,vite7 不是这么用的,它需要.render;vite 5 vite.ssrLoadModule返回{ getMyRender: [Getter], [Symbol(Symbol.toStringTag)]: 'Module' }
   

   const appHTML = await getMyRender(req.url)
   //注意这里,vite7返回一个html对象,可以appHTMl.html ;vite 5这里就一个html字符串
   
   const html = template
   .replace(`<!--app-->`,()=>appHTML)//替换index.html里的模板为React渲染好的组件
   //vite7的官方案例里给的可以分别替换head 和html body
   res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})


app.listen(3000,()=>{
   console.log('server running on 3000')
})
  1. 一个server-entry.js -- 调用React.renderToString(<StrictMode><你的组件></StrictMode>)返回html字符串,server.js里可以使用这个字符串来替换index.html里的"模板"
jsx 复制代码
import { renderToString } from "react-dom/server"
import {App} from "./App"
import React, { StrictMode } from 'react'


export function getMyRender(){
  const htmlstr= renderToString(<StrictMode><App/></StrictMode>)
  return htmlstr
}
  1. 一个index.html - <!--app-->会被server.js替换成React组件,是渲染好的DOM,不是jsx。
html 复制代码
<!doctype html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Vite + React</title>
 </head>
 <body><div id="app"><!--app--></div>
   <script type="module" src="./client-entry.jsx"></script>
 </body>
</html>

===

第二步: hydrate

关注一下index.html的 <script type="module" ...> 它引用了一个client-entry.jsx,这里就是水合发生的地方 1.准备client-entry. index.html里要注意挂载点<div id='app'>要在那

jsx 复制代码
import  { StrictMode } from "react"
import { hydrateRoot } from "react-dom/client"
import {App} from "./App"


hydrateRoot(document.getElementById("app"),<StrictMode><App/></StrictMode>)

第三步 node server.js 运行即可

可能的报错

Hydration failed because the server rendered HTML didn't match the client.

debug:先不加水合(就是把index.html里的script先注释掉)看看渲染结果是不是正常,有可能vite版本会带来ssrLoadModule返回类型的细微差异,导致server拼的html不对,那水合时自然找不到对应的dom元素了,就会报错。server返回的dom与水合时的一定要对上哦

相关推荐
开开心心就好11 分钟前
竖式除法出题工具,整除余数混合模式
运维·服务器·前端·javascript·ocr·perl·1024程序员节
起个名字费劲死了19 分钟前
Halcon学习一:骰子识别
前端·python·学习
大江东去浪淘尽千古风流人物24 分钟前
【HexVIO】DSP卸载双目惯性VIO:Hexagon向量化前端与单帧融合调度深度解析
前端·状态模式·slam·dsp·vio·hexagon·移动端加速
其实防守也摸鱼28 分钟前
Chrome 扩展安全攻防实录:从攻击面到防御实践
开发语言·前端·网络·chrome·安全·web安全·php
web打印社区32 分钟前
Lodop排障总纲-常见报错一张表
开发语言·前端·javascript·chrome·websocket
李剑一1 小时前
为什么几乎所有的知名开源项目都是外国人写的?
前端·ai编程·deepseek
小郎君6581 小时前
(一)为什么表单需要第三种状态:disabled、readonly 与纯文本展示
前端·vue.js
计算机魔术师1 小时前
1.4万亿没人接,OpenAI自己砍了1500亿
前端
溪语流沙2 小时前
【每天一个CSS | Day41】开关三连:一套状态机,三种视觉翻译
前端·css·html5·每日一练·按钮
变与不变8062 小时前
css定位布局精讲
前端·css