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

面试题手册

什么是Expo EAS?它包含哪些核心服务?

Expo EAS (Expo Application Services) 是Expo官方提供的一套云服务,用于简化Expo应用的构建、提交和更新流程。EAS提供了从开发到部署的完整解决方案。EAS核心服务:EAS Build(构建服务)EAS Build是云端构建服务,可以构建Android APK/IPA和iOS IPA文件。主要功能:云端构建,无需本地配置原生环境支持开发和生产两种构建配置自动处理签名和证书构建历史记录和日志查看并行构建支持使用方法:# 安装EAS CLInpm install -g eas-cli# 配置EASeas build:configure# 构建Android应用eas build --platform android# 构建iOS应用(需要Apple开发者账号)eas build --platform ios# 构建开发版本eas build --profile development --platform androidEAS Submit(提交服务)EAS Submit自动将构建好的应用提交到应用商店。支持的平台:Google Play StoreApple App Store使用方法:# 提交到Google Playeas submit --platform android --latest# 提交到App Storeeas submit --platform ios --latestEAS Update(更新服务)EAS Update允许通过OTA (Over-the-Air)方式更新应用,无需重新提交应用商店。主要功能:即时推送更新支持回滚到之前版本细粒度的更新控制更新分组和发布策略使用方法:# 创建更新eas update --branch production --message "Fix bug"# 查看更新历史eas update:list# 回滚更新eas update:rollback --branch productionEAS配置文件:在项目根目录创建eas.json配置文件:{ "cli": { "version": ">= 5.2.0" }, "build": { "development": { "developmentClient": true, "distribution": "internal" }, "preview": { "distribution": "internal", "android": { "buildType": "apk" } }, "production": { "android": { "buildType": "app-bundle" }, "ios": { "autoIncrement": true } } }, "submit": { "production": { "android": { "serviceAccountKeyPath": "./google-service-account.json" }, "ios": { "appleId": "your-apple-id@email.com", "ascAppId": "YOUR_APP_STORE_CONNECT_APP_ID", "appleTeamId": "YOUR_TEAM_ID" } } }}环境变量管理:EAS支持在构建时注入环境变量:# 设置环境变量eas secret:create --name API_KEY --value "your-api-key"# 在代码中使用const apiKey = process.env.API_KEY;最佳实践:CI/CD集成:将EAS Build集成到GitHub Actions或其他CI/CD流程中版本管理:使用Git分支和EAS Update分支对应管理不同环境构建优化:合理配置构建配置,区分开发和生产环境监控和日志:定期查看构建日志,及时发现和解决问题权限管理:为团队成员分配适当的EAS权限限制和注意事项:iOS构建需要Apple开发者账号和付费开发者计划构建时间取决于项目大小和服务器负载免费账户有构建次数限制某些原生功能可能需要额外配置EAS大大简化了Expo应用的部署流程,使开发者能够更专注于应用开发本身。
阅读 0·2月21日 16:08

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

Expo CLI和Expo Go有什么区别?它们如何协同工作?

Expo CLI和Expo Go是Expo开发流程中的两个核心工具,它们各自承担不同的职责,协同工作以提供高效的开发体验。Expo CLI:Expo CLI是命令行工具,用于创建、构建和管理Expo项目。主要功能:项目初始化:通过npx create-expo-app命令快速创建新的Expo项目,支持TypeScript、JavaScript等多种模板。开发服务器:启动开发服务器,实时编译代码并提供热重载功能。构建配置:配置和管理项目的构建设置,包括应用图标、启动画面、权限配置等。打包发布:支持构建APK、IPA等安装包,或直接发布到Expo服务器。依赖管理:安装和更新Expo SDK版本及依赖包。常用命令:npx create-expo-app my-appnpx expo startnpx expo build:androidnpx expo build:iosExpo Go:Expo Go是一个移动应用,可在Android和iOS设备上安装,用于实时预览和测试Expo应用。主要功能:实时预览:通过扫描二维码或输入URL,在真实设备上查看应用效果。无需构建:开发过程中无需编译原生代码,大幅提升开发效率。跨设备测试:同时在多台设备上测试应用,验证不同屏幕尺寸和系统版本的兼容性。内置SDK:包含完整的Expo SDK,支持所有Expo组件和API。工作流程:使用Expo CLI创建项目并启动开发服务器在移动设备上安装Expo Go应用通过Expo Go连接到开发服务器实时查看代码修改效果限制:Expo Go不支持自定义原生代码,如果项目需要添加自定义原生模块,需要使用Expo Development Build或Eject流程。最佳实践:开发阶段优先使用Expo Go进行快速迭代测试阶段使用Development Build获得更接近生产环境的表现生产构建使用EAS Build生成优化的安装包这两个工具的结合使得Expo开发流程既快速又灵活,适合从原型到生产的完整开发周期。
阅读 0·2月21日 16:06

