乐闻世界logo
搜索文章和话题

面试题手册

什么是 Deno?它和 Node.js 有什么区别?

Deno 是由 Node.js 创始人 Ryan Dahl 开发的现代化 JavaScript 和 TypeScript 运行时环境。Deno 的设计目标是解决 Node.js 在早期设计上的一些缺陷,并提供更安全、更简洁的开发体验。核心特性Deno 具有以下核心特性:默认安全:Deno 默认情况下脚本无法访问文件系统、网络或环境变量,除非显式授予相应权限内置 TypeScript 支持:Deno 原生支持 TypeScript,无需额外的转译步骤去中心化模块系统:使用 URL 导入模块,不依赖 package.json 和 node_modules单一可执行文件:Deno 只有一个可执行文件,无需复杂的安装过程内置工具链:包含格式化、测试、linting、打包等开发工具标准库:提供经过审核的标准库,确保代码质量与 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 的权限系统是其安全特性的核心:# 授予文件系统读取权限deno run --allow-read script.ts# 授予网络访问权限deno run --allow-net script.ts# 授予所有权限(不推荐)deno run --allow-all script.ts模块导入示例// 直接从 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 特别适合需要快速开发、安全性和现代化开发体验的项目。
阅读 0·2月21日 16:10

Deno 如何支持 TypeScript?

Deno 原生支持 TypeScript,这是其最吸引人的特性之一。与 Node.js 需要配置 TypeScript 编译器不同,Deno 可以直接运行 TypeScript 文件,无需任何额外的转译步骤。TypeScript 支持概述Deno 内置了 TypeScript 编译器,使用 V8 引擎的 TypeScript 支持和 swc 编译器来提供快速的类型检查和转译。直接运行 TypeScript1. 基本用法// app.tsinterface 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);运行:deno run app.ts2. 类型检查Deno 会在运行时进行类型检查:# 运行时进行类型检查deno run app.ts# 仅类型检查(不执行)deno check app.ts# 类型检查所有文件deno check **/*.ts类型定义1. 使用 Deno 内置类型Deno 提供了丰富的内置类型定义:// 文件系统操作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 导入的模块通常包含类型定义:import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";const app = new Application();const router = new Router();3. 自定义类型定义如果模块没有类型定义,可以创建自定义类型:// types.d.tsdeclare module "https://example.com/module.js" { export function doSomething(): void; export const value: number;}配置选项1. tsconfig.jsonDeno 支持使用 tsconfig.json 进行配置:{ "compilerOptions": { "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"]}2. 命令行选项# 禁用类型检查(不推荐)deno run --no-check app.ts# 使用特定的 tsconfigdeno run --config=tsconfig.json app.ts# 启用严格模式deno run app.ts # 默认启用严格模式类型检查策略1. 本地类型检查# 检查本地文件deno check app.ts# 检查整个项目deno check src/**/*.ts2. 远程类型检查Deno 会缓存远程模块的类型定义:# 重新下载并检查远程类型deno check --remote app.ts# 清除类型缓存deno cache --reload app.ts实际应用示例1. 类型安全的 API 服务器// api-server.tsinterface 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. 类型安全的数据库操作// database.tsinterface 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. 类型安全的工具函数// utils.tstype 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 会缓存类型检查结果:# 首次运行会进行完整类型检查deno run app.ts# 后续运行会使用缓存deno run app.ts2. 选择性类型检查# 只检查修改的文件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 包 |最佳实践始终使用类型:充分利用 TypeScript 的类型系统启用严格模式:使用 strict: true 获得更好的类型安全使用接口定义数据结构:明确 API 和数据模型的类型避免 any 类型:使用 unknown 或具体类型替代定期运行类型检查:使用 deno check 确保代码质量利用泛型:编写可复用的类型安全代码Deno 的 TypeScript 支持使得开发者能够享受类型安全的好处,而无需复杂的配置和构建流程,大大提高了开发效率和代码质量。
阅读 0·2月21日 16:10

Deno 的测试框架如何使用?

