为什么Bun.js能在3秒内启动一个完整的Web应用?

bun.js是什么?

Bun.js 是一个新兴的、高性能的 JavaScript 运行时(runtime),类似于 Node.js,但设计目标是从底层开始优化开发体验与性能。它由 Jarred Sumner 创建,使用 Zig 语言编写,内置了打包器、测试运行器、转译器、包管理器等功能,目标是"all-in-one"工具链。

快速安装

js 复制代码
npm install bun -g

快速启动项目

js 复制代码
my-app/
├── package.json
├── tsconfig.json
├── index.ts
└── test.test.ts

index.ts

js 复制代码
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello from Bun!");
  },
});

console.log(`Listening on http://localhost:${server.port}`);

🚀 bun.js 能做哪些事情呢?

1. JavaScript/TypeScript 执行

详细说明:Bun 直接执行 JS/TS 文件,无需额外编译

实际例子

javascript 复制代码
// example.js
function calculateFibonacci(n) {
  if (n <= 1) return n;
  return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
}

console.log("斐波那契数列第10项:", calculateFibonacci(10));

// 异步操作示例
async function fetchData() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
  const user = await response.json();
  console.log("用户信息:", user.name);
}

fetchData();
typescript 复制代码
// example.ts
interface User {
  id: number;
  name: string;
  email: string;
}

class UserService {
  private users: User[] = [
    { id: 1, name: "Alice", email: "alice@example.com" },
    { id: 2, name: "Bob", email: "bob@example.com" }
  ];

  getUserById(id: number): User | undefined {
    return this.users.find(user => user.id === id);
  }

  getAllUsers(): User[] {
    return this.users;
  }
}

const service = new UserService();
console.log(service.getUserById(1));

2. 包管理器

详细说明:Bun 的包管理器比 npm/yarn 更快

实际例子

bash 复制代码
# package.json
{
  "name": "bun-project",
  "version": "1.0.0",
  "dependencies": {
    "zod": "^3.21.0"
  }
}
javascript 复制代码
// 使用安装的包
import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(2),
  age: z.number().min(0),
  email: z.string().email()
});

const userData = {
  name: "John",
  age: 30,
  email: "john@example.com"
};

try {
  const validUser = userSchema.parse(userData);
  console.log("验证成功:", validUser);
} catch (error) {
  console.log("验证失败:", error.errors);
}

3. 捆绑器 (Bundler)

详细说明:将多个文件打包成单个文件

实际例子

javascript 复制代码
// src/math.js
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

// src/calculator.js
import { add, multiply } from './math.js';

export class Calculator {
  constructor() {
    this.history = [];
  }

  calculate(operation, a, b) {
    let result;
    switch(operation) {
      case 'add':
        result = add(a, b);
        break;
      case 'multiply':
        result = multiply(a, b);
        break;
      default:
        throw new Error('Unknown operation');
    }
    this.history.push({ operation, a, b, result });
    return result;
  }

  getHistory() {
    return this.history;
  }
}

// src/index.js
import { Calculator } from './calculator.js';

const calc = new Calculator();
console.log(calc.calculate('add', 5, 3)); // 8
console.log(calc.calculate('multiply', 4, 6)); // 24

// 构建命令: bun build src/index.js --outfile=dist/bundle.js

快速启动服务

4. HTTP 服务器

详细说明:内置高性能 HTTP 服务器

实际例子 - REST API

javascript 复制代码
// server.js
import { serve } from "bun";

// 模拟数据库
let todos = [
  { id: 1, title: "学习 Bun.js", completed: false },
  { id: 2, title: "构建应用", completed: true }
];

let nextId = 3;

serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    const path = url.pathname;
    const method = request.method;

    // GET /api/todos - 获取所有待办事项
    if (path === "/api/todos" && method === "GET") {
      return Response.json(todos);
    }

    // POST /api/todos - 创建待办事项
    if (path === "/api/todos" && method === "POST") {
      const newTodo = await request.json();
      const todo = {
        id: nextId++,
        ...newTodo,
        completed: false
      };
      todos.push(todo);
      return Response.json(todo, { status: 201 });
    }

    // PUT /api/todos/:id - 更新待办事项
    if (path.startsWith("/api/todos/") && method === "PUT") {
      const id = parseInt(path.split("/")[3]);
      const todoIndex = todos.findIndex(t => t.id === id);
    
      if (todoIndex === -1) {
        return new Response("Todo not found", { status: 404 });
      }

      const updates = await request.json();
      todos[todoIndex] = { ...todos[todoIndex], ...updates };
      return Response.json(todos[todoIndex]);
    }

    // DELETE /api/todos/:id - 删除待办事项
    if (path.startsWith("/api/todos/") && method === "DELETE") {
      const id = parseInt(path.split("/")[3]);
      const todoIndex = todos.findIndex(t => t.id === id);
    
      if (todoIndex === -1) {
        return new Response("Todo not found", { status: 404 });
      }

      todos.splice(todoIndex, 1);
      return new Response("Todo deleted", { status: 200 });
    }

    // 静态文件服务
    if (path === "/" || path === "/index.html") {
      return new Response(`
        <!DOCTYPE html>
        <html>
        <head><title>Todo App</title></head>
        <body>
          <h1>Todo List</h1>
          <div id="app"></div>
          <script>
            fetch('/api/todos')
              .then(r => r.json())
              .then(todos => {
                document.getElementById('app').innerHTML = 
                  '<ul>' + todos.map(t => '<li>' + t.title + '</li>').join('') + '</ul>';
              });
          </script>
        </body>
        </html>
      `, {
        headers: { "Content-Type": "text/html" }
      });
    }

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

console.log("服务器运行在 http://localhost:3000");

5. WebSocket 支持

详细说明:实现实时双向通信

实际例子 - 聊天应用

javascript 复制代码
// chat-server.js
import { serve } from "bun";

const clients = new Set();
const chatHistory = [];

const server = serve({
  port: 3000,
  fetch(req, server) {
    // HTTP 请求:返回 HTML 页面
    if (req.headers.get("upgrade") !== "websocket") {
      return new Response(`
        <!DOCTYPE html>
        <html>
        <head>
          <title>实时聊天室</title>
          <style>
            #messages { height: 300px; overflow-y: scroll; border: 1px solid #ccc; }
            #messageInput { width: 70%; }
          </style>
        </head>
        <body>
          <h1>实时聊天室</h1>
          <div id="messages"></div>
          <input type="text" id="messageInput" placeholder="输入消息...">
          <button onclick="sendMessage()">发送</button>

          <script>
            const ws = new WebSocket('ws://localhost:3000');
            const messages = document.getElementById('messages');
            const messageInput = document.getElementById('messageInput');

            ws.onopen = function() {
              messages.innerHTML += '<div>连接已建立</div>';
            };

            ws.onmessage = function(event) {
              const data = JSON.parse(event.data);
              messages.innerHTML += '<div><strong>' + data.user + ':</strong> ' + data.message + '</div>';
              messages.scrollTop = messages.scrollHeight;
            };

            function sendMessage() {
              const message = messageInput.value.trim();
              if (message) {
                ws.send(JSON.stringify({
                  user: 'User' + Date.now(),
                  message: message
                }));
                messageInput.value = '';
              }
            }

            messageInput.addEventListener('keypress', function(e) {
              if (e.key === 'Enter') {
                sendMessage();
              }
            });
          </script>
        </body>
        </html>
      `, {
        headers: { "Content-Type": "text/html" }
      });
    }

    // WebSocket 连接
    const websocket = server.upgrade(req);
    if (websocket) {
      return websocket;
    }

    return new Response("Expected WebSocket upgrade", { status: 426 });
  },

  websocket: {
    open(ws) {
      console.log("客户端连接:", ws.data);
      clients.add(ws);
    
      // 发送历史消息
      for (const message of chatHistory) {
        ws.send(JSON.stringify(message));
      }
    },

    message(ws, message) {
      const data = JSON.parse(message);
      chatHistory.push(data);
    
      // 广播消息给所有客户端
      for (const client of clients) {
        if (client !== ws) {
          client.send(JSON.stringify(data));
        }
      }
    },

    close(ws) {
      console.log("客户端断开连接");
      clients.delete(ws);
    }
  }
});