什么是ERC-20代币标准?请详细说明ERC-20的接口规范和实现方法

ERC-20是以太坊上最广泛使用的代币标准,定义了同质化代币的接口规范。以下是ERC-20标准的详细解析:ERC-20标准概述ERC-20代表"Ethereum Request for Comments 20",由Fabian Vogelsteller于2015年提出。它定义了一套标准接口,使不同代币能够在以太坊生态系统中互操作。必须实现的方法1. totalSupply()返回代币的总供应量。function totalSupply() external view returns (uint256) { return _totalSupply;}2. balanceOf(address account)返回指定账户的代币余额。function balanceOf(address account) external view returns (uint256) { return _balances[account];}3. transfer(address recipient, uint256 amount)从调用者账户转移代币到接收者账户。function transfer(address recipient, uint256 amount) external returns (bool) { _transfer(_msgSender(), recipient, amount); return true;}4. allowance(address owner, address spender)返回授权给spender的代币数量。function allowance(address owner, address spender) external view returns (uint256) { return _allowances[owner][spender];}5. approve(address spender, uint256 amount)授权spender使用调用者的代币。function approve(address spender, uint256 amount) external returns (bool) { _approve(_msgSender(), spender, amount); return true;}6. transferFrom(address sender, address recipient, uint256 amount)使用授权额度从sender账户转移代币到recipient账户。function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true;}可选实现的方法1. name()返回代币名称。function name() external view returns (string memory) { return _name;}2. symbol()返回代币符号。function symbol() external view returns (string memory) { return _symbol;}3. decimals()返回代币的小数位数,通常为18。function decimals() external view returns (uint8) { return _decimals;}事件(Events)1. Transfer代币转移时触发。event Transfer(address indexed from, address indexed to, uint256 value);2. Approval授权时触发。event Approval(address indexed owner, address indexed spender, uint256 value);完整的ERC-20实现示例// SPDX-License-Identifier: MITpragma solidity ^0.8.0;contract MyToken { string private _name; string private _symbol; uint8 private _decimals; uint256 private _totalSupply; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; constructor(string memory name_, string memory symbol_, uint256 initialSupply) { _name = name_; _symbol = symbol_; _decimals = 18; _totalSupply = initialSupply; _balances[msg.sender] = initialSupply; emit Transfer(address(0), msg.sender, initialSupply); } function name() external view returns (string memory) { return _name; } function symbol() external view returns (string memory) { return _symbol; } function decimals() external view returns (uint8) { return _decimals; } function totalSupply() external view returns (uint256) { return _totalSupply; } function balanceOf(address account) external view returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) external returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) external view returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) external returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][msg.sender]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, msg.sender, currentAllowance - amount); } return true; } function _transfer(address sender, address recipient, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[sender] = senderBalance - amount; _balances[recipient] += amount; } emit Transfer(sender, recipient, amount); } function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); }}使用OpenZeppelin库OpenZeppelin提供了经过审计的ERC-20实现,推荐在生产环境中使用。// SPDX-License-Identifier: MITpragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";import "@openzeppelin/contracts/access/Ownable.sol";contract MyToken is ERC20, Ownable { constructor(uint256 initialSupply) ERC20("My Token", "MTK") { _mint(msg.sender, initialSupply); } function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); }}ERC-20的应用场景1. 去中心化金融(DeFi)流动性提供借贷协议衍生品交易2. 治理代币DAO投票协议治理社区激励3. 稳定币USDT、USDC、DAI法币抵押算法稳定4. 实用代币平台访问权限服务支付奖励机制ERC-20的局限性1. 授权机制问题需要两次交易(approve + transferFrom)可能导致授权额度泄露2. 缺乏批量操作每次转移都需要单独交易Gas成本较高3. 无法处理代币回调不支持接收代币时的通知可能导致代币丢失其他ERC代币标准ERC-721非同质化代币(NFT)标准,每个代币都是唯一的。ERC-1155多代币标准,支持同质化和非同质化代币。ERC-777改进的ERC-20标准,支持代币回调和批量操作。ERC-4626金库代币标准,用于DeFi收益聚合器。最佳实践使用OpenZeppelin库:避免重复造轮子,使用经过审计的代码添加访问控制:实现适当的权限管理事件日志:记录所有重要操作安全审计:在部署前进行专业审计测试覆盖:确保充分的测试覆盖率ERC-20标准是以太坊生态系统的基础,理解其工作原理对于开发区块链应用至关重要。
阅读 0·2月21日 16:06