You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 12:00:28