强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Deno 入门教程 / 第 18 章:最佳实践

第 18 章:最佳实践

18.1 项目结构

推荐目录结构

my-deno-app/
├── deno.json                  # 配置文件
├── deno.lock                  # 依赖锁文件
├── main.ts                    # 应用入口
├── src/
│   ├── config.ts              # 配置
│   ├── app.ts                 # 应用逻辑
│   ├── routes/                # 路由
│   │   ├── index.ts
│   │   └── users.ts
│   ├── services/              # 业务逻辑
│   │   └── user.service.ts
│   ├── models/                # 数据模型
│   │   └── user.ts
│   ├── middleware/             # 中间件
│   │   └── auth.ts
│   └── utils/                 # 工具函数
│       └── helpers.ts
├── tests/                     # 测试
│   ├── unit/
│   └── integration/
├── static/                    # 静态资源
└── docs/                      # 文档

deno.json 配置模板

{
  "tasks": {
    "dev": "deno run --watch --allow-net --allow-env main.ts",
    "start": "deno run --allow-net --allow-env main.ts",
    "test": "deno test --allow-net --allow-read",
    "test:coverage": "deno test --coverage=coverage",
    "lint": "deno lint",
    "fmt": "deno fmt",
    "check": "deno lint && deno fmt --check && deno test"
  },
  "lint": {
    "rules": {
      "tags": ["recommended"]
    }
  },
  "fmt": {
    "options": {
      "lineWidth": 100,
      "indentWidth": 2
    }
  },
  "imports": {
    "std/": "jsr:@std/",
    "oak": "jsr:@oak/oak@^16",
    "hono": "jsr:@hono/hono@^4"
  },
  "compilerOptions": {
    "strict": true,
    "lib": ["deno.window"]
  }
}

18.2 性能优化

冷启动优化

技巧说明效果
编译为可执行文件deno compile跳过模块解析
减少导入数量按需导入减少初始化时间
使用导入映射deno.json imports缓存优化
预热缓存CI 中 deno cache部署时已缓存

运行时优化

// ❌ 错误:每次请求重新创建连接
app.get("/users", async (c) => {
  const db = await createConnection();
  const users = await db.query("SELECT * FROM users");
  return c.json(users);
});

// ✅ 正确:使用连接池
const db = await createConnectionPool();
app.get("/users", async (c) => {
  const users = await db.query("SELECT * FROM users");
  return c.json(users);
});

缓存策略

// 内存缓存(简单场景)
const cache = new Map<string, { data: unknown; expiry: number }>();

async function getCached<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {
  const cached = cache.get(key);
  if (cached && cached.expiry > Date.now()) {
    return cached.data as T;
  }
  
  const data = await fetcher();
  cache.set(key, { data, expiry: Date.now() + ttlMs });
  return data;
}

// 使用
const users = await getCached("users", () => db.query("SELECT * FROM users"), 60_000);

流式处理大文件

// ❌ 错误:全部加载到内存
const content = await Deno.readTextFile("large-file.txt");

// ✅ 正确:流式处理
const file = await Deno.open("large-file.txt");
for await (const line of file.readable.pipeThrough(new TextDecoderStream())) {
  // 逐行处理
}
file.close();

18.3 安全策略

最小权限原则

{
  "tasks": {
    "dev": "deno run --watch --allow-net=localhost:8000 --allow-env=PORT,DB_URL main.ts",
    "test": "deno test --allow-read=./testdata",
    "prod": "deno run --allow-net --allow-env=PORT,DB_URL main.ts"
  }
}

输入验证

import { z } from "npm:zod";

const UserSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().positive(),
});

app.post("/users", async (c) => {
  const body = await c.req.json();
  const result = UserSchema.safeParse(body);
  
  if (!result.success) {
    return c.json({ error: result.error.flatten() }, 400);
  }
  
  const user = await createUser(result.data);
  return c.json(user, 201);
});

密钥管理

// ❌ 错误:硬编码密钥
const API_KEY = "sk-1234567890";

// ✅ 正确:使用环境变量
const API_KEY = Deno.env.get("API_KEY");
if (!API_KEY) throw new Error("API_KEY 未设置");

HTTPS

// 使用 HTTPS
const cert = await Deno.readTextFile("./cert.pem");
const key = await Deno.readTextFile("./key.pem");