Deno 的测试框架提供了强大而简洁的测试功能,使得编写和运行测试变得简单高效。了解 Deno 的测试系统对于保证代码质量至关重要。测试框架概述Deno 内置了测试框架,无需安装额外的测试库。测试文件通常以 _test.ts 或 .test.ts 结尾。基本测试1. 编写第一个测试// math_test.tsimport { 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);});运行测试:deno test math_test.ts2. 测试异步代码// async_test.tsimport { 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. 常用断言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. 测试异常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. 测试选项Deno.test({ name: "test with options", fn: () => { // 测试代码 }, permissions: { read: true, net: true, }, sanitizeOps: true, sanitizeResources: true, sanitizeExit: true,});2. 超时设置Deno.test({ name: "slow test with timeout", fn: async () => { await new Promise(resolve => setTimeout(resolve, 2000)); }, timeout: 5000, // 5秒超时});3. 忽略测试Deno.test({ name: "ignored test", ignore: true, fn: () => { // 这个测试会被跳过 },});// 或者使用 only 只运行特定测试Deno.test({ name: "only this test", only: true, fn: () => { // 只运行这个测试 },});测试组织1. 测试套件// user_test.tsimport { 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. 测试钩子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 和 Stub1. 简单的 Mock// api_test.tsimport { 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// spy_test.tsimport { 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 服务器测试// server_test.tsimport { 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. 文件系统测试// file_test.tsimport { 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. 生成覆盖率报告# 运行测试并生成覆盖率deno test --coverage=coverage# 生成覆盖率报告deno coverage coverage --lcov --output=coverage.lcov# 在浏览器中查看覆盖率deno coverage coverage --html2. 覆盖率配置// deno.json{ "compilerOptions": { "strict": true }, "test": { "include": ["src/**/*_test.ts", "tests/**/*.ts"], "exclude": ["node_modules/"] }}测试最佳实践1. 测试命名// 好的测试名称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)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. 测试隔离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. 测试数据构建器// test-builder.tsclass 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. 基本命令# 运行所有测试deno test# 运行特定测试文件deno test math_test.ts# 监听模式(文件变化时自动运行)deno test --watch# 并行运行测试deno test --parallel# 显示详细输出deno test --verbose# 只运行失败的测试deno test --fail-fast# 允许所有权限deno test --allow-all2. 过滤测试# 运行匹配模式的测试deno test --filter="user"# 运行特定测试deno test --filter="add function"最佳实践总结测试独立性:每个测试应该独立运行,不依赖其他测试清晰的命名:测试名称应该清楚地描述测试的内容AAA 模式:使用 Arrange-Act-Assert 模式组织测试代码适当的断言:使用最合适的断言函数测试边界情况:测试正常情况和边界情况保持简单:测试应该简单、快速、易于理解定期运行:在 CI/CD 中定期运行测试覆盖率监控:监控测试覆盖率,确保代码质量Deno 的测试框架提供了强大而简洁的功能,通过合理使用这些功能,可以构建高质量的测试套件,确保代码的可靠性和可维护性。
阅读 0·2月21日 16:08

Deno 的任务系统如何工作?

Deno 的任务系统(Task System)提供了一种在后台运行异步任务的方式,类似于浏览器中的 Web Workers。这个功能对于执行 CPU 密集型任务或需要并行处理的场景非常有用。任务系统概述Deno 的任务系统允许你创建独立的工作线程,这些线程可以并行执行代码,不会阻塞主线程。每个任务都有自己的内存空间,通过消息传递与主线程通信。基本用法1. 创建简单任务// main.tsconst 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);};// worker.tsself.onmessage = (event) => { console.log("Worker received:", event.data); const result = event.data.data * 2; self.postMessage({ type: "result", data: result });};运行:deno run --allow-read main.ts2. 使用 Promise 封装 Worker// main.tsfunction 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();// worker.tsself.onmessage = (event) => { const { number } = event.data; // 模拟耗时计算 let result = 0; for (let i = 0; i < number * 1000000; i++) { result += i; } self.postMessage(result);};实际应用示例1. 图像处理// image-processor.tsself.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; // 实际实现会更复杂}// main.tsimport { 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. 并行数据处理// data-processor.tsself.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 });};// main.tsimport { 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. 文件批量处理// file-processor.tsself.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 }); }};// main.tsimport { 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. 密码哈希计算// password-hasher.tsself.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 });};// main.tsimport { 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 池// worker-pool.tsexport 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 池:// main.tsimport { 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. 错误处理和重试// worker-with-retry.tsexport 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;}最佳实践合理使用 Worker:只在 CPU 密集型任务中使用 Worker控制并发:限制同时运行的 Worker 数量正确清理:使用完成后终止 Worker错误处理:妥善处理 Worker 错误消息大小:避免传递过大的消息类型安全:使用 TypeScript 确保消息类型正确Deno 的任务系统为并行处理和后台任务提供了强大的支持,能够显著提高应用程序的性能和响应能力。
阅读 0·2月21日 16:08

