js 编译二进制,编译成 macOS/iOS/Android/Linux/Windows 全平台原生应用
一、Perry 是什么
Perry 是一个用 Rust 编写的原生 TypeScript 编译器,核心思路非常清晰:
输入 TypeScript → 输出各平台原生可执行文件
支持的平台:
桌面:macOS、iPadOS、Windows、Linux
移动:iOS、Android
可穿戴:watchOS、tvOS
Web:WebAssembly + JavaScript
对比主流跨平台方案:
方案 运行时依赖 UI 渲染 性能 体积
Perry 无 原生组件 极高 2-5 MB
Electron Node.js + Chromium Chromium 中等 100-300 MB
React Native JS 引擎 原生组件 高 10-20 MB
Flutter 自带引擎 Skia 自绘 高 10-30 MB
Tauri 系统 WebView WebView 高 3-15 MB
二、为什么选择 Perry
无需运行时
传统 Node.js 应用分发后,机器上必须安装 Node.js 环境。Perry 编译出来的是一个独立的原生二进制文件,双击即可运行,完全不依赖任何运行时。
这对以下场景极具价值:
分发给非技术用户:对方无需配置任何环境
CLI 工具分发:一行命令安装,其他用户无需装 Node.js
嵌入式场景:资源受限环境
快速编译
Perry 使用 SWC(Rust 写的超快 JS/TS 编译器)做前端解析,配合 LLVM 优化代码生成,直接输出原生机器码。
流程:
TypeScript → SWC 解析 → AST → LLVM IR → 优化 → 原生机器码
没有中间层,没有解释执行,编译出来就是可以直接运行的二进制。
体积小巧
Perry 编译出来的二进制文件只有 2-5 MB。
如果需要使用纯 JavaScript npm 包(例如 Moment.js、Lodash),可以启用可选的 V8 运行时,此时体积为 15-20 MB——仍然远小于 Electron 的 100-300 MB。
可重现构建
相同输入 → 相同输出(相同二进制)
这解决了传统 Node.js 项目的痛点:同一份代码,在 A 机器上 npm install 装的是 A 版本依赖,在 B 机器上装的是 B 版本,最终打包出来的结果可能不一致。
Perry 的编译结果是确定的,换机器、换 CI、换团队都能完美复现。
全面的标准库
Perry 内置了 TypeScript 标准库的实现,覆盖了 Node.js 开发者熟悉的大部分 API:
文件系统:fs
路径处理:path
加密:crypto
系统信息:os
二进制数据:Buffer
子进程:child_process
无需引入额外的 npm 包,直接使用标准 API。
可选 V8 运行时
如果确实需要使用纯 JavaScript npm 包,Perry 支持启用 V8 运行时模式,获得完整的 npm 生态兼容性。
这意味着你可以渐进式迁移:先用原生能力开发核心功能,再按需引入 npm 包。
三、25+ 原生 UI 组件
Perry 内置了 25+ 原生 UI 组件,且每个组件都是真正的平台原生实现,不是 Web 视图模拟的:
组件 macOS/iOS Android Windows Linux
按钮 AppKit UIKit Win32 GTK4
文本框 AppKit UIKit Win32 GTK4
文本区域 AppKit UIKit Win32 GTK4
表格 AppKit UIKit Win32 GTK4
Canvas AppKit UIKit Win32 GTK4
二维码 AppKit UIKit Win32 GTK4
安全输入框 AppKit UIKit Win32 GTK4
启动画面 AppKit UIKit Win32 GTK4
这意味着:
不需要 WebView:不是用 HTML/CSS 渲染,而是调用平台原生 API
原生外观:按钮、输入框看起来和系统原生应用完全一致
原生性能:UI 渲染不走浏览器引擎,没有额外的抽象层
四、编译时插件系统
Perry 的模块在编译时组合,这与运行时插件系统有本质区别:
传统运行时插件(Electron/Node.js): 主进程 ↔ IPC ↔ 插件进程
Perry 编译时插件:
TypeScript 代码 → 编译时内联 → 最终二进制文件中的原生函数调用
优势:
无运行时插件开销
无 IPC 通信延迟
依赖项直接编译进二进制,发布时只有一个文件
五、真正的多线程
Perry 支持真正的操作系统线程,而非 Node.js 的事件循环伪并发:
// 使用 parallelMap 并行处理
const results = parallelMap(items, (item) => {
return processItem(item); // 在独立线程中执行
});
// 使用 spawn 创建新线程
const worker = spawn(() => {
doHeavyComputation();
});
编译时安全性:Perry 的类型系统在编译时就拒绝可变捕获(mutable capture),确保不会有数据竞争。这意味着使用多线程不需要 SharedArrayBuffer,不需要 Worker API,代码安全且高性能。
六、编译时 i18n
Perry 内置了国际化(i18n)支持,在编译阶段完成字符串提取和翻译验证:
自动字符串提取:从代码中自动提取需要翻译的字符串
30+ 语言环境:支持 CLDR 复数规则
编译时验证:翻译缺失或错误在编译期就能发现
嵌入二进制:翻译数据直接编译进二进制文件,运行时查找开销几乎为零
import { t, plural } from '@perry/i18n';
const message = t('hello'); // 自动替换为当前语言环境的翻译
const count = plural('item_count', items.length); // 自动处理复数形式
七、快速开始
安装 Perry
# macOS / Linux git clone https://github.com/PerryTS/perry.git cd perry cargo build --release # Binary at: target/release/perry # npm安装perry npm install -g @perryts/perry
创建项目
my-api/ ├── package.json ├── src/ │ ├── main.ts │ ├── config.ts │ └── routes/ │ └── users.ts └── node_modules/
src/config.ts
export const config = {
port: 3000,
dbHost: process.env.DB_HOST || 'localhost',
};
src/routes/users.ts
export function getUsers(): object[] {
return [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
}
export function getUserById(id: number): object | undefined {
return getUsers().find((u: any) => u.id === id);
}
src/main.ts
import fastify from 'fastify';
import { config } from './config';
import { getUsers, getUserById } from './routes/users';
const app = fastify();
app.get('/api/users', async () => {
return getUsers();
});
app.get('/api/users/:id', async (request) => {
const { id } = request.params as { id: string };
return getUserById(parseInt(id));
});
app.listen({ port: config.port }, () => {
console.log(`Server running on port ${config.port}`);
});
编译到各平台
# 编译为当前平台 perry compile src/main.ts -o my-api && ./my-api # or: perry run # 编译为指定平台 perry compile src/main.ts --target macos # macOS perry compile src/main.ts --target ios # iOS perry compile src/main.ts --target android # Android perry compile src/main.ts --target windows # Windows perry compile src/main.ts --target linux # Linux perry compile src/main.ts --target wasm # WebAssembly
输出示例
$ perry build --target macos Compiling TypeScript... Optimizing with LLVM... Linking native binary... ✅ Done! Output: dist/my-app (3.2 MB)
八、性能对比
指标 Perry Electron Tauri
启动时间 <100ms 1-3s 200-500ms
内存占用 20-50MB 200-500MB 50-150MB
二进制体积 2-5 MB 100-300 MB 3-15 MB
GPU 加速 原生支持 WebGL WebView
系统集成 原生 API 有限 有限
九、适用场景
Perry 非常适合:
需要同时覆盖桌面 + 移动端 + Web 的应用
CLI 工具开发者,不想让用户装 Node.js
对性能、体积、启动速度有较高要求的场景
已有 TypeScript 技术栈,不想学新语言
Perry 目前不太适合:
需要大量 Web 生态库(React/Vue 等前端框架)的应用
复杂 Web 应用(此时 Tauri 可能是更好的选择)
还在快速迭代、需要灵活生态的项目
十、总结
Perry 的核心价值在于:
用 TypeScript 写一次,编译成所有平台的原生应用——没有 Electron,没有 WebView,没有运行时依赖。
它代表了一种新的跨平台开发思路:不是用 Web 技术模拟原生体验,而是真正把 TypeScript 编译成原生代码,用各平台原生 API 渲染 UI。
如果你厌倦了 Electron 的臃肿,又想要真正的跨平台原生体验,Perry 值得关注。
源码地址:https://github.com/PerryTS/perry

