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

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

2月21日 16:08

Deno 的 deno compile 命令可以将 TypeScript/JavaScript 代码编译为独立的可执行文件,这使得分发和部署变得更加简单。这个功能对于创建命令行工具和独立应用程序特别有用。

deno compile 概述

deno compile 将 Deno 脚本及其所有依赖打包成一个单一的可执行文件,无需在目标机器上安装 Deno。

基本用法

1. 简单编译

bash
# 编译为可执行文件 deno compile app.ts # 默认输出文件名与输入文件相同(无扩展名) # app.ts → app (Linux/Mac) 或 app.exe (Windows)

2. 指定输出文件名

bash
# 指定输出文件名 deno compile --output=myapp app.ts # Windows deno compile --output=myapp.exe app.ts

3. 指定目标平台

bash
# 编译为 Linux 可执行文件 deno compile --target=x86_64-unknown-linux-gnu app.ts # 编译为 macOS 可执行文件 deno compile --target=x86_64-apple-darwin app.ts deno compile --target=aarch64-apple-darwin app.ts # Apple Silicon # 编译为 Windows 可执行文件 deno compile --target=x86_64-pc-windows-msvc app.ts

4. 包含权限

bash
# 编译时包含权限,运行时无需再次指定 deno compile --allow-net --allow-read app.ts # 包含所有权限 deno compile --allow-all app.ts

5. 设置环境变量

bash
# 设置编译时的环境变量 deno compile --env=API_KEY=secret app.ts

实际应用示例

1. 命令行工具

typescript
// cli.ts #!/usr/bin/env -S deno run const name = Deno.args[0] || "World"; const verbose = Deno.args.includes("--verbose"); if (verbose) { console.log("Verbose mode enabled"); } console.log(`Hello, ${name}!`); // 文件操作示例 const filename = Deno.args[1]; if (filename) { try { const content = await Deno.readTextFile(filename); console.log(`File content: ${content}`); } catch (error) { console.error(`Error reading file: ${error.message}`); } }

编译:

bash
deno compile --allow-read --output=hello cli.ts ./hello Deno --verbose

2. Web 服务器

typescript
// server.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const PORT = Deno.env.get("PORT") || "8000"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello from Deno server!", { headers: { "Content-Type": "text/plain" }, }); } if (url.pathname === "/health") { return new Response(JSON.stringify({ status: "ok" }), { headers: { "Content-Type": "application/json" }, }); } return new Response("Not Found", { status: 404 }); }; console.log(`Server running on port ${PORT}`); await serve(handler, { port: parseInt(PORT) });

编译:

bash
deno compile --allow-net --allow-env --output=server server.ts ./server

3. 文件处理工具

typescript
// file-processor.ts import { walk } from "https://deno.land/std@0.208.0/fs/walk.ts"; const directory = Deno.args[0] || "."; const pattern = Deno.args[1]; console.log(`Scanning directory: ${directory}`); if (pattern) { console.log(`Pattern: ${pattern}`); } let count = 0; for await (const entry of walk(directory)) { if (entry.isFile) { if (!pattern || entry.name.includes(pattern)) { console.log(`Found: ${entry.path}`); count++; } } } console.log(`Total files found: ${count}`);

编译:

bash
deno compile --allow-read --output=scan file-processor.ts ./scan ./src .ts

4. API 客户端

typescript
// api-client.ts const API_URL = Deno.env.get("API_URL") || "https://api.example.com"; const API_KEY = Deno.env.get("API_KEY"); if (!API_KEY) { console.error("API_KEY environment variable is required"); Deno.exit(1); } async function fetchData(endpoint: string): Promise<any> { const response = await fetch(`${API_URL}${endpoint}`, { headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", }, }); if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } return response.json(); } const endpoint = Deno.args[0] || "/users"; console.log(`Fetching data from: ${API_URL}${endpoint}`); try { const data = await fetchData(endpoint); console.log(JSON.stringify(data, null, 2)); } catch (error) { console.error(`Error: ${error.message}`); Deno.exit(1); }

编译:

bash
deno compile --allow-net --allow-env --output=api-client api-client.ts API_KEY=your_key ./api-client /users

高级用法

1. 交叉编译

在 macOS 上编译 Linux 可执行文件:

bash
deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts

2. 压缩可执行文件

使用 upx 压缩编译后的可执行文件:

bash
# 安装 upx brew install upx # macOS apt install upx # Linux # 压缩 upx --best myapp

3. 创建图标(仅限 Windows)

bash
# 为 Windows 可执行文件添加图标 deno compile --icon=icon.ico --output=myapp.exe app.ts

4. 设置元数据

bash
# 设置可执行文件元数据 deno compile \ --output=myapp \ --app-name="My Application" \ --app-version="1.0.0" \ app.ts

部署场景

1. Docker 部署

dockerfile
# 多阶段构建 FROM denoland/deno:1.38.0 AS builder WORKDIR /app COPY . . RUN deno compile --allow-net --allow-read --output=app server.ts FROM debian:bullseye-slim WORKDIR /app COPY --from=builder /app/app . EXPOSE 8000 CMD ["./app"]

2. 云函数部署

typescript
// cloud-function.ts export async function handler(event: any): Promise<any> { const name = event.name || "World"; return { statusCode: 200, body: JSON.stringify({ message: `Hello, ${name}!` }), }; }

编译:

bash
deno compile --output=handler cloud-function.ts

3. 独立应用分发

bash
# 为不同平台编译 deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts deno compile --target=x86_64-apple-darwin --output=myapp-macos app.ts deno compile --target=x86_64-pc-windows-msvc --output=myapp-windows.exe app.ts # 创建发布包 mkdir release cp myapp-linux release/ cp myapp-macos release/ cp myapp-windows.exe release/

限制和注意事项

1. 文件大小

编译后的可执行文件通常较大(约 50-100 MB),因为包含了整个 Deno 运行时和 V8 引擎。

2. 动态导入

动态导入的模块会在运行时下载,不会被包含在可执行文件中:

typescript
// 这个模块不会被打包 const module = await import("https://example.com/module.ts");

3. 原生模块

使用 FFI 的原生模块需要确保目标平台上有相应的库。

4. 权限

编译时指定的权限在运行时生效,无法在运行时更改。

最佳实践

  1. 指定目标平台:明确指定编译目标,避免兼容性问题
  2. 最小权限:只授予必要的权限
  3. 测试编译结果:在目标平台上测试可执行文件
  4. 版本控制:记录编译命令和 Deno 版本
  5. 文档化:在 README 中说明如何编译和运行
  6. 使用 CI/CD:自动化编译和发布流程

与其他工具对比

特性deno compilepkgnexe
原生支持
跨平台
文件大小较大中等中等
性能
维护性官方支持社区维护社区维护

故障排查

1. 编译失败

bash
# 查看详细错误信息 deno compile --log-level=debug app.ts

2. 运行时错误

bash
# 检查权限 deno compile --allow-net --allow-read app.ts # 检查环境变量 API_KEY=secret ./app

3. 兼容性问题

bash
# 检查目标平台 deno compile --target=x86_64-unknown-linux-gnu app.ts # 使用 Docker 测试 docker run --rm -v $(pwd):/app debian:bullseye-slim ./app

deno compile 为 Deno 应用程序提供了简单而强大的分发方式,特别适合需要独立部署的场景。通过合理使用这个功能,可以大大简化应用程序的部署和分发流程。

标签:Deno