Deno.serve({ port: 443, cert, key }, handler);

18.4 错误处理

全局错误处理

// 全局未捕获错误处理
globalThis.addEventListener("unhandledrejection", (e) => {
  console.error("未处理的 Promise 拒绝:", e.reason);
  e.preventDefault();
});

globalThis.addEventListener("error", (e) => {
  console.error("未捕获的错误:", e.error);
  e.preventDefault();
});

HTTP 错误处理

import { Hono, HTTPException } from "jsr:@hono/hono";

const app = new Hono();

// 全局错误处理中间件
app.onError((err, c) => {
  console.error(`[Error] ${err.message}`, err.stack);
  
  if (err instanceof HTTPException) {
    return c.json({ error: err.message }, err.status);
  }
  
  return c.json({ error: "服务器内部错误" }, 500);
});

// 404 处理
app.notFound((c) => {
  return c.json({ error: "页面未找到" }, 404);
});

18.5 日志最佳实践

// 结构化日志
interface LogEntry {
  timestamp: string;
  level: "info" | "warn" | "error";
  message: string;
  meta?: Record<string, unknown>;
}

function log(level: LogEntry["level"], message: string, meta?: Record<string, unknown>) {
  const entry: LogEntry = {
    timestamp: new Date().toISOString(),
    level,
    message,
    meta,
  };
  
  if (level === "error") {
    console.error(JSON.stringify(entry));
  } else {
    console.log(JSON.stringify(entry));
  }
}

// 使用
log("info", "用户登录", { userId: "123", ip: "192.168.1.1" });
log("error", "数据库连接失败", { host: "localhost", port: 5432 });

18.6 测试策略

测试金字塔

       /  E2E 测试  \        少量,验证关键流程
      /  集成测试    \       适量,验证模块协作
     /   单元测试     \      大量,验证单个函数

测试文件组织

src/
├── user.service.ts
├── user.service.test.ts     # 单元测试(就近放置)
└── __tests__/
    └── user.integration.test.ts  # 集成测试

18.7 迁移策略

从 Node.js 迁移步骤

Phase 1: 评估
├── 盘点依赖(哪些 npm 包需要使用)
├── 评估 Node.js API 使用情况
└── 确定迁移范围

Phase 2: 准备
├── 创建 deno.json 配置
├── 设置导入映射
└── 配置 CI/CD

Phase 3: 迁移
├── 替换 require() 为 import
├── 替换 __dirname 等全局变量
├── 更新 package.json 依赖
├── 运行测试确保功能正常

Phase 4: 优化
├── 使用 Deno 特有功能
├── 优化权限配置
└── 清理 Node.js 相关代码

18.8 监控与可观测性

健康检查

app.get("/health", (c) => {
  return c.json({
    status: "healthy",
    timestamp: new Date().toISOString(),
    version: Deno.env.get("APP_VERSION") || "dev",
  });
});

app.get("/ready", async (c) => {
  try {
    await db.query("SELECT 1");
    return c.json({ status: "ready" });
  } catch {
    return c.json({ status: "not ready" }, 503);
  }
});

性能指标

// 请求计时中间件
app.use("*", async (c, next) => {
  const start = performance.now();
  await next();
  const duration = performance.now() - start;
  
  c.header("X-Response-Time", `${duration.toFixed(2)}ms`);
  
  // 记录慢请求
  if (duration > 1000) {
    console.warn(`Slow request: ${c.req.method} ${c.req.url} took ${duration.toFixed(2)}ms`);
  }
});

18.9 本章小结

类别最佳实践
项目结构按功能分层,测试就近放置
性能连接池、缓存、流式处理
安全最小权限、输入验证、HTTPS
错误处理全局捕获、结构化错误
日志结构化日志、日志级别
测试测试金字塔、CI 集成
迁移渐进式迁移、评估先行

📖 扩展阅读


🎉 恭喜! 你已完成 Deno 入门教程的全部 18 章。现在你可以开始使用 Deno 构建自己的项目了!


📚 教程回顾

章节主题
01Deno 简介
02安装与环境配置
03Hello World
04TypeScript 深入
05权限系统
06模块系统
07标准库
08Web API
09文件 I/O
10HTTP 服务器
11数据库操作
12测试
13Fresh 框架
14代码规范
15部署
16npm 兼容性
17Docker 容器化
18最佳实践

返回目录教程首页