Deno 的标准库有哪些常用模块?

Deno 的标准库(Standard Library)是一组经过精心设计、测试和维护的模块,为开发者提供了高质量、可复用的代码。标准库涵盖了从文件系统操作到网络编程的各个方面,是 Deno 生态系统的重要组成部分。标准库概述Deno 标准库托管在 https://deno.land/std/,所有模块都经过严格的代码审查和测试,确保代码质量和安全性。版本管理标准库使用语义化版本控制,建议在导入时指定版本:// 推荐:指定版本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 服务器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 客户端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. 文件系统模块文件操作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");遍历目录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. 路径模块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 编码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 编码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. 测试模块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. 日志模块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 模块import { v4 as uuidv4, v5 as uuidv5 } from "https://deno.land/std@0.208.0/uuid/mod.ts";const id1 = uuidv4();console.log(id1); // 生成随机 UUIDconst id2 = uuidv5("hello", uuidv4());console.log(id2); // 基于命名空间生成 UUID8. 日期时间模块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. 颜色模块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. 异步模块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. 流模块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. 命令行模块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" }实际应用示例文件上传服务器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 });日志记录系统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; }}标准库的优势高质量代码:所有模块都经过严格审查和测试类型安全:完整的 TypeScript 类型定义文档完善:详细的 API 文档和示例定期更新:持续维护和功能增强一致性:统一的代码风格和 API 设计安全可靠:经过安全审计,无已知漏洞最佳实践指定版本:始终使用特定版本的标准库优先使用:在可能的情况下优先使用标准库而非第三方库查看文档:使用 deno doc 查看模块文档贡献代码:发现问题时可以提交 PR 贡献代码关注更新:定期查看标准库的更新日志Deno 标准库为开发者提供了强大而可靠的基础设施,大大简化了常见任务的实现,是 Deno 生态系统的重要支柱。
阅读 0·2月21日 16:08

Deno 的权限系统是如何工作的?

Deno 的权限系统是其最核心的安全特性之一,采用"默认拒绝"的安全模型。这种设计确保了代码在未经明确授权的情况下无法访问敏感资源。权限系统概述Deno 的安全模型基于最小权限原则,默认情况下脚本没有任何权限,所有资源访问都需要显式授权。权限类型1. 文件系统权限# 允许读取所有文件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.ts2. 网络权限# 允许所有网络访问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.ts3. 环境变量权限# 允许访问所有环境变量deno run --allow-env script.ts# 允许访问特定环境变量deno run --allow-env=API_KEY,DATABASE_URL script.ts4. 子进程权限# 允许创建子进程deno run --allow-run script.ts# 允许运行特定命令deno run --allow-run=git,npm script.ts5. 系统信息权限# 允许获取系统信息deno run --allow-sys script.ts# 允许获取特定系统信息deno run --allow-sys=hostname,osRelease,osVersion script.ts6. 高精度时间权限# 允许访问高精度时间deno run --allow-hrtime script.ts7. FFI(外部函数接口)权限# 允许加载动态库deno run --allow-ffi script.ts# 允许加载特定库deno run --allow-ffi=/path/to/library.so script.ts权限组合使用# 组合多个权限deno run --allow-read --allow-write --allow-net --allow-env=API_KEY app.ts# 使用 --allow-all 授予所有权限(不推荐生产环境)deno run --allow-all app.ts代码中的权限检查Deno 提供了 API 来检查当前拥有的权限:// 检查是否具有读取权限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 支持交互式权限提示:# 使用 --prompt 权限标志deno run --prompt=net script.ts当脚本尝试访问需要权限的资源时,Deno 会提示用户是否授权。实际应用示例1. 文件服务器// file-server.tsimport { 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 });运行:deno run --allow-read --allow-net file-server.ts2. API 客户端// api-client.tsconst 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);运行:deno run --allow-net --allow-env=API_KEY api-client.ts3. 文件处理工具// file-processor.tsconst 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}`);运行:deno run --allow-read --allow-write file-processor.ts input.txt output.txt权限最佳实践最小权限原则:只授予必要的权限明确指定资源:使用具体的路径和域名,而不是通配符避免 --allow-all:在生产环境中绝不使用文档化权限需求:在 README 中说明运行脚本所需的权限使用权限检查:在代码中检查权限并提供友好的错误信息环境隔离:在容器或沙箱环境中运行不受信任的代码安全优势Deno 的权限系统提供了以下安全优势:防止数据泄露:未经授权无法读取敏感文件防止系统破坏:未经授权无法写入或删除文件防止网络攻击:未经授权无法进行网络请求防止环境泄露:未经授权无法访问环境变量防止命令注入:未经授权无法执行系统命令可审计性:所有权限使用都是显式的,便于审计与 Node.js 的对比| 特性 | Deno | Node.js ||------|------|---------|| 默认权限 | 无权限 | 完全访问 || 权限控制 | 命令行标志 | 无内置机制 || 安全模型 | 默认拒绝 | 默认允许 || 权限粒度 | 细粒度控制 | 无控制 || 审计能力 | 显式权限 | 隐式权限 |Deno 的权限系统为 JavaScript/TypeScript 运行时提供了企业级的安全保障,使其特别适合处理敏感数据和运行不受信任代码的场景。
阅读 0·2月21日 16:08