console.log("聊天服务器运行在 http://localhost:3000");

📁 文件系统操作详解

6. 文件读写

详细说明:高效读写文件操作

实际例子 - 配置管理器

javascript 复制代码
// config-manager.js
class ConfigManager {
  constructor(configFile = "./config.json") {
    this.configFile = configFile;
  }

  async readConfig() {
    try {
      const file = Bun.file(this.configFile);
      if (await file.exists()) {
        const content = await file.json();
        return content;
      }
      return {};
    } catch (error) {
      console.log("配置文件不存在,创建默认配置");
      return this.createDefaultConfig();
    }
  }

  async writeConfig(config) {
    await Bun.write(this.configFile, JSON.stringify(config, null, 2));
  }

  async updateConfig(key, value) {
    const config = await this.readConfig();
    config[key] = value;
    await this.writeConfig(config);
  }

  async createDefaultConfig() {
    const defaultConfig = {
      server: {
        port: 3000,
        host: "localhost"
      },
      database: {
        url: "sqlite://db.sqlite",
        maxConnections: 10
      },
      logging: {
        level: "info",
        file: "app.log"
      }
    };
  
    await this.writeConfig(defaultConfig);
    return defaultConfig;
  }
}

// 使用示例
const configManager = new ConfigManager();

async function main() {
  const config = await configManager.readConfig();
  console.log("当前配置:", config);

  // 更新配置
  await configManager.updateConfig("server.port", 8080);
  console.log("端口已更新为 8080");

  // 读取日志文件
  const logFile = Bun.file("./app.log");
  if (await logFile.exists()) {
    const logContent = await logFile.text();
    console.log("日志内容:", logContent.substring(0, 100) + "...");
  } else {
    console.log("日志文件不存在");
  }
}

main();

7. 文件监听

详细说明:监控文件变化

实际例子 - 代码热重载

javascript 复制代码
// file-watcher.js
class HotReloader {
  constructor(watchPaths = ["./src"], onChangeCallback) {
    this.watchPaths = watchPaths;
    this.onChangeCallback = onChangeCallback;
    this.lastChanges = [];
  }

  async start() {
    const watcher = Bun.watch({
      paths: this.watchPaths,
      ignore: ["node_modules", "dist", ".git"],
      onChange: async (files) => {
        console.log("检测到文件变化:", files);
      
        for (const file of files) {
          const stat = await Bun.file(file).stat();
          const changeInfo = {
            file,
            timestamp: new Date(stat.mtime),
            type: this.getFileType(file)
          };
        
          this.lastChanges.push(changeInfo);
          console.log(`文件类型: ${changeInfo.type}, 修改时间: ${changeInfo.timestamp}`);
        }

        if (this.onChangeCallback) {
          await this.onChangeCallback(files);
        }
      }
    });

    console.log("开始监听文件变化...");
  }

  getFileType(filename) {
    if (filename.endsWith('.js') || filename.endsWith('.ts')) return 'script';
    if (filename.endsWith('.css')) return 'stylesheet';
    if (filename.endsWith('.html')) return 'html';
    if (filename.endsWith('.json')) return 'config';
    return 'other';
  }

  getRecentChanges(hours = 1) {
    const cutoffTime = new Date(Date.now() - hours * 60 * 60 * 1000);
    return this.lastChanges.filter(change => change.timestamp > cutoffTime);
  }
}

// 使用示例
const reloader = new HotReloader(
  ["./src"],
  async (changedFiles) => {
    console.log("文件变化,执行构建...");
  
    // 模拟构建过程
    for (const file of changedFiles) {
      if (file.endsWith('.js')) {
        console.log(`重新编译 JavaScript 文件: ${file}`);
        // 这里可以调用构建命令
      }
    }
  
    console.log("构建完成,刷新浏览器...");
  }
);

reloader.start();

🔧 系统工具详解

8. 子进程执行

详细说明:执行系统命令

实际例子 - 自动化部署脚本

javascript 复制代码
// deploy-script.js
class Deployer {
  constructor(projectDir = "./") {
    this.projectDir = projectDir;
  }

