mcpField
B
bun-dev-server

bun-dev-server

github.com/daleseo开发442 次安装40 周前更新
代码仓库

关于 bun-dev-server

bun-dev-server 是使用 Bun.serve 配置开发服务器的技能,适用于 React、API、全栈应用和静态文件服务,并涵盖 HMR 与 React Fast Refresh 设置。

主要功能

  1. 01选择服务器类型
  2. 02配置 React HMR
  3. 03配置 Hono API 服务
  4. 04设置环境与脚本
  5. 05配置 HTTPS 和 API 代理
  6. 06排查常见问题

适用场景

  1. 01创建 React 开发服务器
  2. 02设置可自动重载的 API 服务
  3. 03在同一服务中组合前端与 API
  4. 04将 API 请求代理到现有后端

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/daleseo/bun-skills --skill bun-dev-server

安装后在 Claude Code 中输入/bun-dev-server确认已加载。

附带文件references/hmr-examples.md

详细介绍

Bun 开发服务器设置

你正在协助使用 Bun.serve 设置高性能开发服务器,并启用热模块替换(HMR)和 React Fast Refresh。

工作流

1. 确定服务器类型

询问用户需要哪种类型的开发服务器:

  • React/前端应用:支持 React Fast Refresh 的 SPA
  • API 服务器:支持自动重新加载的 REST/GraphQL API
  • 全栈应用:前端与 API 合并
  • 静态服务器:支持实时重新加载的文件服务器
2. 检查先决条件
# Verify Bun installation
bun --version

# Check if project has package.json
ls -la package.json

如果不存在 package.json,建议先运行 bun init。

3. 安装依赖

对于 React 应用:

bun add react react-dom
bun add -d @types/react @types/react-dom

对于使用 Hono 的 API(推荐):

bun add hono

对于全栈应用:

bun add react react-dom hono
bun add -d @types/react @types/react-dom
4. 创建服务器配置
React 开发服务器

在项目根目录创建 server.ts:

import type { ServerWebSocket } from "bun";

const clients = new Set<ServerWebSocket<unknown>>();

const server = Bun.serve({
  port: 3000,

  async fetch(request, server) {
    const url = new URL(request.url);

    // WebSocket for HMR
    if (url.pathname === "/_hmr") {
      const upgraded = server.upgrade(request);
      if (upgraded) return undefined;
      return new Response("WebSocket upgrade failed", { status: 500 });
    }

    // Serve index.html for SPA routing
    if (url.pathname === "/" || !url.pathname.includes(".")) {
      return new Response(
        Bun.file("public/index.html"),
        { headers: { "Content-Type": "text/html" } }
      );
    }

    // Serve static files
    const filePath = `public${url.pathname}`;
    const file = Bun.file(filePath);

    if (await file.exists()) {
      return new Response(file);
    }

    return new Response("Not Found", { status: 404 });
  },

  websocket: {
    open(ws) {
      clients.add(ws);
      console.log("HMR client connected");
    },

    close(ws) {
      clients.delete(ws);
      console.log("HMR client disconnected");
    },

    message(ws, message) {
      // Handle client messages if needed
    },
  },
});

console.log(`🚀 Dev server running at http://localhost:${server.port}`);

// Watch for file changes
const watcher = Bun.file.watch(import.meta.dir + "/src", {
  recursive: true,
});

for await (const event of watcher) {
  if (event.kind === "change" && event.path.endsWith(".tsx")) {
    console.log(`📝 File changed: ${event.path}`);

    // Notify all connected clients to reload
    for (const client of clients) {
      client.send(JSON.stringify({ type: "reload" }));
    }
  }
}

创建 public/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bun + React App</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/index.tsx"></script>

  
  <script>
    const ws = new WebSocket('ws://localhost:3000/_hmr');

    ws.addEventListener('message', (event) => {
      const data = JSON.parse(event.data);

      if (data.type === 'reload') {
        console.log('🔄 Reloading...');
        window.location.reload();
      }
    });

    ws.addEventListener('close', () => {
      console.log('❌ HMR connection lost. Reconnecting...');
      setTimeout(() => window.location.reload(), 1000);
    });
  </script>
</body>
</html>

创建 src/index.tsx:

import { render } from 'react-dom';
import App from './App';

const root = document.getElementById('root');
render(<App />, root);

创建 src/App.tsx:

export default function App() {
  return (
    <div>
      <h1>Welcome to Bun + React!</h1>
      <p>Edit src/App.tsx to see HMR in action</p>
    </div>
  );
}
使用 Hono 的 API 服务器

创建 server.ts:

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';

const app = new Hono();

// Middleware
app.use('*', cors());
app.use('*', logger());

// Routes
app.get('/', (c) => {
  return c.json({ message: 'Welcome to Bun API' });
});

app.get('/api/health', (c) => {
  return c.json({ status: 'ok', timestamp: Date.now() });
});

// Example POST endpoint
app.post('/api/users', async (c) => {
  const body = await c.req.json();
  return c.json({ created: true, data: body }, 201);
});

// Start server
const server = Bun.serve({
  port: process.env.PORT || 3000,
  fetch: app.fetch,
});

console.log(`🚀 API server running at http://localhost:${server.port}`);
全栈服务器

创建 server.ts:

import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';

const app = new Hono();

// API routes
const api = new Hono();

api.get('/health', (c) => c.json({ status: 'ok' }));
api.get('/users', (c) => c.json({ users: [] }));

app.route('/api', api);

// Serve static files
app.use('/*', serveStatic({ root: './public' }));