Deno 如何处理模块导入和依赖管理?

Deno 的模块系统采用去中心化的设计,与 Node.js 的 npm 生态系统有显著不同。理解 Deno 的模块导入机制对于高效开发至关重要。模块导入方式1. URL 导入Deno 使用 URL 直接导入模块,这是其最显著的特点:// 从 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. 本地文件导入// 相对路径导入import { utils } from "./utils.ts";import { helper } from "../helper/helper.ts";// 绝对路径导入import { config } from "/app/config.ts";3. 导入映射(Import Maps)使用 import_map.json 管理模块别名:{ "imports": { "std/": "https://deno.land/std@0.208.0/", "oak": "https://deno.land/x/oak@v12.6.1/mod.ts", "@utils/": "./src/utils/" }}使用方式:import { serve } from "std/http/server.ts";import { Application } from "oak";import { formatDate } from "@utils/date.ts";运行时指定 import map:deno run --import-map=import_map.json app.ts版本管理1. 版本锁定Deno 推荐在 URL 中指定具体版本:// 推荐:指定版本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 会缓存所有下载的模块:# 查看缓存位置deno info# 重新下载依赖deno cache --reload app.ts# 清除缓存deno cache --reload all模块导出1. 命名导出// utils.tsexport 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. 默认导出// app.tsexport default class Application { start() { console.log("Application started"); }}3. 重新导出// index.tsexport * from "./utils.ts";export { default as App } from "./app.ts";export type { User } from "./types.ts";权限要求导入远程模块时,Deno 需要网络权限:# 允许网络访问以下载模块deno run --allow-net app.ts# 允许读取缓存deno run --allow-read app.ts与 Node.js 的对比| 特性 | Deno | Node.js ||------|------|---------|| 导入方式 | URL 导入 | npm 包名 || 依赖管理 | 无 package.json | package.json + nodemodules || 版本控制 | URL 中的版本号 | package.json 版本范围 || 模块解析 | 直接 URL | nodemodules 查找算法 || 缓存机制 | 全局缓存 | 本地 node_modules || 类型支持 | 原生 TypeScript | 需要 @types 包 |最佳实践始终指定版本:在 URL 中使用明确的版本号使用 Import Maps:简化模块路径管理依赖锁定:考虑使用 deno.lock 文件权限最小化:只授予必要的权限缓存管理:定期清理和更新缓存实际示例创建一个简单的 Web 服务器:// server.tsimport { 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 });运行:deno run --allow-net server.tsDeno 的模块系统提供了更简单、更直接的依赖管理方式,消除了 node_modules 的复杂性,同时保持了良好的类型支持和开发体验。
阅读 0·2月21日 16:08

如何使用 deno compile 编译可执行文件?

