使用Bun.serve()无框架部署TSX文件时,如何让onClick、onChange等DOM事件正常触发?
Bun.serve()无框架部署TSX文件时,如何让onClick、onChange等DOM事件正常触发?
嘿,我一眼就看出问题所在啦!你现在的代码只做了服务端渲染(SSR)——也就是在服务器上生成了静态的HTML字符串并返回给浏览器,但完全没在浏览器端加载React的客户端代码,也没完成「水合(Hydration)」这个关键步骤。
React的事件绑定(比如onClick)是在客户端运行的,纯SSR输出的静态页面就是一堆死的HTML标签,没有任何交互逻辑绑定,按钮当然点不动啦😅。
下面给你一步步解决:
步骤1:修改服务端代码,返回完整的HTML页面
现在你的服务端只返回了<MyApp />渲染后的内容,得把它包裹成完整的HTML结构,还要引入客户端的入口脚本。修改index.tsx里的fetch处理逻辑:
async fetch(req) { const url = new URL(req.url); // 先处理静态文件请求(比如客户端脚本) if (url.pathname !== "/") { const filePath = new URL(`.${url.pathname}`, import.meta.url).pathname; try { const file = Bun.file(filePath); if (await file.exists()) { return new Response(file); } } catch (e) { // 文件不存在就继续走其他逻辑 } } // 根路径返回带客户端脚本的完整HTML if (url.pathname === "/") { // 先把SSR的内容转成字符串 const stream = await renderToReadableStream(<MyApp />); const ssrHtml = await new Response(stream).text(); const fullHtml = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <!-- 给React客户端挂载用的根容器 --> <div id="root">${ssrHtml}</div> <!-- 引入客户端入口脚本 --> <script type="module" src="/client.tsx"></script> </body> </html> `; return new Response(fullHtml, { headers: { "Content-Type": "text/html" }, }); } return new Response("Not Found", { status: 404 }); },
步骤2:创建客户端入口文件,完成React水合
在项目根目录新建client.tsx,这个文件的作用是把React的客户端逻辑和服务端生成的静态HTML绑定起来:
import { hydrateRoot } from 'react-dom/client'; import MyApp from './App'; // 找到服务端渲染时的根容器 const rootElement = document.getElementById('root'); if (rootElement) { // 用hydrateRoot而不是createRoot,因为要复用服务端生成的HTML hydrateRoot(rootElement, <MyApp />); }
这里一定要用hydrateRoot,而不是createRoot——前者会复用已有的HTML结构,只绑定交互逻辑;后者会重新渲染整个组件树,浪费性能还可能导致样式闪烁。
步骤3:重启服务测试
现在重新运行bun start,打开浏览器访问http://localhost:2000,点击按钮应该就能正常触发onClick事件了!
额外说明
如果不想用SSR,只想做纯客户端渲染,其实可以简化流程:把服务端改成直接返回一个空的根容器和客户端脚本,然后用createRoot挂载组件。不过既然你已经搭了SSR的基础,用水合的方式更符合React的最佳实践~
备注:内容来源于stack exchange,提问作者Colin Keenan
相关产品推荐
相关产品推荐

