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 构建自己的项目了!
📚 教程回顾
| 章节 | 主题 |
|---|
| 01 | Deno 简介 |
| 02 | 安装与环境配置 |
| 03 | Hello World |
| 04 | TypeScript 深入 |
| 05 | 权限系统 |
| 06 | 模块系统 |
| 07 | 标准库 |
| 08 | Web API |
| 09 | 文件 I/O |
| 10 | HTTP 服务器 |
| 11 | 数据库操作 |
| 12 | 测试 |
| 13 | Fresh 框架 |
| 14 | 代码规范 |
| 15 | 部署 |
| 16 | npm 兼容性 |
| 17 | Docker 容器化 |
| 18 | 最佳实践 |
返回目录:教程首页