Deno 的 deno compile 命令可以将 TypeScript/JavaScript 代码编译为独立的可执行文件,这使得分发和部署变得更加简单。这个功能对于创建命令行工具和独立应用程序特别有用。deno compile 概述deno compile 将 Deno 脚本及其所有依赖打包成一个单一的可执行文件,无需在目标机器上安装 Deno。基本用法1. 简单编译# 编译为可执行文件deno compile app.ts# 默认输出文件名与输入文件相同(无扩展名)# app.ts → app (Linux/Mac) 或 app.exe (Windows)2. 指定输出文件名# 指定输出文件名deno compile --output=myapp app.ts# Windowsdeno compile --output=myapp.exe app.ts3. 指定目标平台# 编译为 Linux 可执行文件deno compile --target=x86_64-unknown-linux-gnu app.ts# 编译为 macOS 可执行文件deno compile --target=x86_64-apple-darwin app.tsdeno compile --target=aarch64-apple-darwin app.ts # Apple Silicon# 编译为 Windows 可执行文件deno compile --target=x86_64-pc-windows-msvc app.ts4. 包含权限# 编译时包含权限,运行时无需再次指定deno compile --allow-net --allow-read app.ts# 包含所有权限deno compile --allow-all app.ts5. 设置环境变量# 设置编译时的环境变量deno compile --env=API_KEY=secret app.ts实际应用示例1. 命令行工具// cli.ts#!/usr/bin/env -S deno runconst 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}`); }}编译:deno compile --allow-read --output=hello cli.ts./hello Deno --verbose2. Web 服务器// server.tsimport { 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) });编译:deno compile --allow-net --allow-env --output=server server.ts./server3. 文件处理工具// file-processor.tsimport { 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}`);编译:deno compile --allow-read --output=scan file-processor.ts./scan ./src .ts4. API 客户端// api-client.tsconst 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);}编译:deno compile --allow-net --allow-env --output=api-client api-client.tsAPI_KEY=your_key ./api-client /users高级用法1. 交叉编译在 macOS 上编译 Linux 可执行文件:deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts2. 压缩可执行文件使用 upx 压缩编译后的可执行文件:# 安装 upxbrew install upx # macOSapt install upx # Linux# 压缩upx --best myapp3. 创建图标(仅限 Windows)# 为 Windows 可执行文件添加图标deno compile --icon=icon.ico --output=myapp.exe app.ts4. 设置元数据# 设置可执行文件元数据deno compile \ --output=myapp \ --app-name="My Application" \ --app-version="1.0.0" \ app.ts部署场景1. Docker 部署# 多阶段构建FROM denoland/deno:1.38.0 AS builderWORKDIR /appCOPY . .RUN deno compile --allow-net --allow-read --output=app server.tsFROM debian:bullseye-slimWORKDIR /appCOPY --from=builder /app/app .EXPOSE 8000CMD ["./app"]2. 云函数部署// cloud-function.tsexport async function handler(event: any): Promise<any> { const name = event.name || "World"; return { statusCode: 200, body: JSON.stringify({ message: `Hello, ${name}!` }), };}编译:deno compile --output=handler cloud-function.ts3. 独立应用分发# 为不同平台编译deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.tsdeno compile --target=x86_64-apple-darwin --output=myapp-macos app.tsdeno compile --target=x86_64-pc-windows-msvc --output=myapp-windows.exe app.ts# 创建发布包mkdir releasecp myapp-linux release/cp myapp-macos release/cp myapp-windows.exe release/限制和注意事项1. 文件大小编译后的可执行文件通常较大(约 50-100 MB),因为包含了整个 Deno 运行时和 V8 引擎。2. 动态导入动态导入的模块会在运行时下载,不会被包含在可执行文件中:// 这个模块不会被打包const module = await import("https://example.com/module.ts");3. 原生模块使用 FFI 的原生模块需要确保目标平台上有相应的库。4. 权限编译时指定的权限在运行时生效,无法在运行时更改。最佳实践指定目标平台:明确指定编译目标,避免兼容性问题最小权限:只授予必要的权限测试编译结果:在目标平台上测试可执行文件版本控制:记录编译命令和 Deno 版本文档化:在 README 中说明如何编译和运行使用 CI/CD:自动化编译和发布流程与其他工具对比| 特性 | deno compile | pkg | nexe ||------|--------------|-----|------|| 原生支持 | 是 | 否 | 否 || 跨平台 | 是 | 是 | 是 || 文件大小 | 较大 | 中等 | 中等 || 性能 | 好 | 好 | 好 || 维护性 | 官方支持 | 社区维护 | 社区维护 |故障排查1. 编译失败# 查看详细错误信息deno compile --log-level=debug app.ts2. 运行时错误# 检查权限deno compile --allow-net --allow-read app.ts# 检查环境变量API_KEY=secret ./app3. 兼容性问题# 检查目标平台deno compile --target=x86_64-unknown-linux-gnu app.ts# 使用 Docker 测试docker run --rm -v $(pwd):/app debian:bullseye-slim ./appdeno compile 为 Deno 应用程序提供了简单而强大的分发方式,特别适合需要独立部署的场景。通过合理使用这个功能,可以大大简化应用程序的部署和分发流程。
阅读 0·2月21日 16:08