  async runCommand(command, args) {
    console.log(`执行命令: ${command} ${args.join(' ')}`);
  
    const process = Bun.spawn([command, ...args], {
      cwd: this.projectDir,
      stdout: "pipe",
      stderr: "pipe"
    });

    const stdout = await new Response(process.stdout).text();
    const stderr = await new Response(process.stderr).text();
    const exitCode = await process.exited;

    return { stdout, stderr, exitCode };
  }

  async gitStatus() {
    const result = await this.runCommand("git", ["status", "--porcelain"]);
    return result.stdout.trim();
  }

  async gitCommit(message) {
    await this.runCommand("git", ["add", "."]);
    const result = await this.runCommand("git", ["commit", "-m", message]);
    return result;
  }

  async buildProject() {
    console.log("开始构建项目...");
    const result = await this.runCommand("bun", ["run", "build"]);
  
    if (result.exitCode !== 0) {
      throw new Error(`构建失败: ${result.stderr}`);
    }
  
    console.log("构建成功!");
    return result.stdout;
  }

  async deploy() {
    try {
      // 检查是否有未提交的更改
      const status = await this.gitStatus();
      if (status) {
        console.log("检测到未提交的更改,自动提交...");
        await this.gitCommit(`Auto deploy ${new Date().toISOString()}`);
      }

      // 构建项目
      await this.buildProject();

      // 推送到远程仓库
      const pushResult = await this.runCommand("git", ["push", "origin", "main"]);
      if (pushResult.exitCode !== 0) {
        throw new Error(`推送失败: ${pushResult.stderr}`);
      }

      console.log("部署成功!");
    } catch (error) {
      console.error("部署失败:", error.message);
      throw error;
    }
  }
}

// 使用示例
const deployer = new Deployer();
deployer.deploy().catch(console.error);

9. 测试框架

详细说明:内置测试框架

实际例子 - 完整测试套件

javascript 复制代码
// tests/user.test.js
import { expect, test, describe, beforeAll, afterAll } from "bun:test";

// 被测试的类
class UserService {
  constructor() {
    this.users = [
      { id: 1, name: "Alice", email: "alice@example.com", age: 25 },
      { id: 2, name: "Bob", email: "bob@example.com", age: 30 }
    ];
  }

  getUserById(id) {
    return this.users.find(user => user.id === id);
  }

  getUserByEmail(email) {
    return this.users.find(user => user.email === email);
  }

  createUser(userData) {
    const newUser = {
      id: this.users.length + 1,
      ...userData
    };
    this.users.push(newUser);
    return newUser;
  }

  validateUser(user) {
    if (!user.name || user.name.length < 2) {
      throw new Error("Name must be at least 2 characters");
    }
    if (!user.email || !user.email.includes("@")) {
      throw new Error("Invalid email");
    }
    return true;
  }
}

// 测试套件
describe("UserService", () => {
  let service;

  beforeAll(() => {
    service = new UserService();
  });

  test("should get user by id", () => {
    const user = service.getUserById(1);
    expect(user).toBeDefined();
    expect(user.name).toBe("Alice");
    expect(user.email).toBe("alice@example.com");
  });

  test("should return undefined for non-existent user", () => {
    const user = service.getUserById(999);
    expect(user).toBeUndefined();
  });

  test("should get user by email", () => {
    const user = service.getUserByEmail("bob@example.com");
    expect(user).toBeDefined();
    expect(user.name).toBe("Bob");
  });

  test("should create new user", () => {
    const newUser = service.createUser({
      name: "Charlie",
      email: "charlie@example.com",
      age: 28
    });

    expect(newUser.id).toBe(3);
    expect(newUser.name).toBe("Charlie");
    expect(service.getUserById(3)).toBeDefined();
  });

  test("should validate user with valid data", () => {
    const validUser = { name: "Valid", email: "valid@test.com" };
    expect(() => service.validateUser(validUser)).not.toThrow();
  });

  test("should throw error for invalid user name", () => {
    const invalidUser = { name: "A", email: "test@example.com" };
    expect(() => service.validateUser(invalidUser))
      .toThrow("Name must be at least 2 characters");
  });

  test("should throw error for invalid email", () => {
    const invalidUser = { name: "Valid", email: "invalid-email" };
    expect(() => service.validateUser(invalidUser))
      .toThrow("Invalid email");
  });

  test("should handle async operations", async () => {
    // 模拟异步操作
    const result = await new Promise(resolve => {
      setTimeout(() => resolve("async result"), 100);
    });
    expect(result).toBe("async result");
  });
});

