使用 Deno Compile 构建自包含可执行程序

从 Deno v1.6 开始,deno compile 命令就让开发者能够将 JavaScript 和 TypeScript 程序转换成一个独立的二进制可执行文件,在各主要平台运行,无须另外安装依赖或运行时。这带来了几项重要能力:
- 跨平台兼容:分发独立二进制文件,目标系统无须安装 Deno 运行时或依赖。
- 资源打包:把应用所需内容放进二进制文件,方便携带。
- 简化部署:向生产环境交付一个文件,减少复杂性。
- 改善启动时间:相比典型的服务器或运行时配置,启动更快。
自 Deno 2 发布以来,deno compile 又获得了多项改进,包括 npm 包支持、Web Worker 支持、交叉编译、更小的二进制文件,以及带有自定义图标的代码签名。这些升级意味着,你现在不仅可以编译脚本,还可以把桌面游戏等完整应用直接编译为本地二进制程序。
本文介绍 deno compile 的独特之处、工作原理,以及不同应用场景的示例,说明它相较于生态中其他方案的优势。
什么是 deno compile?
Deno 为 JavaScript 和 TypeScript 提供零配置工具链,让你不必先拼装各种工具才能开始写代码。其中一个子命令就是 deno compile,可以将 JavaScript 或 TypeScript 代码构建为一个二进制文件。与 Node 的八步编译流程相比,deno compile 只需一个命令。
该命令让你跳过安装要求,在未安装 Deno 的系统上运行二进制程序,支持 Windows、macOS 和 Linux 等平台。Deno 编译出来的二进制文件也针对体积做了优化。对于来自 Node 的开发者,这种简洁性可以降低在不同环境间迁移代码的阻力,尤其是在为其他操作系统进行交叉编译时。
deno compile 如何工作
虽然名字叫“compile”,但 deno compile 并不是在传统意义上编译 JavaScript。它将 JavaScript 和 TypeScript 嵌入一个特殊的 Deno 运行时二进制文件,名为 denort,即“Deno runtime”的缩写。
执行 deno compile 时,过程大致如下:
- 下载精简版运行时 denort。如果为其他操作系统交叉编译,就下载对应平台的 denort。
- 将脚本及依赖打包为
eszip文件。这是一种用于序列化 ECMAScript 模块图的无损格式。 - 使用 Sui 将打包内容注入 denort 二进制文件。
由于打包内容嵌入的是可执行文件的一个节区,最终文件仍然能够进行代码签名。
示例
基本的自包含二进制文件
先来看将 TypeScript 脚本转换成单个可执行文件有多简单。
// main.ts
import open from "jsr:@rdsq/open";
await open("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
编译只需:
deno compile -A -o runme main.ts
这会生成包含所有必要依赖的可执行二进制文件 runme。现在可以分享这一个文件,最终用户无须安装 Deno。
注意,可以在 deno compile 命令中传入需要明确启用的权限选项,例如 --allow-net。这些权限选项会保留在编译后的二进制文件中,为限制二进制文件的使用方式提供额外的安全控制。
为 Windows 交叉编译
假设要把应用分享给 Windows 用户,可以使用 --target 选项进行交叉编译:
deno compile --allow-all -o runme --target x86_64-pc-windows-msvc main.ts
这样就得到了兼容 Windows 的 runme.exe。还可以通过 --icon 选项添加自定义图标,让应用更完善:
deno compile --allow-all -o runme --target x86_64-pc-windows-msvc --icon icon.ico main.ts
代码签名
代码签名用于验证二进制文件及其可信来源,对于软件分发十分重要。Deno 简化了这一过程;在 macOS 上,也可以像处理其他应用一样使用 codesign。
下面是原文展示的二进制完整性验证及输出:
codesign --verify -vv ./runme
./runme: valid on disk
./runme: satisfies its Designated Requirement
原文示例中,编译得到的二进制文件签名有效,可以准备分发。
编译 npm 包
deno compile 当然也能使用 npm 包。这适合对性能敏感的 CI 环境,或者需要提供某个工具的单一可移植版本的场景。
例如,可以用 deno compile 编译 npm 本身:
deno compile -A npm:npm
运行编译后的 npm 二进制文件很简单,原文测试中还观察到了性能提升:通过 Deno 编译的 npm 二进制文件运行速度约为常规 npm 的 1.9 倍。
# hyperfine "./npm -v" "npm -v"
Benchmark 1: ./npm -v
Time (mean ± σ): 40.1 ms ± 2.0 ms [User: 37.7 ms, System: 5.3 ms]
Range (min … max): 38.9 ms … 51.8 ms 71 runs
Benchmark 2: npm -v
Time (mean ± σ): 75.2 ms ± 7.6 ms [User: 59.1 ms, System: 9.0 ms]
Range (min … max): 69.2 ms … 105.0 ms 40 runs
Summary
./npm -v ran
1.87 ± 0.21 times faster than npm -v
这种方式也适用于 prettier、eslint 等常见 npm 工具,可以获得运行更快、便于携带的工具版本。
游戏和桌面应用
deno compile 也让基于 Web 的游戏和应用有机会被打包成原生桌面可执行文件。例如,可以把 HTML/CSS/JavaScript 游戏转换成二进制文件,方便在桌面上分享和启动。
原文展示了一个将 HTML 游戏通过 deno compile 打包的演示。这个 Flappybird 克隆项目可以使用 Deno 编译为桌面应用。对应编译命令为:
deno compile --unstable-ffi --env -o flappybird -A main.js
打包资源
在原文发表于2024年11月时,deno compile 尚不直接支持资源打包。不过,可以将 .png 或其他资源文件转换成 Base64 编码的 .js 文件,再将它们导入项目,使它们被包含在最终二进制文件中。虽然属于变通方式,这仍让开发者能够把游戏和其他资源打包为单个可执行文件。
接下来会怎样
Deno 团队会继续依据反馈改进 deno compile,计划简化资源打包、支持 Svelte 等更多框架、缩小二进制文件体积,并通过缓存进一步改善启动时间。











暂无评论内容