Deno 的生态系统有哪些流行的库和工具?

Deno 的生态系统虽然相对年轻,但发展迅速,提供了丰富的第三方库和工具。了解 Deno 的生态系统有助于开发者更好地利用现有资源。生态系统概述Deno 的生态系统主要由以下几个部分组成:deno.land/x - 第三方模块注册表deno.land/std - 官方标准库nest.land - 另一个流行的模块注册表Deno Deploy - 官方边缘计算平台社区工具和框架 - 各种开发工具和框架流行的第三方库1. Web 框架Oakimport { 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 });Freshimport { 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 });Honoimport { 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)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)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)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)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 和查询构建器DenoDBimport { 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 ORMimport { 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)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)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)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)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)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)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 内置了格式化工具:deno fmt .2. 代码检查deno lint .3. 文档生成deno doc --html --output=./docs ./src4. 依赖检查deno info包管理工具1. Denoify将 Node.js 包转换为 Deno 兼容的包:denoify2. Deno 2 NPM (d2n)将 npm 包转换为 Deno 模块:d2n express3. Import Map Generator自动生成 import map:deno run --allow-read --allow-write https://deno.land/x/import_map_generator/main.ts部署平台1. Deno Deploy官方边缘计算平台:// main.tsimport { serve } from "https://deno.land/std@0.208.0/http/server.ts";serve((req) => { return new Response("Hello from Deno Deploy!");});2. Vercel支持 Deno 部署:{ "builds": [ { "src": "main.ts", "use": "@vercel/deno" } ]}3. Railway支持 Deno 部署:[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/x2. 社区论坛GitHub Discussions: https://github.com/denoland/deno/discussionsDiscord: https://discord.gg/denoTwitter: @deno_land3. 学习资源Deno by Example: https://examples.deno.landDeno Tutorial: https://deno-tutorial.comAwesome Deno: https://github.com/denolib/awesome-deno最佳实践1. 选择合适的库优先使用标准库选择活跃维护的第三方库查看库的文档和示例检查库的更新频率2. 版本管理// 指定具体版本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 迁移// Node.jsconst express = require('express');const app = express();// Denoimport { Application } from "https://deno.land/x/oak@v12.6.1/mod.ts";const app = new Application();2. 使用兼容层import { polyfill } from "https://deno.land/x/node_polyfills@v0.1.48/main.ts";polyfill();// 现在可以使用 Node.js APIconst fs = require('fs');未来发展Deno 生态系统正在快速发展,以下是一些值得关注的趋势:更多第三方库:生态系统持续扩大更好的工具支持:开发工具不断完善企业级功能:更多企业级特性性能优化:持续的性能改进跨平台支持:更好的跨平台兼容性Deno 的生态系统虽然年轻,但发展迅速,提供了丰富的资源和工具。通过合理利用这些资源,开发者可以更高效地构建应用程序。
阅读 0·2月21日 16:08

Deno 的部署和运维有哪些最佳实践?