// SPA fallback
app.get('*', (c) => c.html(Bun.file('public/index.html')));

const server = Bun.serve({
  port: 3000,
  fetch: app.fetch,
});

console.log(`🚀 Full-stack server at http://localhost:${server.port}`);
5. 配置 React Fast Refresh(高级)

如需实现真正的 React Fast Refresh,创建 hmr-runtime.ts:

// React Fast Refresh runtime
let timeout: Timer | null = null;

export function refresh() {
  if (timeout) clearTimeout(timeout);

  timeout = setTimeout(() => {
    // Re-import the App component
    import('./App.tsx?t=' + Date.now()).then((module) => {
      const { render } = require('react-dom');
      const root = document.getElementById('root');
      render(module.default(), root);
    });
  }, 100);
}

// Listen for HMR events
if (import.meta.hot) {
  import.meta.hot.accept(() => {
    refresh();
  });
}
6. 环境配置

创建 .env.development:

# Server
PORT=3000
NODE_ENV=development

# API
API_URL=http://localhost:3000/api

# Features
ENABLE_HMR=true

创建 .env.production:

# Server
PORT=8080
NODE_ENV=production

# API
API_URL=https://api.example.com

# Features
ENABLE_HMR=false

在 server.ts 中加载环境变量:

// Environment is loaded automatically by Bun
const isDev = process.env.NODE_ENV === 'development';
const port = process.env.PORT || 3000;
7. 更新 package.json 脚本

添加开发脚本:

{
  "scripts": {
    "dev": "bun run --hot server.ts",
    "dev:watch": "bun run --watch server.ts",
    "start": "NODE_ENV=production bun run server.ts",
    "build": "bun build src/index.tsx --outdir=dist --minify",
    "clean": "rm -rf dist"
  }
}

脚本说明:

  • dev:以热重载模式运行(文件更改时重启)
  • dev:watch:监视模式(速度更快,但崩溃时不会重新加载)
  • start:生产模式
  • build:为生产环境构建前端
8. 配置 TypeScript

更新 tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "types": ["bun-types"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src/**/*", "server.ts"]
}
9. 创建项目结构

生成完整的项目结构:

project/
├── server.ts              # Development server
├── src/
│   ├── index.tsx         # App entry point
│   ├── App.tsx           # Main component
│   ├── components/       # React components
│   └── styles/           # CSS files
├── public/
│   ├── index.html        # HTML template
│   └── assets/           # Static assets
├── .env.development
├── .env.production
├── package.json
├── tsconfig.json
└── README.md
10. 高级:本地开发使用 HTTPS

如需支持 HTTPS(某些浏览器 API 需要):

import { readFileSync } from 'fs';

const server = Bun.serve({
  port: 3000,
  tls: {
    cert: readFileSync('./localhost.pem'),
    key: readFileSync('./localhost-key.pem'),
  },
  fetch: app.fetch,
});

console.log(`🔒 HTTPS server at https://localhost:${server.port}`);

使用以下命令生成证书:

# Install mkcert first: brew install mkcert
mkcert -install
mkcert localhost 127.0.0.1 ::1
11. 代理配置(用于现有后端)

如果用户需要将 API 请求代理到另一台服务器:

const app = new Hono();

// Proxy /api requests to backend
app.all('/api/*', async (c) => {
  const url = new URL(c.req.url);
  const backendUrl = `http://localhost:8080${url.pathname}${url.search}`;

  const response = await fetch(backendUrl, {
    method: c.req.method,
    headers: c.req.raw.headers,
    body: c.req.method !== 'GET' ? await c.req.raw.text() : undefined,
  });

  return new Response(response.body, {
    status: response.status,
    headers: response.headers,
  });
});

测试设置

创建完成后,引导用户进行测试:

# 1. Start dev server
bun run dev

# 2. Open browser
open http://localhost:3000

# 3. Make a change to src/App.tsx
# 4. Verify HMR reloads the page

# 5. Test API endpoints
curl http://localhost:3000/api/health

故障排除

HMR 无法正常工作
// Check if WebSocket connection is established
// Open browser console and look for:
// "HMR client connected"

// If not, verify:
// 1. Port is correct
// 2. No firewall blocking WebSocket
// 3. Server is running with --hot flag
端口已被占用
# Find process using port 3000
lsof -ti:3000

# Kill the process
kill -9 $(lsof -ti:3000)

# Or use a different port
PORT=3001 bun run dev
CORS 问题

为服务器添加 CORS 标头:

app.use('*', cors({
  origin: 'http://localhost:3000',
  credentials: true,
}));

性能提示

  1. 开发时使用 --hot:在大多数情况下比 --watch 更快
  2. 缩小文件监视范围:仅监视 src/ 目录
  3. 使用 HTTP/2:启用以加快并行加载
  4. 缓存静态资源:添加 Cache-Control 标头
app.use('/assets/*', async (c, next) => {
  await next();
  c.header('Cache-Control', 'public, max-age=31536000');
});

完成清单

  • ✅ 已创建开发服务器
  • ✅ 已配置并测试 HMR
  • ✅ 已设置环境变量
  • ✅ 已添加 Package.json 脚本
  • ✅ 已整理项目结构
  • ✅ 已配置 TypeScript
  • ✅ 浏览器已成功连接
  • ✅ 文件更改会触发重新加载

后续步骤

建议用户:

  1. 添加错误边界,以更好地处理错误
  2. 设置 ESLint 和 Prettier
  3. 在 tsconfig.json 中配置路径别名
  4. 添加开发与生产构建
  5. 考虑添加 bun-test 进行测试

评论

支持 Markdown