// 性能测试
test("performance test", async () => {
  const start = performance.now();

  // 执行一些操作
  for (let i = 0; i < 1000; i++) {
    const user = new UserService();
    user.getUserById(1);
  }

  const end = performance.now();
  const duration = end - start;

  console.log(`1000次操作耗时: ${duration}ms`);
  expect(duration).toBeLessThan(100); // 应该在100ms内完成
});

// 运行测试: bun test

📦 生态系统集成详解

10. Node.js 兼容

详细说明:与 Node.js 生态系统兼容

实际例子 - 文件处理工具

javascript 复制代码
// file-processor.js
import fs from "fs";
import path from "path";
import { createHash } from "crypto";

class FileProcessor {
  constructor() {
    this.stats = {
      processed: 0,
      errors: 0,
      totalSize: 0
    };
  }

  async processDirectory(dirPath) {
    const files = await fs.promises.readdir(dirPath);
  
    for (const file of files) {
      const filePath = path.join(dirPath, file);
      const stat = await fs.promises.stat(filePath);
    
      if (stat.isDirectory()) {
        await this.processDirectory(filePath);
      } else if (this.isTextFile(file)) {
        await this.processFile(filePath, stat.size);
      }
    }
  
    return this.stats;
  }

  isTextFile(filename) {
    const extensions = ['.js', '.ts', '.json', '.txt', '.md', '.html', '.css'];
    return extensions.some(ext => filename.endsWith(ext));
  }

  async processFile(filePath, size) {
    try {
      const content = await fs.promises.readFile(filePath, 'utf8');
      const hash = createHash('md5').update(content).digest('hex');
    
      this.stats.processed++;
      this.stats.totalSize += size;
    
      console.log(`处理文件: ${filePath}`);
      console.log(`大小: ${size} bytes`);
      console.log(`MD5: ${hash.substring(0, 8)}...`);
      console.log('---');
    
    } catch (error) {
      this.stats.errors++;
      console.error(`处理文件错误 ${filePath}:`, error.message);
    }
  }

  generateReport() {
    return `
      文件处理报告:
      - 处理文件数: ${this.stats.processed}
      - 错误数: ${this.stats.errors}
      - 总大小: ${this.formatBytes(this.stats.totalSize)}
    `;
  }

  formatBytes(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
}

// 使用示例
async function main() {
  const processor = new FileProcessor();
  const stats = await processor.processDirectory('./src');
  console.log(processor.generateReport());
}

main().catch(console.error);

11. 前端开发

详细说明:完整的前端开发工具链

实际例子 - 博客构建系统

javascript 复制代码
// blog-builder.js
import { glob } from "bun";

class BlogBuilder {
  constructor() {
    this.postsDir = "./posts";
    this.outputDir = "./dist";
  }

  async build() {
    console.log("开始构建博客...");
  
    // 清理输出目录
    await this.cleanOutputDir();
  
    // 读取所有 Markdown 文件
    const markdownFiles = await glob("**/*.md", this.postsDir);
    const posts = [];
  
    for (const file of markdownFiles) {
      const content = await Bun.file(file).text();
      const post = this.parseMarkdown(content);
      posts.push(post);
    }
  
    // 生成文章页面
    for (const post of posts) {
      await this.generatePostPage(post);
    }
  
    // 生成首页
    await this.generateIndex(posts);
  
    // 生成 RSS 订阅
    await this.generateRSS(posts);
  
    console.log(`构建完成! 生成了 ${posts.length} 篇文章`);
  }

