Deno
Deno 是一个简单、现代且安全的 JavaScript 和 TypeScript 运行时环境,由 Node.js 的创始人 Ryan Dahl 开发,目标是解决 Node.js 的一些设计缺陷。Deno 于2020年正式发布,它内置了 V8 JavaScript 引擎和 Tokio 事件循环,提供了一系列默认的安全限制,并支持 TypeScript 的运行而无需额外的转译步骤。

查看更多相关内容
什么是 Deno?它和 Node.js 有什么区别?Deno 是由 Node.js 创始人 Ryan Dahl 开发的现代化 JavaScript 和 TypeScript 运行时环境。Deno 的设计目标是解决 Node.js 在早期设计上的一些缺陷,并提供更安全、更简洁的开发体验。
## 核心特性
Deno 具有以下核心特性:
1. **默认安全**:Deno 默认情况下脚本无法访问文件系统、网络或环境变量,除非显式授予相应权限
2. **内置 TypeScript 支持**:Deno 原生支持 TypeScript,无需额外的转译步骤
3. **去中心化模块系统**:使用 URL 导入模块,不依赖 package.json 和 node_modules
4. **单一可执行文件**:Deno 只有一个可执行文件,无需复杂的安装过程
5. **内置工具链**:包含格式化、测试、linting、打包等开发工具
6. **标准库**:提供经过审核的标准库,确保代码质量
## 与 Node.js 的主要区别
| 特性 | Deno | Node.js |
|------|------|---------|
| 模块系统 | ES Modules (URL 导入) | CommonJS + ES Modules |
| 包管理 | 无 package.json,直接使用 URL | npm + package.json |
| 安全性 | 默认安全,需要显式授权 | 默认无安全限制 |
| TypeScript | 原生支持 | 需要配置和转译 |
| API 设计 | Promise-based | 回调和 Promise 混合 |
| 架构 | Rust + V8 | C++ + V8 |
## 权限系统
Deno 的权限系统是其安全特性的核心:
```bash
# 授予文件系统读取权限
deno run --allow-read script.ts
# 授予网络访问权限
deno run --allow-net script.ts
# 授予所有权限(不推荐)
deno run --allow-all script.ts
```
## 模块导入示例
```typescript
// 直接从 URL 导入模块
import { serve } from "https://deno.land/std/http/server.ts";
// 从本地文件导入
import { myFunction } from "./utils.ts";
// 使用标准库
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
```
## 典型应用场景
- Web 服务器和 API 开发
- 命令行工具开发
- 自动化脚本
- 微服务架构
- 实时应用(WebSocket)
Deno 特别适合需要快速开发、安全性和现代化开发体验的项目。
服务端 · 2月21日 16:10
Deno 如何支持 TypeScript?Deno 原生支持 TypeScript,这是其最吸引人的特性之一。与 Node.js 需要配置 TypeScript 编译器不同,Deno 可以直接运行 TypeScript 文件,无需任何额外的转译步骤。
## TypeScript 支持概述
Deno 内置了 TypeScript 编译器,使用 V8 引擎的 TypeScript 支持和 swc 编译器来提供快速的类型检查和转译。
## 直接运行 TypeScript
### 1. 基本用法
```typescript
// app.ts
interface User {
id: number;
name: string;
email: string;
}
function createUser(user: User): User {
console.log(`Creating user: ${user.name}`);
return user;
}
const newUser: User = {
id: 1,
name: "John Doe",
email: "john@example.com"
};
createUser(newUser);
```
运行:
```bash
deno run app.ts
```
### 2. 类型检查
Deno 会在运行时进行类型检查:
```bash
# 运行时进行类型检查
deno run app.ts
# 仅类型检查(不执行)
deno check app.ts
# 类型检查所有文件
deno check **/*.ts
```
## 类型定义
### 1. 使用 Deno 内置类型
Deno 提供了丰富的内置类型定义:
```typescript
// 文件系统操作
const content: string = await Deno.readTextFile("./data.txt");
// HTTP 服务器
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const handler = async (req: Request): Promise<Response> => {
return new Response("Hello World");
};
```
### 2. 第三方库类型
从 URL 导入的模块通常包含类型定义:
```typescript
import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";
const app = new Application();
const router = new Router();
```
### 3. 自定义类型定义
如果模块没有类型定义,可以创建自定义类型:
```typescript
// types.d.ts
declare module "https://example.com/module.js" {
export function doSomething(): void;
export const value: number;
}
```
## 配置选项
### 1. tsconfig.json
Deno 支持使用 `tsconfig.json` 进行配置:
```json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
### 2. 命令行选项
```bash
# 禁用类型检查(不推荐)
deno run --no-check app.ts
# 使用特定的 tsconfig
deno run --config=tsconfig.json app.ts
# 启用严格模式
deno run app.ts # 默认启用严格模式
```
## 类型检查策略
### 1. 本地类型检查
```bash
# 检查本地文件
deno check app.ts
# 检查整个项目
deno check src/**/*.ts
```
### 2. 远程类型检查
Deno 会缓存远程模块的类型定义:
```bash
# 重新下载并检查远程类型
deno check --remote app.ts
# 清除类型缓存
deno cache --reload app.ts
```
## 实际应用示例
### 1. 类型安全的 API 服务器
```typescript
// api-server.ts
interface User {
id: number;
name: string;
email: string;
}
interface CreateUserRequest {
name: string;
email: string;
}
const users: Map<number, User> = new Map();
function createUser(request: CreateUserRequest): User {
const id = users.size + 1;
const user: User = { id, ...request };
users.set(id, user);
return user;
}
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/users") {
try {
const body: CreateUserRequest = await req.json();
const user = createUser(body);
return new Response(JSON.stringify(user), {
headers: { "Content-Type": "application/json" }
});
} catch (error) {
return new Response("Invalid request", { status: 400 });
}
}
return new Response("Not Found", { status: 404 });
};
await serve(handler, { port: 8000 });
```
### 2. 类型安全的数据库操作
```typescript
// database.ts
interface DatabaseRecord {
id: string;
createdAt: Date;
updatedAt: Date;
}
interface Post extends DatabaseRecord {
title: string;
content: string;
authorId: string;
}
class Database<T extends DatabaseRecord> {
private records: Map<string, T> = new Map();
async create(record: Omit<T, keyof DatabaseRecord>): Promise<T> {
const id = crypto.randomUUID();
const now = new Date();
const newRecord: T = {
id,
createdAt: now,
updatedAt: now,
...record
} as T;
this.records.set(id, newRecord);
return newRecord;
}
async findById(id: string): Promise<T | undefined> {
return this.records.get(id);
}
}
const postDB = new Database<Post();
const newPost = await postDB.create({
title: "Hello Deno",
content: "TypeScript support is amazing!",
authorId: "user-1"
});
```
### 3. 类型安全的工具函数
```typescript
// utils.ts
type AsyncFunction<T = any> = (...args: any[]) => Promise<T>;
async function retry<T>(
fn: AsyncFunction<T>,
maxAttempts: number = 3,
delay: number = 1000
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
async function fetchWithTimeout(
url: string,
timeout: number = 5000
): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
```
## 类型检查性能优化
### 1. 增量类型检查
Deno 会缓存类型检查结果:
```bash
# 首次运行会进行完整类型检查
deno run app.ts
# 后续运行会使用缓存
deno run app.ts
```
### 2. 选择性类型检查
```bash
# 只检查修改的文件
deno check app.ts
# 排除某些文件
deno check --exclude=**/*.test.ts src/**/*.ts
```
## 与 Node.js 的对比
| 特性 | Deno | Node.js |
|------|------|---------|
| TypeScript 支持 | 原生支持 | 需要配置 tsc |
| 运行方式 | 直接运行 .ts 文件 | 需要先编译为 .js |
| 类型检查 | 运行时检查 | 编译时检查 |
| 配置复杂度 | 零配置 | 需要 tsconfig.json |
| 性能 | 使用 swc 快速编译 | 使用 tsc 较慢 |
| 类型定义 | 自动加载 | 需要 @types 包 |
## 最佳实践
1. **始终使用类型**:充分利用 TypeScript 的类型系统
2. **启用严格模式**:使用 `strict: true` 获得更好的类型安全
3. **使用接口定义数据结构**:明确 API 和数据模型的类型
4. **避免 any 类型**:使用 unknown 或具体类型替代
5. **定期运行类型检查**:使用 `deno check` 确保代码质量
6. **利用泛型**:编写可复用的类型安全代码
Deno 的 TypeScript 支持使得开发者能够享受类型安全的好处,而无需复杂的配置和构建流程,大大提高了开发效率和代码质量。
服务端 · 2月21日 16:10
Deno 的测试框架如何使用?Deno 的测试框架提供了强大而简洁的测试功能,使得编写和运行测试变得简单高效。了解 Deno 的测试系统对于保证代码质量至关重要。
## 测试框架概述
Deno 内置了测试框架,无需安装额外的测试库。测试文件通常以 `_test.ts` 或 `.test.ts` 结尾。
## 基本测试
### 1. 编写第一个测试
```typescript
// math_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";
function add(a: number, b: number): number {
return a + b;
}
Deno.test("add function adds two numbers", () => {
assertEquals(add(1, 2), 3);
assertEquals(add(-1, 1), 0);
assertEquals(add(0, 0), 0);
});
```
运行测试:
```bash
deno test math_test.ts
```
### 2. 测试异步代码
```typescript
// async_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";
async function fetchData(id: number): Promise<{ id: number; name: string }> {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 100));
return { id, name: `Item ${id}` };
}
Deno.test("fetchData returns correct data", async () => {
const result = await fetchData(1);
assertEquals(result.id, 1);
assertEquals(result.name, "Item 1");
});
```
## 断言函数
### 1. 常用断言
```typescript
import {
assertEquals,
assertNotEquals,
assertExists,
assertStrictEquals,
assertStringIncludes,
assertArrayIncludes,
assertMatch,
assertThrows,
assertRejects,
} from "https://deno.land/std@0.208.0/testing/asserts.ts";
Deno.test("assertEquals examples", () => {
assertEquals(1 + 1, 2);
assertEquals("hello", "hello");
assertEquals({ a: 1 }, { a: 1 });
});
Deno.test("assertNotEquals examples", () => {
assertNotEquals(1, 2);
assertNotEquals("hello", "world");
});
Deno.test("assertExists examples", () => {
assertExists("hello");
assertExists(42);
assertExists({ key: "value" });
});
Deno.test("assertStrictEquals examples", () => {
assertStrictEquals(1, 1);
assertStrictEquals("hello", "hello");
// assertStrictEquals({ a: 1 }, { a: 1 }); // 会失败,因为引用不同
});
Deno.test("assertStringIncludes examples", () => {
assertStringIncludes("hello world", "world");
assertStringIncludes("Deno is awesome", "Deno");
});
Deno.test("assertArrayIncludes examples", () => {
assertArrayIncludes([1, 2, 3], [2]);
assertArrayIncludes(["a", "b", "c"], ["b", "c"]);
});
Deno.test("assertMatch examples", () => {
assertMatch("hello@deno.com", /@/);
assertMatch("12345", /^\d+$/);
});
```
### 2. 测试异常
```typescript
import { assertThrows, assertRejects } from "https://deno.land/std@0.208.0/testing/asserts.ts";
function divide(a: number, b: number): number {
if (b === 0) {
throw new Error("Division by zero");
}
return a / b;
}
Deno.test("divide throws error on zero", () => {
assertThrows(
() => divide(10, 0),
Error,
"Division by zero"
);
});
async function asyncDivide(a: number, b: number): Promise<number> {
if (b === 0) {
throw new Error("Division by zero");
}
return a / b;
}
Deno.test("asyncDivide rejects on zero", async () => {
await assertRejects(
() => asyncDivide(10, 0),
Error,
"Division by zero"
);
});
```
## 测试配置
### 1. 测试选项
```typescript
Deno.test({
name: "test with options",
fn: () => {
// 测试代码
},
permissions: {
read: true,
net: true,
},
sanitizeOps: true,
sanitizeResources: true,
sanitizeExit: true,
});
```
### 2. 超时设置
```typescript
Deno.test({
name: "slow test with timeout",
fn: async () => {
await new Promise(resolve => setTimeout(resolve, 2000));
},
timeout: 5000, // 5秒超时
});
```
### 3. 忽略测试
```typescript
Deno.test({
name: "ignored test",
ignore: true,
fn: () => {
// 这个测试会被跳过
},
});
// 或者使用 only 只运行特定测试
Deno.test({
name: "only this test",
only: true,
fn: () => {
// 只运行这个测试
},
});
```
## 测试组织
### 1. 测试套件
```typescript
// user_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";
class User {
constructor(
public id: number,
public name: string,
public email: string
) {}
greet(): string {
return `Hello, ${this.name}!`;
}
isValid(): boolean {
return this.id > 0 && this.name.length > 0 && this.email.includes("@");
}
}
Deno.test("User class - constructor", () => {
const user = new User(1, "John", "john@example.com");
assertEquals(user.id, 1);
assertEquals(user.name, "John");
assertEquals(user.email, "john@example.com");
});
Deno.test("User class - greet", () => {
const user = new User(1, "John", "john@example.com");
assertEquals(user.greet(), "Hello, John!");
});
Deno.test("User class - isValid", () => {
const validUser = new User(1, "John", "john@example.com");
assertEquals(validUser.isValid(), true);
const invalidUser = new User(0, "", "invalid");
assertEquals(invalidUser.isValid(), false);
});
```
### 2. 测试钩子
```typescript
Deno.test({
name: "test with setup and teardown",
async fn() {
// Setup
const db = await connectDatabase();
try {
// Test
const user = await db.createUser({ name: "John" });
assertEquals(user.name, "John");
} finally {
// Teardown
await db.close();
}
},
sanitizeOps: false,
sanitizeResources: false,
});
```
## Mock 和 Stub
### 1. 简单的 Mock
```typescript
// api_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";
interface APIClient {
fetchData(id: number): Promise<any>;
}
class RealAPIClient implements APIClient {
async fetchData(id: number): Promise<any> {
const response = await fetch(`https://api.example.com/data/${id}`);
return response.json();
}
}
class MockAPIClient implements APIClient {
private data: Map<number, any> = new Map();
setData(id: number, data: any) {
this.data.set(id, data);
}
async fetchData(id: number): Promise<any> {
return this.data.get(id);
}
}
Deno.test("MockAPIClient returns mock data", async () => {
const mockClient = new MockAPIClient();
mockClient.setData(1, { id: 1, name: "Test" });
const result = await mockClient.fetchData(1);
assertEquals(result.id, 1);
assertEquals(result.name, "Test");
});
```
### 2. 使用 Spy
```typescript
// spy_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";
class Spy<T extends (...args: any[]) => any> {
calls: Array<{ args: Parameters<T>; result: ReturnType<T> }> = [];
wrap(fn: T): T {
return ((...args: Parameters<T>) => {
const result = fn(...args);
this.calls.push({ args, result });
return result;
}) as T;
}
callCount(): number {
return this.calls.length;
}
calledWith(...args: Parameters<T>): boolean {
return this.calls.some(call =>
JSON.stringify(call.args) === JSON.stringify(args)
);
}
}
function processData(data: string, callback: (result: string) => void) {
const result = data.toUpperCase();
callback(result);
}
Deno.test("processData calls callback", () => {
const spy = new Spy<(result: string) => void>();
const callbackSpy = spy.wrap((result: string) => {
console.log(result);
});
processData("hello", callbackSpy);
assertEquals(spy.callCount(), 1);
assertEquals(spy.calledWith("HELLO"), true);
});
```
## 集成测试
### 1. HTTP 服务器测试
```typescript
// server_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
async function startTestServer(): Promise<{ port: number; stop: () => Promise<void> }> {
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Hello, World!");
}
if (url.pathname === "/api/users") {
return new Response(JSON.stringify([{ id: 1, name: "John" }]), {
headers: { "Content-Type": "application/json" },
});
}
return new Response("Not Found", { status: 404 });
};
const server = await serve(handler, { port: 0 }); // 使用随机端口
const port = (server.addr as Deno.NetAddr).port;
return {
port,
stop: async () => {
server.shutdown();
},
};
}
Deno.test("HTTP server responds to root", async () => {
const { port, stop } = await startTestServer();
try {
const response = await fetch(`http://localhost:${port}/`);
assertEquals(response.status, 200);
assertEquals(await response.text(), "Hello, World!");
} finally {
await stop();
}
});
Deno.test("HTTP server returns JSON for /api/users", async () => {
const { port, stop } = await startTestServer();
try {
const response = await fetch(`http://localhost:${port}/api/users`);
assertEquals(response.status, 200);
assertEquals(response.headers.get("Content-Type"), "application/json");
const data = await response.json();
assertEquals(Array.isArray(data), true);
assertEquals(data[0].name, "John");
} finally {
await stop();
}
});
```
### 2. 文件系统测试
```typescript
// file_test.ts
import { assertEquals, assertExists } from "https://deno.land/std@0.208.0/testing/asserts.ts";
async function createTestDirectory(): Promise<string> {
const testDir = await Deno.makeTempDir();
return testDir;
}
Deno.test("file operations", async () => {
const testDir = await createTestDirectory();
try {
const filePath = `${testDir}/test.txt`;
const content = "Hello, Deno!";
// 写入文件
await Deno.writeTextFile(filePath, content);
// 读取文件
const readContent = await Deno.readTextFile(filePath);
assertEquals(readContent, content);
// 检查文件存在
const stat = await Deno.stat(filePath);
assertExists(stat);
assertEquals(stat.isFile, true);
} finally {
// 清理测试目录
await Deno.remove(testDir, { recursive: true });
}
});
```
## 测试覆盖率
### 1. 生成覆盖率报告
```bash
# 运行测试并生成覆盖率
deno test --coverage=coverage
# 生成覆盖率报告
deno coverage coverage --lcov --output=coverage.lcov
# 在浏览器中查看覆盖率
deno coverage coverage --html
```
### 2. 覆盖率配置
```json
// deno.json
{
"compilerOptions": {
"strict": true
},
"test": {
"include": ["src/**/*_test.ts", "tests/**/*.ts"],
"exclude": ["node_modules/"]
}
}
```
## 测试最佳实践
### 1. 测试命名
```typescript
// 好的测试名称
Deno.test("add returns sum of two numbers", () => {});
Deno.test("divide throws error when dividing by zero", () => {});
Deno.test("fetchData returns user object when given valid ID", () => {});
// 不好的测试名称
Deno.test("test add", () => {});
Deno.test("it works", () => {});
```
### 2. AAA 模式(Arrange-Act-Assert)
```typescript
Deno.test("calculateTotal returns correct total", () => {
// Arrange - 准备测试数据
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 },
];
const expectedTotal = 35;
// Act - 执行被测试的操作
const total = calculateTotal(items);
// Assert - 验证结果
assertEquals(total, expectedTotal);
});
```
### 3. 测试隔离
```typescript
Deno.test("user creation", async () => {
// 每个测试使用独立的数据库连接
const db = await createTestDatabase();
try {
const user = await db.createUser({ name: "John" });
assertEquals(user.name, "John");
} finally {
// 清理资源
await db.close();
}
});
```
### 4. 测试数据构建器
```typescript
// test-builder.ts
class UserBuilder {
private user: Partial<User> = {
id: 1,
name: "John Doe",
email: "john@example.com",
};
withId(id: number): UserBuilder {
this.user.id = id;
return this;
}
withName(name: string): UserBuilder {
this.user.name = name;
return this;
}
withEmail(email: string): UserBuilder {
this.user.email = email;
return this;
}
build(): User {
return this.user as User;
}
}
// 使用构建器创建测试数据
Deno.test("user validation", () => {
const user = new UserBuilder()
.withId(1)
.withName("John")
.withEmail("john@example.com")
.build();
assertEquals(user.isValid(), true);
});
```
## 运行测试
### 1. 基本命令
```bash
# 运行所有测试
deno test
# 运行特定测试文件
deno test math_test.ts
# 监听模式(文件变化时自动运行)
deno test --watch
# 并行运行测试
deno test --parallel
# 显示详细输出
deno test --verbose
# 只运行失败的测试
deno test --fail-fast
# 允许所有权限
deno test --allow-all
```
### 2. 过滤测试
```bash
# 运行匹配模式的测试
deno test --filter="user"
# 运行特定测试
deno test --filter="add function"
```
## 最佳实践总结
1. **测试独立性**:每个测试应该独立运行,不依赖其他测试
2. **清晰的命名**:测试名称应该清楚地描述测试的内容
3. **AAA 模式**:使用 Arrange-Act-Assert 模式组织测试代码
4. **适当的断言**:使用最合适的断言函数
5. **测试边界情况**:测试正常情况和边界情况
6. **保持简单**:测试应该简单、快速、易于理解
7. **定期运行**:在 CI/CD 中定期运行测试
8. **覆盖率监控**:监控测试覆盖率,确保代码质量
Deno 的测试框架提供了强大而简洁的功能,通过合理使用这些功能,可以构建高质量的测试套件,确保代码的可靠性和可维护性。
服务端 · 2月21日 16:08
Deno 的任务系统如何工作?Deno 的任务系统(Task System)提供了一种在后台运行异步任务的方式,类似于浏览器中的 Web Workers。这个功能对于执行 CPU 密集型任务或需要并行处理的场景非常有用。
## 任务系统概述
Deno 的任务系统允许你创建独立的工作线程,这些线程可以并行执行代码,不会阻塞主线程。每个任务都有自己的内存空间,通过消息传递与主线程通信。
## 基本用法
### 1. 创建简单任务
```typescript
// main.ts
const worker = new Worker(new URL("./worker.ts", import.meta.url).href, {
type: "module",
});
worker.postMessage({ type: "start", data: 42 });
worker.onmessage = (event) => {
console.log("Received from worker:", event.data);
worker.terminate();
};
worker.onerror = (error) => {
console.error("Worker error:", error);
};
```
```typescript
// worker.ts
self.onmessage = (event) => {
console.log("Worker received:", event.data);
const result = event.data.data * 2;
self.postMessage({ type: "result", data: result });
};
```
运行:
```bash
deno run --allow-read main.ts
```
### 2. 使用 Promise 封装 Worker
```typescript
// main.ts
function runWorker<T>(workerFile: string, data: any): Promise<T> {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL(workerFile, import.meta.url).href, {
type: "module",
});
worker.postMessage(data);
worker.onmessage = (event) => {
resolve(event.data);
worker.terminate();
};
worker.onerror = (error) => {
reject(error);
worker.terminate();
};
});
}
async function main() {
try {
const result = await runWorker<number>("./worker.ts", { number: 10 });
console.log("Result:", result);
} catch (error) {
console.error("Error:", error);
}
}
main();
```
```typescript
// worker.ts
self.onmessage = (event) => {
const { number } = event.data;
// 模拟耗时计算
let result = 0;
for (let i = 0; i < number * 1000000; i++) {
result += i;
}
self.postMessage(result);
};
```
## 实际应用示例
### 1. 图像处理
```typescript
// image-processor.ts
self.onmessage = async (event) => {
const { imageData, operation } = event.data;
let result;
switch (operation) {
case "grayscale":
result = applyGrayscale(imageData);
break;
case "invert":
result = applyInvert(imageData);
break;
case "blur":
result = applyBlur(imageData);
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
self.postMessage({ result });
};
function applyGrayscale(data: Uint8ClampedArray): Uint8ClampedArray {
const result = new Uint8ClampedArray(data.length);
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
result[i] = gray;
result[i + 1] = gray;
result[i + 2] = gray;
result[i + 3] = data[i + 3];
}
return result;
}
function applyInvert(data: Uint8ClampedArray): Uint8ClampedArray {
const result = new Uint8ClampedArray(data.length);
for (let i = 0; i < data.length; i += 4) {
result[i] = 255 - data[i];
result[i + 1] = 255 - data[i + 1];
result[i + 2] = 255 - data[i + 2];
result[i + 3] = data[i + 3];
}
return result;
}
function applyBlur(data: Uint8ClampedArray): Uint8ClampedArray {
// 简化的模糊算法
return data; // 实际实现会更复杂
}
```
```typescript
// main.ts
import { runWorker } from "./worker-utils.ts";
async function processImage(imagePath: string) {
const imageData = await Deno.readFile(imagePath);
const grayscaleResult = await runWorker<Uint8ClampedArray>(
"./image-processor.ts",
{ imageData, operation: "grayscale" }
);
await Deno.writeFile(`${imagePath}.grayscale.png`, grayscaleResult);
const invertResult = await runWorker<Uint8ClampedArray>(
"./image-processor.ts",
{ imageData, operation: "invert" }
);
await Deno.writeFile(`${imagePath}.invert.png`, invertResult);
console.log("Image processing complete");
}
processImage("input.png");
```
### 2. 并行数据处理
```typescript
// data-processor.ts
self.onmessage = (event) => {
const { data, chunkIndex, totalChunks } = event.data;
console.log(`Processing chunk ${chunkIndex}/${totalChunks}`);
// 模拟数据处理
const processed = data.map((item: number) => ({
value: item,
processed: true,
timestamp: Date.now(),
}));
self.postMessage({ chunkIndex, processed });
};
```
```typescript
// main.ts
import { runWorker } from "./worker-utils.ts";
async function processDataInParallel(data: number[], chunkSize: number = 1000) {
const chunks: number[][] = [];
for (let i = 0; i < data.length; i += chunkSize) {
chunks.push(data.slice(i, i + chunkSize));
}
console.log(`Processing ${chunks.length} chunks in parallel`);
const promises = chunks.map((chunk, index) =>
runWorker("./data-processor.ts", {
data: chunk,
chunkIndex: index,
totalChunks: chunks.length,
})
);
const results = await Promise.all(promises);
// 合并结果
const processedData = results
.sort((a, b) => a.chunkIndex - b.chunkIndex)
.flatMap((result) => result.processed);
console.log(`Processed ${processedData.length} items`);
return processedData;
}
// 生成测试数据
const testData = Array.from({ length: 10000 }, (_, i) => i);
processDataInParallel(testData, 1000);
```
### 3. 文件批量处理
```typescript
// file-processor.ts
self.onmessage = async (event) => {
const { filePath, operation } = event.data;
try {
const content = await Deno.readTextFile(filePath);
let result: string;
switch (operation) {
case "uppercase":
result = content.toUpperCase();
break;
case "lowercase":
result = content.toLowerCase();
break;
case "reverse":
result = content.split("").reverse().join("");
break;
case "count":
result = String(content.length);
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
self.postMessage({ filePath, result, success: true });
} catch (error) {
self.postMessage({ filePath, error: error.message, success: false });
}
};
```
```typescript
// main.ts
import { runWorker } from "./worker-utils.ts";
async function processFilesInParallel(
files: string[],
operation: string
) {
console.log(`Processing ${files.length} files with operation: ${operation}`);
const promises = files.map((file) =>
runWorker("./file-processor.ts", { filePath: file, operation })
);
const results = await Promise.all(promises);
results.forEach((result) => {
if (result.success) {
console.log(`✓ ${result.filePath}: ${result.result.substring(0, 50)}...`);
} else {
console.error(`✗ ${result.filePath}: ${result.error}`);
}
});
return results;
}
// 获取当前目录所有 .txt 文件
const files = Array.from(Deno.readDirSync("."))
.filter((entry) => entry.isFile && entry.name.endsWith(".txt"))
.map((entry) => entry.name);
processFilesInParallel(files, "uppercase");
```
### 4. 密码哈希计算
```typescript
// password-hasher.ts
self.onmessage = async (event) => {
const { password, algorithm = "SHA-256" } = event.data;
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest(algorithm, data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
self.postMessage({ password, hash: hashHex, algorithm });
};
```
```typescript
// main.ts
import { runWorker } from "./worker-utils.ts";
async function hashPasswords(passwords: string[]) {
console.log(`Hashing ${passwords.length} passwords`);
const promises = passwords.map((password) =>
runWorker("./password-hasher.ts", { password })
);
const results = await Promise.all(promises);
results.forEach((result) => {
console.log(`${result.password}: ${result.hash}`);
});
return results;
}
const passwords = ["password123", "admin", "user123", "test"];
hashPasswords(passwords);
```
## 高级用法
### 1. Worker 池
```typescript
// worker-pool.ts
export class WorkerPool {
private workers: Worker[] = [];
private taskQueue: Array<{ data: any; resolve: (value: any) => void; reject: (error: any) => void }> = [];
private maxWorkers: number;
constructor(workerFile: string, maxWorkers: number = 4) {
this.maxWorkers = maxWorkers;
for (let i = 0; i < maxWorkers; i++) {
const worker = new Worker(new URL(workerFile, import.meta.url).href, {
type: "module",
});
worker.onmessage = (event) => {
const task = this.taskQueue.shift();
if (task) {
task.resolve(event.data);
this.assignNextTask(worker);
}
};
worker.onerror = (error) => {
const task = this.taskQueue.shift();
if (task) {
task.reject(error);
this.assignNextTask(worker);
}
};
this.workers.push(worker);
}
}
private assignNextTask(worker: Worker) {
const task = this.taskQueue[0];
if (task) {
worker.postMessage(task.data);
}
}
async execute(data: any): Promise<any> {
return new Promise((resolve, reject) => {
this.taskQueue.push({ data, resolve, reject });
// 查找空闲的 worker
const idleWorker = this.workers.find((w) => !this.taskQueue.includes(w));
if (idleWorker) {
this.assignNextTask(idleWorker);
}
});
}
terminate() {
this.workers.forEach((worker) => worker.terminate());
this.workers = [];
}
}
```
使用 Worker 池:
```typescript
// main.ts
import { WorkerPool } from "./worker-pool.ts";
const pool = new WorkerPool("./data-processor.ts", 4);
async function processWithPool(data: number[]) {
const promises = data.map((item) => pool.execute({ data: item }));
const results = await Promise.all(promises);
pool.terminate();
return results;
}
processWithPool([1, 2, 3, 4, 5, 6, 7, 8]);
```
### 2. 错误处理和重试
```typescript
// worker-with-retry.ts
export async function runWorkerWithRetry<T>(
workerFile: string,
data: any,
maxRetries: number = 3
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await runWorker<T>(workerFile, data);
} catch (error) {
lastError = error as Error;
console.error(`Attempt ${attempt} failed: ${error.message}`);
if (attempt < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
}
}
}
throw lastError;
}
```
## 最佳实践
1. **合理使用 Worker**:只在 CPU 密集型任务中使用 Worker
2. **控制并发**:限制同时运行的 Worker 数量
3. **正确清理**:使用完成后终止 Worker
4. **错误处理**:妥善处理 Worker 错误
5. **消息大小**:避免传递过大的消息
6. **类型安全**:使用 TypeScript 确保消息类型正确
Deno 的任务系统为并行处理和后台任务提供了强大的支持,能够显著提高应用程序的性能和响应能力。
服务端 · 2月21日 16:08
Deno 的标准库有哪些常用模块?Deno 的标准库(Standard Library)是一组经过精心设计、测试和维护的模块,为开发者提供了高质量、可复用的代码。标准库涵盖了从文件系统操作到网络编程的各个方面,是 Deno 生态系统的重要组成部分。
## 标准库概述
Deno 标准库托管在 `https://deno.land/std/`,所有模块都经过严格的代码审查和测试,确保代码质量和安全性。
### 版本管理
标准库使用语义化版本控制,建议在导入时指定版本:
```typescript
// 推荐:指定版本
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
// 不推荐:使用最新版本
import { serve } from "https://deno.land/std/http/server.ts";
```
## 主要模块
### 1. HTTP 模块
#### HTTP 服务器
```typescript
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Hello, Deno!", {
headers: { "content-type": "text/plain" },
});
}
return new Response("Not Found", { status: 404 });
};
await serve(handler, { port: 8000 });
```
#### HTTP 客户端
```typescript
import { fetch } from "https://deno.land/std@0.208.0/http/fetch.ts";
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
```
### 2. 文件系统模块
#### 文件操作
```typescript
import { ensureDir, ensureFile } from "https://deno.land/std@0.208.0/fs/mod.ts";
// 确保目录存在
await ensureDir("./data/uploads");
// 确保文件存在
await ensureFile("./config.json");
// 复制文件
import { copy } from "https://deno.land/std@0.208.0/fs/copy.ts";
await copy("source.txt", "destination.txt");
// 移动文件
import { move } from "https://deno.land/std@0.208.0/fs/move.ts";
await move("old.txt", "new.txt");
```
#### 遍历目录
```typescript
import { walk } from "https://deno.land/std@0.208.0/fs/walk.ts";
for await (const entry of walk("./src")) {
console.log(entry.path);
if (entry.isFile) {
console.log(`File: ${entry.name}`);
} else if (entry.isDirectory) {
console.log(`Directory: ${entry.name}`);
}
}
```
### 3. 路径模块
```typescript
import { join, basename, dirname, extname, resolve } from "https://deno.land/std@0.208.0/path/mod.ts";
const path = "/home/user/documents/file.txt";
console.log(basename(path)); // "file.txt"
console.log(dirname(path)); // "/home/user/documents"
console.log(extname(path)); // ".txt"
console.log(join("/home", "user", "docs")); // "/home/user/docs"
console.log(resolve("./src", "file.ts")); // 绝对路径
```
### 4. 编码模块
#### Base64 编码
```typescript
import { encodeBase64, decodeBase64 } from "https://deno.land/std@0.208.0/encoding/base64.ts";
const text = "Hello, Deno!";
const encoded = encodeBase64(text);
console.log(encoded); // "SGVsbG8sIERlbm8h"
const decoded = decodeBase64(encoded);
console.log(decoded); // "Hello, Deno!"
```
#### Hex 编码
```typescript
import { encodeHex, decodeHex } from "https://deno.land/std@0.208.0/encoding/hex.ts";
const data = new TextEncoder().encode("Hello");
const hex = encodeHex(data);
console.log(hex); // "48656c6c6f"
const decoded = decodeHex(hex);
console.log(new TextDecoder().decode(decoded)); // "Hello"
```
### 5. 测试模块
```typescript
import { assertEquals, assertThrows } from "https://deno.land/std@0.208.0/testing/asserts.ts";
Deno.test("assertEquals example", () => {
assertEquals(1 + 1, 2);
assertEquals("hello", "hello");
});
Deno.test("assertThrows example", () => {
assertThrows(
() => {
throw new Error("Test error");
},
Error,
"Test error"
);
});
```
### 6. 日志模块
```typescript
import { getLogger, setup, handlers } from "https://deno.land/std@0.208.0/log/mod.ts";
await setup({
handlers: {
console: new handlers.ConsoleHandler("INFO"),
},
loggers: {
default: {
level: "INFO",
handlers: ["console"],
},
},
});
const logger = getLogger();
logger.info("Application started");
logger.warning("This is a warning");
logger.error("An error occurred");
```
### 7. UUID 模块
```typescript
import { v4 as uuidv4, v5 as uuidv5 } from "https://deno.land/std@0.208.0/uuid/mod.ts";
const id1 = uuidv4();
console.log(id1); // 生成随机 UUID
const id2 = uuidv5("hello", uuidv4());
console.log(id2); // 基于命名空间生成 UUID
```
### 8. 日期时间模块
```typescript
import { format, parse } from "https://deno.land/std@0.208.0/datetime/mod.ts";
const now = new Date();
const formatted = format(now, "yyyy-MM-dd HH:mm:ss");
console.log(formatted); // "2024-01-15 10:30:45"
const parsed = parse("2024-01-15", "yyyy-MM-dd");
console.log(parsed); // Date 对象
```
### 9. 颜色模块
```typescript
import { red, green, blue, bold } from "https://deno.land/std@0.208.0/fmt/colors.ts";
console.log(red("Error message"));
console.log(green("Success message"));
console.log(blue("Info message"));
console.log(bold("Important message"));
```
### 10. 异步模块
```typescript
import { delay, retry } from "https://deno.land/std@0.208.0/async/mod.ts";
// 延迟执行
await delay(1000); // 等待 1 秒
// 重试机制
const result = await retry(async () => {
const response = await fetch("https://api.example.com");
if (!response.ok) throw new Error("Request failed");
return response.json();
}, {
maxAttempts: 3,
minTimeout: 1000,
});
```
### 11. 流模块
```typescript
import { copy } from "https://deno.land/std@0.208.0/streams/copy.ts";
const file = await Deno.open("input.txt");
const output = await Deno.open("output.txt", { create: true, write: true });
await copy(file, output);
file.close();
output.close();
```
### 12. 命令行模块
```typescript
import { parseArgs } from "https://deno.land/std@0.208.0/cli/parse_args.ts";
const args = parseArgs(Deno.args, {
boolean: ["verbose", "help"],
string: ["output"],
default: { verbose: false },
});
console.log(args);
// 运行: deno run script.ts --verbose --output=result.txt
// 输出: { _: [], verbose: true, help: false, output: "result.txt" }
```
## 实际应用示例
### 文件上传服务器
```typescript
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
import { ensureDir } from "https://deno.land/std@0.208.0/fs/mod.ts";
import { extname } from "https://deno.land/std@0.208.0/path/mod.ts";
const UPLOAD_DIR = "./uploads";
await ensureDir(UPLOAD_DIR);
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/upload") {
const formData = await req.formData();
const file = formData.get("file") as File;
if (file) {
const filename = `${crypto.randomUUID()}${extname(file.name)}`;
const filepath = `${UPLOAD_DIR}/${filename}`;
const content = await file.arrayBuffer();
await Deno.writeFile(filepath, new Uint8Array(content));
return new Response(JSON.stringify({ filename }), {
headers: { "Content-Type": "application/json" },
});
}
}
return new Response("Not Found", { status: 404 });
};
await serve(handler, { port: 8000 });
```
### 日志记录系统
```typescript
import { getLogger, setup, handlers, LogRecord } from "https://deno.land/std@0.208.0/log/mod.ts";
class CustomHandler extends handlers.BaseHandler {
override format(logRecord: LogRecord): string {
const { levelName, msg, datetime } = logRecord;
return `[${datetime.toISOString()}] [${levelName}] ${msg}`;
}
}
await setup({
handlers: {
console: new CustomHandler("DEBUG"),
},
loggers: {
default: {
level: "DEBUG",
handlers: ["console"],
},
},
});
const logger = getLogger();
async function processData(data: any) {
logger.debug(`Processing data: ${JSON.stringify(data)}`);
try {
const result = await performOperation(data);
logger.info("Operation completed successfully");
return result;
} catch (error) {
logger.error(`Operation failed: ${error.message}`);
throw error;
}
}
```
## 标准库的优势
1. **高质量代码**:所有模块都经过严格审查和测试
2. **类型安全**:完整的 TypeScript 类型定义
3. **文档完善**:详细的 API 文档和示例
4. **定期更新**:持续维护和功能增强
5. **一致性**:统一的代码风格和 API 设计
6. **安全可靠**:经过安全审计,无已知漏洞
## 最佳实践
1. **指定版本**:始终使用特定版本的标准库
2. **优先使用**:在可能的情况下优先使用标准库而非第三方库
3. **查看文档**:使用 `deno doc` 查看模块文档
4. **贡献代码**:发现问题时可以提交 PR 贡献代码
5. **关注更新**:定期查看标准库的更新日志
Deno 标准库为开发者提供了强大而可靠的基础设施,大大简化了常见任务的实现,是 Deno 生态系统的重要支柱。
服务端 · 2月21日 16:08
Deno 的权限系统是如何工作的?Deno 的权限系统是其最核心的安全特性之一,采用"默认拒绝"的安全模型。这种设计确保了代码在未经明确授权的情况下无法访问敏感资源。
## 权限系统概述
Deno 的安全模型基于最小权限原则,默认情况下脚本没有任何权限,所有资源访问都需要显式授权。
## 权限类型
### 1. 文件系统权限
```bash
# 允许读取所有文件
deno run --allow-read script.ts
# 允许读取特定目录
deno run --allow-read=/app,/data script.ts
# 允许写入所有文件
deno run --allow-write script.ts
# 允许写入特定目录
deno run --allow-write=/tmp script.ts
# 同时允许读写
deno run --allow-read --allow-write script.ts
```
### 2. 网络权限
```bash
# 允许所有网络访问
deno run --allow-net script.ts
# 允许访问特定域名
deno run --allow-net=api.example.com script.ts
# 允许访问特定端口
deno run --allow-net=:8080 script.ts
# 允许访问特定 IP 和端口
deno run --allow-net=127.0.0.1:8000 script.ts
```
### 3. 环境变量权限
```bash
# 允许访问所有环境变量
deno run --allow-env script.ts
# 允许访问特定环境变量
deno run --allow-env=API_KEY,DATABASE_URL script.ts
```
### 4. 子进程权限
```bash
# 允许创建子进程
deno run --allow-run script.ts
# 允许运行特定命令
deno run --allow-run=git,npm script.ts
```
### 5. 系统信息权限
```bash
# 允许获取系统信息
deno run --allow-sys script.ts
# 允许获取特定系统信息
deno run --allow-sys=hostname,osRelease,osVersion script.ts
```
### 6. 高精度时间权限
```bash
# 允许访问高精度时间
deno run --allow-hrtime script.ts
```
### 7. FFI(外部函数接口)权限
```bash
# 允许加载动态库
deno run --allow-ffi script.ts
# 允许加载特定库
deno run --allow-ffi=/path/to/library.so script.ts
```
## 权限组合使用
```bash
# 组合多个权限
deno run --allow-read --allow-write --allow-net --allow-env=API_KEY app.ts
# 使用 --allow-all 授予所有权限(不推荐生产环境)
deno run --allow-all app.ts
```
## 代码中的权限检查
Deno 提供了 API 来检查当前拥有的权限:
```typescript
// 检查是否具有读取权限
const canRead = await Deno.permissions.query({ name: "read", path: "/tmp" });
console.log(canRead.state); // "granted", "prompt", or "denied"
// 检查是否具有网络权限
const canAccessNet = await Deno.permissions.query({ name: "net" });
console.log(canAccessNet.state);
// 请求权限
const netPermission = await Deno.permissions.request({ name: "net" });
if (netPermission.state === "granted") {
console.log("Network access granted");
}
```
## 权限提示模式
Deno 支持交互式权限提示:
```bash
# 使用 --prompt 权限标志
deno run --prompt=net script.ts
```
当脚本尝试访问需要权限的资源时,Deno 会提示用户是否授权。
## 实际应用示例
### 1. 文件服务器
```typescript
// file-server.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
const filePath = `.${url.pathname}`;
try {
const content = await Deno.readFile(filePath);
return new Response(content);
} catch {
return new Response("File not found", { status: 404 });
}
};
await serve(handler, { port: 8000 });
```
运行:
```bash
deno run --allow-read --allow-net file-server.ts
```
### 2. API 客户端
```typescript
// api-client.ts
const API_KEY = Deno.env.get("API_KEY");
if (!API_KEY) {
throw new Error("API_KEY environment variable not set");
}
const response = await fetch("https://api.example.com/data", {
headers: {
"Authorization": `Bearer ${API_KEY}`,
},
});
const data = await response.json();
console.log(data);
```
运行:
```bash
deno run --allow-net --allow-env=API_KEY api-client.ts
```
### 3. 文件处理工具
```typescript
// file-processor.ts
const inputFile = Deno.args[0];
const outputFile = Deno.args[1];
const content = await Deno.readTextFile(inputFile);
const processed = content.toUpperCase();
await Deno.writeTextFile(outputFile, processed);
console.log(`Processed ${inputFile} to ${outputFile}`);
```
运行:
```bash
deno run --allow-read --allow-write file-processor.ts input.txt output.txt
```
## 权限最佳实践
1. **最小权限原则**:只授予必要的权限
2. **明确指定资源**:使用具体的路径和域名,而不是通配符
3. **避免 --allow-all**:在生产环境中绝不使用
4. **文档化权限需求**:在 README 中说明运行脚本所需的权限
5. **使用权限检查**:在代码中检查权限并提供友好的错误信息
6. **环境隔离**:在容器或沙箱环境中运行不受信任的代码
## 安全优势
Deno 的权限系统提供了以下安全优势:
- **防止数据泄露**:未经授权无法读取敏感文件
- **防止系统破坏**:未经授权无法写入或删除文件
- **防止网络攻击**:未经授权无法进行网络请求
- **防止环境泄露**:未经授权无法访问环境变量
- **防止命令注入**:未经授权无法执行系统命令
- **可审计性**:所有权限使用都是显式的,便于审计
## 与 Node.js 的对比
| 特性 | Deno | Node.js |
|------|------|---------|
| 默认权限 | 无权限 | 完全访问 |
| 权限控制 | 命令行标志 | 无内置机制 |
| 安全模型 | 默认拒绝 | 默认允许 |
| 权限粒度 | 细粒度控制 | 无控制 |
| 审计能力 | 显式权限 | 隐式权限 |
Deno 的权限系统为 JavaScript/TypeScript 运行时提供了企业级的安全保障,使其特别适合处理敏感数据和运行不受信任代码的场景。
服务端 · 2月21日 16:08
Deno 如何处理模块导入和依赖管理?Deno 的模块系统采用去中心化的设计,与 Node.js 的 npm 生态系统有显著不同。理解 Deno 的模块导入机制对于高效开发至关重要。
## 模块导入方式
### 1. URL 导入
Deno 使用 URL 直接导入模块,这是其最显著的特点:
```typescript
// 从 Deno 标准库导入
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
// 从第三方库导入
import { oak } from "https://deno.land/x/oak@v12.6.1/mod.ts";
// 从 GitHub 导入
import { myLib } from "https://raw.githubusercontent.com/user/repo/main/mod.ts";
```
### 2. 本地文件导入
```typescript
// 相对路径导入
import { utils } from "./utils.ts";
import { helper } from "../helper/helper.ts";
// 绝对路径导入
import { config } from "/app/config.ts";
```
### 3. 导入映射(Import Maps)
使用 `import_map.json` 管理模块别名:
```json
{
"imports": {
"std/": "https://deno.land/std@0.208.0/",
"oak": "https://deno.land/x/oak@v12.6.1/mod.ts",
"@utils/": "./src/utils/"
}
}
```
使用方式:
```typescript
import { serve } from "std/http/server.ts";
import { Application } from "oak";
import { formatDate } from "@utils/date.ts";
```
运行时指定 import map:
```bash
deno run --import-map=import_map.json app.ts
```
## 版本管理
### 1. 版本锁定
Deno 推荐在 URL 中指定具体版本:
```typescript
// 推荐:指定版本
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
// 不推荐:使用最新版本(可能不稳定)
import { serve } from "https://deno.land/std/http/server.ts";
```
### 2. 依赖缓存
Deno 会缓存所有下载的模块:
```bash
# 查看缓存位置
deno info
# 重新下载依赖
deno cache --reload app.ts
# 清除缓存
deno cache --reload all
```
## 模块导出
### 1. 命名导出
```typescript
// utils.ts
export const add = (a: number, b: number): number => a + b;
export const subtract = (a: number, b: number): number => a - b;
export interface User {
id: number;
name: string;
}
```
### 2. 默认导出
```typescript
// app.ts
export default class Application {
start() {
console.log("Application started");
}
}
```
### 3. 重新导出
```typescript
// index.ts
export * from "./utils.ts";
export { default as App } from "./app.ts";
export type { User } from "./types.ts";
```
## 权限要求
导入远程模块时,Deno 需要网络权限:
```bash
# 允许网络访问以下载模块
deno run --allow-net app.ts
# 允许读取缓存
deno run --allow-read app.ts
```
## 与 Node.js 的对比
| 特性 | Deno | Node.js |
|------|------|---------|
| 导入方式 | URL 导入 | npm 包名 |
| 依赖管理 | 无 package.json | package.json + node_modules |
| 版本控制 | URL 中的版本号 | package.json 版本范围 |
| 模块解析 | 直接 URL | node_modules 查找算法 |
| 缓存机制 | 全局缓存 | 本地 node_modules |
| 类型支持 | 原生 TypeScript | 需要 @types 包 |
## 最佳实践
1. **始终指定版本**:在 URL 中使用明确的版本号
2. **使用 Import Maps**:简化模块路径管理
3. **依赖锁定**:考虑使用 `deno.lock` 文件
4. **权限最小化**:只授予必要的权限
5. **缓存管理**:定期清理和更新缓存
## 实际示例
创建一个简单的 Web 服务器:
```typescript
// server.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Hello, Deno!", {
headers: { "content-type": "text/plain" },
});
}
return new Response("Not Found", { status: 404 });
};
await serve(handler, { port: 8000 });
```
运行:
```bash
deno run --allow-net server.ts
```
Deno 的模块系统提供了更简单、更直接的依赖管理方式,消除了 node_modules 的复杂性,同时保持了良好的类型支持和开发体验。
服务端 · 2月21日 16:08
如何使用 deno compile 编译可执行文件?Deno 的 `deno compile` 命令可以将 TypeScript/JavaScript 代码编译为独立的可执行文件,这使得分发和部署变得更加简单。这个功能对于创建命令行工具和独立应用程序特别有用。
## deno compile 概述
`deno compile` 将 Deno 脚本及其所有依赖打包成一个单一的可执行文件,无需在目标机器上安装 Deno。
## 基本用法
### 1. 简单编译
```bash
# 编译为可执行文件
deno compile app.ts
# 默认输出文件名与输入文件相同(无扩展名)
# app.ts → app (Linux/Mac) 或 app.exe (Windows)
```
### 2. 指定输出文件名
```bash
# 指定输出文件名
deno compile --output=myapp app.ts
# Windows
deno compile --output=myapp.exe app.ts
```
### 3. 指定目标平台
```bash
# 编译为 Linux 可执行文件
deno compile --target=x86_64-unknown-linux-gnu app.ts
# 编译为 macOS 可执行文件
deno compile --target=x86_64-apple-darwin app.ts
deno compile --target=aarch64-apple-darwin app.ts # Apple Silicon
# 编译为 Windows 可执行文件
deno compile --target=x86_64-pc-windows-msvc app.ts
```
### 4. 包含权限
```bash
# 编译时包含权限,运行时无需再次指定
deno compile --allow-net --allow-read app.ts
# 包含所有权限
deno compile --allow-all app.ts
```
### 5. 设置环境变量
```bash
# 设置编译时的环境变量
deno compile --env=API_KEY=secret app.ts
```
## 实际应用示例
### 1. 命令行工具
```typescript
// cli.ts
#!/usr/bin/env -S deno run
const name = Deno.args[0] || "World";
const verbose = Deno.args.includes("--verbose");
if (verbose) {
console.log("Verbose mode enabled");
}
console.log(`Hello, ${name}!`);
// 文件操作示例
const filename = Deno.args[1];
if (filename) {
try {
const content = await Deno.readTextFile(filename);
console.log(`File content: ${content}`);
} catch (error) {
console.error(`Error reading file: ${error.message}`);
}
}
```
编译:
```bash
deno compile --allow-read --output=hello cli.ts
./hello Deno --verbose
```
### 2. Web 服务器
```typescript
// server.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const PORT = Deno.env.get("PORT") || "8000";
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Hello from Deno server!", {
headers: { "Content-Type": "text/plain" },
});
}
if (url.pathname === "/health") {
return new Response(JSON.stringify({ status: "ok" }), {
headers: { "Content-Type": "application/json" },
});
}
return new Response("Not Found", { status: 404 });
};
console.log(`Server running on port ${PORT}`);
await serve(handler, { port: parseInt(PORT) });
```
编译:
```bash
deno compile --allow-net --allow-env --output=server server.ts
./server
```
### 3. 文件处理工具
```typescript
// file-processor.ts
import { walk } from "https://deno.land/std@0.208.0/fs/walk.ts";
const directory = Deno.args[0] || ".";
const pattern = Deno.args[1];
console.log(`Scanning directory: ${directory}`);
if (pattern) {
console.log(`Pattern: ${pattern}`);
}
let count = 0;
for await (const entry of walk(directory)) {
if (entry.isFile) {
if (!pattern || entry.name.includes(pattern)) {
console.log(`Found: ${entry.path}`);
count++;
}
}
}
console.log(`Total files found: ${count}`);
```
编译:
```bash
deno compile --allow-read --output=scan file-processor.ts
./scan ./src .ts
```
### 4. API 客户端
```typescript
// api-client.ts
const API_URL = Deno.env.get("API_URL") || "https://api.example.com";
const API_KEY = Deno.env.get("API_KEY");
if (!API_KEY) {
console.error("API_KEY environment variable is required");
Deno.exit(1);
}
async function fetchData(endpoint: string): Promise<any> {
const response = await fetch(`${API_URL}${endpoint}`, {
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
}
return response.json();
}
const endpoint = Deno.args[0] || "/users";
console.log(`Fetching data from: ${API_URL}${endpoint}`);
try {
const data = await fetchData(endpoint);
console.log(JSON.stringify(data, null, 2));
} catch (error) {
console.error(`Error: ${error.message}`);
Deno.exit(1);
}
```
编译:
```bash
deno compile --allow-net --allow-env --output=api-client api-client.ts
API_KEY=your_key ./api-client /users
```
## 高级用法
### 1. 交叉编译
在 macOS 上编译 Linux 可执行文件:
```bash
deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts
```
### 2. 压缩可执行文件
使用 `upx` 压缩编译后的可执行文件:
```bash
# 安装 upx
brew install upx # macOS
apt install upx # Linux
# 压缩
upx --best myapp
```
### 3. 创建图标(仅限 Windows)
```bash
# 为 Windows 可执行文件添加图标
deno compile --icon=icon.ico --output=myapp.exe app.ts
```
### 4. 设置元数据
```bash
# 设置可执行文件元数据
deno compile \
--output=myapp \
--app-name="My Application" \
--app-version="1.0.0" \
app.ts
```
## 部署场景
### 1. Docker 部署
```dockerfile
# 多阶段构建
FROM denoland/deno:1.38.0 AS builder
WORKDIR /app
COPY . .
RUN deno compile --allow-net --allow-read --output=app server.ts
FROM debian:bullseye-slim
WORKDIR /app
COPY --from=builder /app/app .
EXPOSE 8000
CMD ["./app"]
```
### 2. 云函数部署
```typescript
// cloud-function.ts
export async function handler(event: any): Promise<any> {
const name = event.name || "World";
return {
statusCode: 200,
body: JSON.stringify({ message: `Hello, ${name}!` }),
};
}
```
编译:
```bash
deno compile --output=handler cloud-function.ts
```
### 3. 独立应用分发
```bash
# 为不同平台编译
deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts
deno compile --target=x86_64-apple-darwin --output=myapp-macos app.ts
deno compile --target=x86_64-pc-windows-msvc --output=myapp-windows.exe app.ts
# 创建发布包
mkdir release
cp myapp-linux release/
cp myapp-macos release/
cp myapp-windows.exe release/
```
## 限制和注意事项
### 1. 文件大小
编译后的可执行文件通常较大(约 50-100 MB),因为包含了整个 Deno 运行时和 V8 引擎。
### 2. 动态导入
动态导入的模块会在运行时下载,不会被包含在可执行文件中:
```typescript
// 这个模块不会被打包
const module = await import("https://example.com/module.ts");
```
### 3. 原生模块
使用 FFI 的原生模块需要确保目标平台上有相应的库。
### 4. 权限
编译时指定的权限在运行时生效,无法在运行时更改。
## 最佳实践
1. **指定目标平台**:明确指定编译目标,避免兼容性问题
2. **最小权限**:只授予必要的权限
3. **测试编译结果**:在目标平台上测试可执行文件
4. **版本控制**:记录编译命令和 Deno 版本
5. **文档化**:在 README 中说明如何编译和运行
6. **使用 CI/CD**:自动化编译和发布流程
## 与其他工具对比
| 特性 | deno compile | pkg | nexe |
|------|--------------|-----|------|
| 原生支持 | 是 | 否 | 否 |
| 跨平台 | 是 | 是 | 是 |
| 文件大小 | 较大 | 中等 | 中等 |
| 性能 | 好 | 好 | 好 |
| 维护性 | 官方支持 | 社区维护 | 社区维护 |
## 故障排查
### 1. 编译失败
```bash
# 查看详细错误信息
deno compile --log-level=debug app.ts
```
### 2. 运行时错误
```bash
# 检查权限
deno compile --allow-net --allow-read app.ts
# 检查环境变量
API_KEY=secret ./app
```
### 3. 兼容性问题
```bash
# 检查目标平台
deno compile --target=x86_64-unknown-linux-gnu app.ts
# 使用 Docker 测试
docker run --rm -v $(pwd):/app debian:bullseye-slim ./app
```
`deno compile` 为 Deno 应用程序提供了简单而强大的分发方式,特别适合需要独立部署的场景。通过合理使用这个功能,可以大大简化应用程序的部署和分发流程。
服务端 · 2月21日 16:08
Deno 的生态系统有哪些流行的库和工具?Deno 的生态系统虽然相对年轻,但发展迅速,提供了丰富的第三方库和工具。了解 Deno 的生态系统有助于开发者更好地利用现有资源。
## 生态系统概述
Deno 的生态系统主要由以下几个部分组成:
1. **deno.land/x** - 第三方模块注册表
2. **deno.land/std** - 官方标准库
3. **nest.land** - 另一个流行的模块注册表
4. **Deno Deploy** - 官方边缘计算平台
5. **社区工具和框架** - 各种开发工具和框架
## 流行的第三方库
### 1. Web 框架
#### Oak
```typescript
import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";
const app = new Application();
const router = new Router();
router.get("/", (ctx) => {
ctx.response.body = "Hello, Oak!";
});
router.get("/users/:id", (ctx) => {
const { id } = ctx.params;
ctx.response.body = { id, name: `User ${id}` };
});
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 8000 });
```
#### Fresh
```typescript
import { Fresh } from "https://deno.land/x/fresh@1.4.0/server.ts";
import { handler } from "./routes/index.tsx";
const app = new Fresh();
app.get("/", handler);
await app.listen({ port: 8000 });
```
#### Hono
```typescript
import { Hono } from "https://deno.land/x/hono@v3.11.0/mod.ts";
const app = new Hono();
app.get("/", (c) => c.text("Hello, Hono!"));
app.get("/users/:id", (c) => {
const id = c.req.param("id");
return c.json({ id, name: `User ${id}` });
});
Deno.serve(app.fetch);
```
### 2. 数据库客户端
#### PostgreSQL (postgres)
```typescript
import { Client } from "https://deno.land/x/postgres@v0.17.0/mod.ts";
const client = new Client({
user: "user",
database: "database",
hostname: "localhost",
port: 5432,
password: "password",
});
await client.connect();
const result = await client.queryArray("SELECT * FROM users");
console.log(result.rows);
await client.end();
```
#### MySQL (mysql)
```typescript
import { Client } from "https://deno.land/x/mysql@v2.12.0/mod.ts";
const client = await new Client().connect({
hostname: "127.0.0.1",
username: "root",
password: "password",
db: "test",
});
const users = await client.query("SELECT * FROM users");
console.log(users);
await client.close();
```
#### MongoDB (mongo)
```typescript
import { MongoClient } from "https://deno.land/x/mongo@v0.31.0/mod.ts";
const client = new MongoClient();
await client.connect("mongodb://localhost:27017");
const db = client.database("test");
const users = db.collection<User>("users");
const user = await users.findOne({ name: "John" });
console.log(user);
await client.close();
```
#### Redis (redis)
```typescript
import { connect } from "https://deno.land/x/redis@v0.31.0/mod.ts";
const redis = await connect({
hostname: "127.0.0.1",
port: 6379,
});
await redis.set("key", "value");
const value = await redis.get("key");
console.log(value);
await redis.close();
```
### 3. ORM 和查询构建器
#### DenoDB
```typescript
import { Database } from "https://deno.land/x/denodb@v1.0.0/mod.ts";
const db = new Database("postgres", {
host: "127.0.0.1",
username: "user",
password: "password",
database: "test",
});
class User extends Model {
static fields = {
id: { primaryKey: true, autoIncrement: true },
name: { type: VARCHAR },
email: { type: VARCHAR, unique: true },
};
}
db.link([User]);
await db.sync();
const user = await User.create({ name: "John", email: "john@example.com" });
console.log(user);
```
#### Drizzle ORM
```typescript
import { drizzle } from "https://deno.land/x/drizzle@v0.5.0/mod.ts";
import { pgTable, serial, text, varchar } from "https://deno.land/x/drizzle@v0.5.0/pg-core/mod.ts";
const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: varchar("email", { length: 255 }).notNull().unique(),
});
const db = drizzle("postgresql://user:password@localhost:5432/test");
const user = await db.insert(users).values({
name: "John",
email: "john@example.com",
}).returning().get();
console.log(user);
```
### 4. 认证和授权
#### JWT (djwt)
```typescript
import { create, verify, getNumericDate } from "https://deno.land/x/djwt@v3.0.1/mod.ts";
const key = await crypto.subtle.generateKey(
{ name: "HMAC", hash: "SHA-256" },
true,
["sign", "verify"]
);
const payload = {
iss: "my-app",
exp: getNumericDate(60 * 60), // 1 hour
data: { userId: 123 },
};
const token = await create({ alg: "HS256", typ: "JWT" }, payload, key);
console.log("Token:", token);
const verified = await verify(token, key);
console.log("Verified:", verified);
```
#### OAuth (oauth2_client)
```typescript
import { OAuth2Client } from "https://deno.land/x/oauth2_client@v1.0.2/mod.ts";
const oauth2Client = new OAuth2Client({
clientId: "your-client-id",
clientSecret: "your-client-secret",
authorizationEndpointUri: "https://github.com/login/oauth/authorize",
tokenUri: "https://github.com/login/oauth/access_token",
});
const authUrl = oauth2Client.code.getAuthorizationUri({
redirectUri: "http://localhost:8000/callback",
scope: "read:user",
});
console.log("Visit:", authUrl);
```
### 5. 工具库
#### Lodash (lodash)
```typescript
import _ from "https://deno.land/x/lodash@4.17.19-es/mod.ts";
const users = [
{ name: "John", age: 30 },
{ name: "Jane", age: 25 },
];
const sorted = _.orderBy(users, ["age"], ["asc"]);
console.log(sorted);
const names = _.map(users, "name");
console.log(names);
```
#### Date-fns (date_fns)
```typescript
import { format, addDays, isAfter } from "https://deno.land/x/date_fns@v2.29.3/mod.ts";
const now = new Date();
const future = addDays(now, 7);
console.log(format(now, "yyyy-MM-dd"));
console.log(format(future, "yyyy-MM-dd"));
console.log(isAfter(future, now));
```
#### Zod (zod)
```typescript
import { z } from "https://deno.land/x/zod@v3.21.4/mod.ts";
const UserSchema = z.object({
id: z.number(),
name: z.string().min(2),
email: z.string().email(),
age: z.number().min(0).max(120),
});
const userData = {
id: 1,
name: "John",
email: "john@example.com",
age: 30,
};
const user = UserSchema.parse(userData);
console.log(user);
```
### 6. 测试工具
#### Supabase (supabase)
```typescript
import { createClient } from "https://deno.land/x/supabase@2.0.0/mod.ts";
const supabase = createClient(
"your-project-url",
"your-anon-key"
);
const { data, error } = await supabase
.from("users")
.select("*")
.eq("id", 1);
console.log(data);
```
## 开发工具
### 1. 代码格式化
Deno 内置了格式化工具:
```bash
deno fmt .
```
### 2. 代码检查
```bash
deno lint .
```
### 3. 文档生成
```bash
deno doc --html --output=./docs ./src
```
### 4. 依赖检查
```bash
deno info
```
## 包管理工具
### 1. Denoify
将 Node.js 包转换为 Deno 兼容的包:
```bash
denoify
```
### 2. Deno 2 NPM (d2n)
将 npm 包转换为 Deno 模块:
```bash
d2n express
```
### 3. Import Map Generator
自动生成 import map:
```bash
deno run --allow-read --allow-write https://deno.land/x/import_map_generator/main.ts
```
## 部署平台
### 1. Deno Deploy
官方边缘计算平台:
```typescript
// main.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
serve((req) => {
return new Response("Hello from Deno Deploy!");
});
```
### 2. Vercel
支持 Deno 部署:
```json
{
"builds": [
{
"src": "main.ts",
"use": "@vercel/deno"
}
]
}
```
### 3. Railway
支持 Deno 部署:
```toml
[build]
builder = "NIXPACKS"
[deploy]
startCommand = "deno run --allow-net main.ts"
```
## 社区资源
### 1. 官方资源
- **官方网站**: https://deno.land
- **文档**: https://deno.land/manual
- **标准库**: https://deno.land/std
- **第三方库**: https://deno.land/x
### 2. 社区论坛
- **GitHub Discussions**: https://github.com/denoland/deno/discussions
- **Discord**: https://discord.gg/deno
- **Twitter**: @deno_land
### 3. 学习资源
- **Deno by Example**: https://examples.deno.land
- **Deno Tutorial**: https://deno-tutorial.com
- **Awesome Deno**: https://github.com/denolib/awesome-deno
## 最佳实践
### 1. 选择合适的库
- 优先使用标准库
- 选择活跃维护的第三方库
- 查看库的文档和示例
- 检查库的更新频率
### 2. 版本管理
```typescript
// 指定具体版本
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
import { Application } from "https://deno.land/x/oak@v12.6.1/mod.ts";
```
### 3. 安全性
- 检查库的安全性
- 使用最小权限原则
- 定期更新依赖
### 4. 性能
- 选择性能优化的库
- 避免不必要的依赖
- 使用缓存策略
## 迁移指南
### 1. 从 Node.js 迁移
```typescript
// Node.js
const express = require('express');
const app = express();
// Deno
import { Application } from "https://deno.land/x/oak@v12.6.1/mod.ts";
const app = new Application();
```
### 2. 使用兼容层
```typescript
import { polyfill } from "https://deno.land/x/node_polyfills@v0.1.48/main.ts";
polyfill();
// 现在可以使用 Node.js API
const fs = require('fs');
```
## 未来发展
Deno 生态系统正在快速发展,以下是一些值得关注的趋势:
1. **更多第三方库**:生态系统持续扩大
2. **更好的工具支持**:开发工具不断完善
3. **企业级功能**:更多企业级特性
4. **性能优化**:持续的性能改进
5. **跨平台支持**:更好的跨平台兼容性
Deno 的生态系统虽然年轻,但发展迅速,提供了丰富的资源和工具。通过合理利用这些资源,开发者可以更高效地构建应用程序。
服务端 · 2月21日 16:08
Deno 的部署和运维有哪些最佳实践?Deno 的部署和运维是构建生产级应用的重要环节。了解如何正确部署和运维 Deno 应用程序可以确保应用的稳定性和可维护性。
## 部署概述
Deno 应用可以部署到多种环境,包括传统服务器、容器化平台、云服务和边缘计算平台。
## Docker 部署
### 1. 基础 Dockerfile
```dockerfile
# 使用官方 Deno 镜像
FROM denoland/deno:1.38.0
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY deno.json ./
# 缓存依赖
RUN deno cache src/main.ts
# 复制源代码
COPY . .
# 暴露端口
EXPOSE 8000
# 运行应用
CMD ["deno", "run", "--allow-net", "--allow-env", "src/main.ts"]
```
### 2. 多阶段构建
```dockerfile
# 构建阶段
FROM denoland/deno:1.38.0 AS builder
WORKDIR /app
COPY . .
# 编译为可执行文件
RUN deno compile --allow-net --allow-env --output=app src/main.ts
# 运行阶段
FROM debian:bullseye-slim
WORKDIR /app
# 从构建阶段复制可执行文件
COPY --from=builder /app/app .
# 暴露端口
EXPOSE 8000
# 运行应用
CMD ["./app"]
```
### 3. 生产环境 Dockerfile
```dockerfile
# 构建阶段
FROM denoland/deno:1.38.0 AS builder
WORKDIR /app
# 安装依赖
COPY deno.json ./
RUN deno cache src/main.ts
# 复制源代码
COPY . .
# 运行测试
RUN deno test --allow-all
# 编译为可执行文件
RUN deno compile \
--allow-net \
--allow-env \
--allow-read \
--output=app \
src/main.ts
# 运行阶段
FROM debian:bullseye-slim
WORKDIR /app
# 安装必要的运行时依赖
RUN apt-get update && \
apt-get install -y ca-certificates && \
rm -rf /var/lib/apt/lists/*
# 创建非 root 用户
RUN useradd -m -u 1000 deno
# 从构建阶段复制可执行文件
COPY --from=builder /app/app .
# 更改所有者
RUN chown -R deno:deno /app
# 切换到非 root 用户
USER deno
# 暴露端口
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 运行应用
CMD ["./app"]
```
## Kubernetes 部署
### 1. Deployment 配置
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deno-app
labels:
app: deno-app
spec:
replicas: 3
selector:
matchLabels:
app: deno-app
template:
metadata:
labels:
app: deno-app
spec:
containers:
- name: deno-app
image: your-registry/deno-app:latest
ports:
- containerPort: 8000
env:
- name: PORT
value: "8000"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
```
### 2. Service 配置
```yaml
apiVersion: v1
kind: Service
metadata:
name: deno-app-service
spec:
selector:
app: deno-app
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: LoadBalancer
```
### 3. ConfigMap 和 Secret
```yaml
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
PORT: "8000"
LOG_LEVEL: "info"
---
# Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
database-url: <base64-encoded-url>
api-key: <base64-encoded-key>
```
## 云平台部署
### 1. Deno Deploy
Deno Deploy 是 Deno 官方的边缘计算平台。
```typescript
// main.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Hello from Deno Deploy!", {
headers: { "Content-Type": "text/plain" },
});
}
return new Response("Not Found", { status: 404 });
};
await serve(handler, { port: 8000 });
```
部署步骤:
1. 创建 Deno Deploy 账户
2. 连接 GitHub 仓库
3. 配置部署设置
4. 自动部署
### 2. Vercel 部署
```json
// vercel.json
{
"version": 2,
"builds": [
{
"src": "src/main.ts",
"use": "@vercel/deno"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/src/main.ts"
}
]
}
```
### 3. Railway 部署
```toml
# railway.toml
[build]
builder = "NIXPACKS"
[deploy]
startCommand = "deno run --allow-net --allow-env src/main.ts"
[env]
PORT = "8000"
```
## 进程管理
### 1. 使用 PM2
```javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'deno-app',
script: 'deno',
args: 'run --allow-net --allow-env src/main.ts',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 8000
}
}]
};
```
```bash
# 安装 PM2
npm install -g pm2
# 启动应用
pm2 start ecosystem.config.js
# 查看状态
pm2 status
# 查看日志
pm2 logs
# 重启应用
pm2 restart deno-app
# 停止应用
pm2 stop deno-app
```
### 2. 使用 Systemd
```ini
# /etc/systemd/system/deno-app.service
[Unit]
Description=Deno Application
After=network.target
[Service]
Type=simple
User=deno
WorkingDirectory=/app
Environment="PORT=8000"
Environment="DATABASE_URL=postgres://..."
ExecStart=/usr/local/bin/deno run --allow-net --allow-env /app/src/main.ts
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
```bash
# 启用服务
sudo systemctl enable deno-app
# 启动服务
sudo systemctl start deno-app
# 查看状态
sudo systemctl status deno-app
# 查看日志
sudo journalctl -u deno-app -f
# 重启服务
sudo systemctl restart deno-app
```
## 监控和日志
### 1. 日志管理
```typescript
// logger.ts
import { getLogger, setup, handlers } from "https://deno.land/std@0.208.0/log/mod.ts";
await setup({
handlers: {
console: new handlers.ConsoleHandler("INFO"),
file: new handlers.FileHandler("INFO", {
filename: "./logs/app.log",
formatter: "{levelName} {datetime} {msg}",
}),
},
loggers: {
default: {
level: "INFO",
handlers: ["console", "file"],
},
},
});
export const logger = getLogger();
```
### 2. 健康检查
```typescript
// health.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
let isHealthy = true;
let isReady = false;
// 模拟启动时间
setTimeout(() => {
isReady = true;
}, 5000);
const handler = async (req: Request): Promise<Response> => {
const url = new URL(req.url);
if (url.pathname === "/health") {
return new Response(JSON.stringify({ status: isHealthy ? "ok" : "error" }), {
headers: { "Content-Type": "application/json" },
status: isHealthy ? 200 : 503,
});
}
if (url.pathname === "/ready") {
return new Response(JSON.stringify({ ready: isReady }), {
headers: { "Content-Type": "application/json" },
status: isReady ? 200 : 503,
});
}
return new Response("Not Found", { status: 404 });
};
await serve(handler, { port: 8000 });
```
### 3. 指标收集
```typescript
// metrics.ts
class MetricsCollector {
private metrics: Map<string, number> = new Map();
private counters: Map<string, number> = new Map();
increment(name: string, value: number = 1) {
const current = this.counters.get(name) || 0;
this.counters.set(name, current + value);
}
gauge(name: string, value: number) {
this.metrics.set(name, value);
}
timing(name: string, duration: number) {
const timings = this.metrics.get(`${name}_timings`) || [];
timings.push(duration);
this.metrics.set(`${name}_timings`, timings);
}
getMetrics(): Record<string, any> {
return {
counters: Object.fromEntries(this.counters),
gauges: Object.fromEntries(this.metrics),
};
}
}
export const metrics = new MetricsCollector();
```
## 性能优化
### 1. 连接池
```typescript
// connection-pool.ts
class ConnectionPool<T> {
private pool: T[] = [];
private maxConnections: number;
private factory: () => Promise<T>;
constructor(maxConnections: number, factory: () => Promise<T>) {
this.maxConnections = maxConnections;
this.factory = factory;
}
async acquire(): Promise<T> {
if (this.pool.length > 0) {
return this.pool.pop()!;
}
return await this.factory();
}
release(connection: T) {
if (this.pool.length < this.maxConnections) {
this.pool.push(connection);
}
}
}
```
### 2. 缓存策略
```typescript
// cache.ts
class Cache {
private cache: Map<string, { value: any; expires: number }> = new Map();
private ttl: number;
constructor(ttl: number = 60000) {
this.ttl = ttl;
}
set(key: string, value: any, ttl?: number) {
const expires = Date.now() + (ttl || this.ttl);
this.cache.set(key, { value, expires });
}
get(key: string): any | null {
const item = this.cache.get(key);
if (!item) {
return null;
}
if (Date.now() > item.expires) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
cleanup() {
const now = Date.now();
for (const [key, item] of this.cache.entries()) {
if (now > item.expires) {
this.cache.delete(key);
}
}
}
}
```
## 安全最佳实践
### 1. 环境变量管理
```typescript
// config.ts
interface Config {
port: number;
databaseUrl: string;
apiKey: string;
logLevel: string;
}
function loadConfig(): Config {
const port = parseInt(Deno.env.get("PORT") || "8000");
const databaseUrl = Deno.env.get("DATABASE_URL");
const apiKey = Deno.env.get("API_KEY");
const logLevel = Deno.env.get("LOG_LEVEL") || "info";
if (!databaseUrl) {
throw new Error("DATABASE_URL environment variable is required");
}
if (!apiKey) {
throw new Error("API_KEY environment variable is required");
}
return {
port,
databaseUrl,
apiKey,
logLevel,
};
}
export const config = loadConfig();
```
### 2. 权限最小化
```bash
# 只授予必要的权限
deno run --allow-net --allow-env src/main.ts
# 限制网络访问范围
deno run --allow-net=api.example.com src/main.ts
# 限制文件访问
deno run --allow-read=/app/data src/main.ts
```
## CI/CD 集成
### 1. GitHub Actions
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.38.0
- name: Run tests
run: deno test --allow-all
- name: Lint
run: deno lint
- name: Format check
run: deno fmt --check
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t deno-app:${{ github.sha }} .
- name: Push to registry
run: |
echo ${{ secrets.REGISTRY_PASSWORD }} | docker login -u ${{ secrets.REGISTRY_USER }} --password-stdin
docker push deno-app:${{ github.sha }}
```
### 2. GitLab CI
```yaml
# .gitlab-ci.yml
stages:
- test
- build
- deploy
test:
stage: test
image: denoland/deno:1.38.0
script:
- deno test --allow-all
- deno lint
- deno fmt --check
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t deno-app:$CI_COMMIT_SHA .
- docker push deno-app:$CI_COMMIT_SHA
deploy:
stage: deploy
image: alpine:latest
script:
- kubectl set image deployment/deno-app deno-app=deno-app:$CI_COMMIT_SHA
only:
- main
```
## 故障排查
### 1. 常见问题
**问题:应用启动失败**
```bash
# 检查日志
deno run --log-level=debug src/main.ts
# 检查权限
deno info src/main.ts
```
**问题:内存泄漏**
```typescript
// 定期检查内存使用
setInterval(() => {
const usage = Deno.memoryUsage();
console.log("Memory usage:", usage);
}, 60000);
```
**问题:性能下降**
```typescript
// 使用性能分析
import { performance } from "https://deno.land/std@0.208.0/node/performance.ts";
const start = performance.now();
// 执行操作
const duration = performance.now() - start;
console.log(`Operation took ${duration}ms`);
```
## 最佳实践
1. **容器化部署**:使用 Docker 确保环境一致性
2. **健康检查**:实现健康检查端点
3. **日志记录**:记录关键操作和错误
4. **监控指标**:收集和监控应用指标
5. **自动化部署**:使用 CI/CD 自动化部署流程
6. **权限最小化**:只授予必要的权限
7. **资源限制**:设置合理的资源限制
8. **备份策略**:定期备份重要数据
Deno 的部署和运维需要综合考虑多个方面,通过合理的规划和实施,可以构建稳定、可靠的生产级应用。
服务端 · 2月21日 16:06