为什么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 应用都有涉及。

相关推荐
Olrookie2 小时前
若依前后端分离版学习笔记(十七)——ruoyi开发规范&流程,请求流程,依赖引入,组件注册&通信
前端·笔记
Keepreal4962 小时前
谈谈对闭包的理解以及常见用法
前端·javascript
luckymiaow2 小时前
告别环境配置地狱!UniApp Android 本地 一键打包神器
前端
Keepreal4962 小时前
JS加载时机
前端·javascript
itslife2 小时前
从头看 vite 源码 - 调试
前端
叫我詹躲躲2 小时前
ES2025:10个让你眼前一亮的JavaScript新特性
前端·javascript
乐~~~2 小时前
解决avue-input-tree组件重置数据不回显/重置失败
前端·javascript·vue.js
你的电影很有趣3 小时前
lesson68:JavaScript 操作 HTML 元素、属性与样式全指南
开发语言·前端·javascript
妄小闲3 小时前
html网站源码 html网页模板下载
前端·html