Deno 的部署和运维是构建生产级应用的重要环节。了解如何正确部署和运维 Deno 应用程序可以确保应用的稳定性和可维护性。部署概述Deno 应用可以部署到多种环境,包括传统服务器、容器化平台、云服务和边缘计算平台。Docker 部署1. 基础 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. 多阶段构建# 构建阶段FROM denoland/deno:1.38.0 AS builderWORKDIR /appCOPY . .# 编译为可执行文件RUN deno compile --allow-net --allow-env --output=app src/main.ts# 运行阶段FROM debian:bullseye-slimWORKDIR /app# 从构建阶段复制可执行文件COPY --from=builder /app/app .# 暴露端口EXPOSE 8000# 运行应用CMD ["./app"]3. 生产环境 Dockerfile# 构建阶段FROM denoland/deno:1.38.0 AS builderWORKDIR /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-slimWORKDIR /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 配置apiVersion: apps/v1kind: Deploymentmetadata: name: deno-app labels: app: deno-appspec: 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: 52. Service 配置apiVersion: v1kind: Servicemetadata: name: deno-app-servicespec: selector: app: deno-app ports: - protocol: TCP port: 80 targetPort: 8000 type: LoadBalancer3. ConfigMap 和 Secret# ConfigMapapiVersion: v1kind: ConfigMapmetadata: name: app-configdata: PORT: "8000" LOG_LEVEL: "info"---# SecretapiVersion: v1kind: Secretmetadata: name: app-secretstype: Opaquedata: database-url: <base64-encoded-url> api-key: <base64-encoded-key>云平台部署1. Deno DeployDeno Deploy 是 Deno 官方的边缘计算平台。// main.tsimport { 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 });部署步骤:创建 Deno Deploy 账户连接 GitHub 仓库配置部署设置自动部署2. Vercel 部署// vercel.json{ "version": 2, "builds": [ { "src": "src/main.ts", "use": "@vercel/deno" } ], "routes": [ { "src": "/(.*)", "dest": "/src/main.ts" } ]}3. Railway 部署# railway.toml[build]builder = "NIXPACKS"[deploy]startCommand = "deno run --allow-net --allow-env src/main.ts"[env]PORT = "8000"进程管理1. 使用 PM2// ecosystem.config.jsmodule.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 } }]};# 安装 PM2npm install -g pm2# 启动应用pm2 start ecosystem.config.js# 查看状态pm2 status# 查看日志pm2 logs# 重启应用pm2 restart deno-app# 停止应用pm2 stop deno-app2. 使用 Systemd# /etc/systemd/system/deno-app.service[Unit]Description=Deno ApplicationAfter=network.target[Service]Type=simpleUser=denoWorkingDirectory=/appEnvironment="PORT=8000"Environment="DATABASE_URL=postgres://..."ExecStart=/usr/local/bin/deno run --allow-net --allow-env /app/src/main.tsRestart=alwaysRestartSec=10[Install]WantedBy=multi-user.target# 启用服务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. 日志管理// logger.tsimport { 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. 健康检查// health.tsimport { 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. 指标收集// metrics.tsclass 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. 连接池// connection-pool.tsclass 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. 缓存策略// cache.tsclass 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. 环境变量管理// config.tsinterface 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. 权限最小化# 只授予必要的权限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.tsCI/CD 集成1. GitHub Actions# .github/workflows/deploy.ymlname: Deployon: 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# .gitlab-ci.ymlstages: - test - build - deploytest: stage: test image: denoland/deno:1.38.0 script: - deno test --allow-all - deno lint - deno fmt --checkbuild: stage: build image: docker:latest services: - docker:dind script: - docker build -t deno-app:$CI_COMMIT_SHA . - docker push deno-app:$CI_COMMIT_SHAdeploy: stage: deploy image: alpine:latest script: - kubectl set image deployment/deno-app deno-app=deno-app:$CI_COMMIT_SHA only: - main故障排查1. 常见问题问题:应用启动失败# 检查日志deno run --log-level=debug src/main.ts# 检查权限deno info src/main.ts问题:内存泄漏// 定期检查内存使用setInterval(() => { const usage = Deno.memoryUsage(); console.log("Memory usage:", usage);}, 60000);问题:性能下降// 使用性能分析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`);最佳实践容器化部署:使用 Docker 确保环境一致性健康检查:实现健康检查端点日志记录:记录关键操作和错误监控指标:收集和监控应用指标自动化部署:使用 CI/CD 自动化部署流程权限最小化:只授予必要的权限资源限制:设置合理的资源限制备份策略:定期备份重要数据Deno 的部署和运维需要综合考虑多个方面,通过合理的规划和实施,可以构建稳定、可靠的生产级应用。
阅读 0·2月21日 16:06