  parseMarkdown(content) {
    const lines = content.split('\n');
    let frontmatter = {};
    let body = '';
    let inFrontmatter = false;
  
    for (let i = 0; i < lines.length; i++) {
      if (lines[i].trim() === '---') {
        if (!inFrontmatter) {
          inFrontmatter = true;
        } else {
          body = lines.slice(i + 1).join('\n');
          break;
        }
      } else if (inFrontmatter) {
        const [key, value] = lines[i].split(':');
        if (key && value) {
          frontmatter[key.trim()] = value.trim().replace(/"/g, '');
        }
      }
    }
  
    return {
      ...frontmatter,
      content: this.convertMarkdownToHTML(body),
      slug: this.generateSlug(frontmatter.title)
    };
  }

  convertMarkdownToHTML(markdown) {
    // 简单的 Markdown 转 HTML
    return markdown
      .replace(/^### (.*$)/gim, '<h3>$1</h3>')
      .replace(/^## (.*$)/gim, '<h2>$1</h2>')
      .replace(/^# (.*$)/gim, '<h1>$1</h1>')
      .replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
      .replace(/\*(.*)\*/gim, '<em>$1</em>')
      .replace(/`(.*?)`/gim, '<code>$1</code>')
      .replace(/\n\n/gim, '<br><br>');
  }

  generateSlug(title) {
    return title
      .toLowerCase()
      .replace(/[^\w\s-]/g, '')
      .replace(/[\s_-]+/g, '-')
      .replace(/^-+|-+$/g, '');
  }

  async generatePostPage(post) {
    const html = `
      <!DOCTYPE html>
      <html>
      <head>
        <title>${post.title}</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
          body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
          h1 { color: #333; }
          .meta { color: #666; font-size: 0.9em; }
        </style>
      </head>
      <body>
        <h1>${post.title}</h1>
        <div class="meta">发布于 ${post.date} | 作者: ${post.author}</div>
        <div class="content">${post.content}</div>
        <a href="/">← 返回首页</a>
      </body>
      </html>
    `;
  
    const outputPath = `${this.outputDir}/posts/${post.slug}.html`;
    await Bun.write(outputPath, html);
  }

  async generateIndex(posts) {
    const postsList = posts
      .sort((a, b) => new Date(b.date) - new Date(a.date))
      .map(post => `
        <div style="margin-bottom: 30px;">
          <h2><a href="posts/${post.slug}.html">${post.title}</a></h2>
          <div style="color: #666; font-size: 0.9em;">发布于 ${post.date} | 作者: ${post.author}</div>
          <p>${post.excerpt || post.content.substring(0, 100) + '...'}</p>
        </div>
      `)
      .join('');
  
    const html = `
      <!DOCTYPE html>
      <html>
      <head>
        <title>我的博客</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
          body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
          h1 { color: #333; }
          a { color: #007acc; text-decoration: none; }
          a:hover { text-decoration: underline; }
        </style>
      </head>
      <body>
        <h1>我的博客</h1>
        <div>${postsList}</div>
      </body>
      </html>
    `;
  
    await Bun.write(`${this.outputDir}/index.html`, html);
  }

  async generateRSS(posts) {
    const rss = `<?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0">
      <channel>
        <title>我的博客</title>
        <link>http://localhost:3000</link>
        <description>技术博客</description>
        ${posts.map(post => `
          <item>
            <title>${post.title}</title>
            <link>http://localhost:3000/posts/${post.slug}.html</link>
            <description>${post.excerpt || post.content.substring(0, 100)}</description>
            <pubDate>${new Date(post.date).toUTCString()}</pubDate>
          </item>
        `).join('')}
      </channel>
    </rss>`;
  
    await Bun.write(`${this.outputDir}/rss.xml`, rss);
  }

  async cleanOutputDir() {
    try {
      const outputDir = Bun.file(this.outputDir);
      if (await outputDir.exists()) {
        await Bun.spawn(["rm", "-rf", this.outputDir]);
      }
      await Bun.mkdir(this.outputDir);
      await Bun.mkdir(`${this.outputDir}/posts`);
    } catch (error) {
      console.log("清理输出目录:", error.message);
    }
  }
}

// 使用示例
const builder = new BlogBuilder();
builder.build().catch(console.error);

这些例子展示了 Bun.js 在实际项目中的完整应用,从简单的脚本到复杂的 Web 应用都有涉及。

相关推荐
崔庆才丨静觅8 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax