Perry:用 TypeScript 写原生应用,彻底告别 Node.js 和 Electron

0 阅读7分钟

官方仓库:github.com/PerryTS/per… · 官方网站:www.perryts.com · 当前版本:v0.5.306

一、先说痛点:你是不是也在忍受这些?

你用 TypeScript 写了一个 CLI 工具或桌面应用,然后:

  • 用户需要先装 Node.js 才能运行,体验极差
  • 打包成 Electron 应用,随便一个 Hello World 就 200 MB 起步
  • 冷启动要等好几秒,内存动辄吃掉 300 MB
  • 想发布到 App Store?想都别想

Perry 的出现就是为了彻底解决这些问题。


二、Perry 是什么?一句话说清楚

Perry 是一个用 Rust 写的原生 TypeScript 编译器。

它把你的 .ts 文件直接编译成平台原生可执行文件,底层使用 SWC 解析 TypeScript、LLVM 生成机器码。

你的 TypeScript 代码
       ↓
   perry compile
       ↓
  原生二进制文件(macOS / Windows / Linux / iOS / Android / WASM…)

没有 Node.js,没有 V8,没有 Electron,没有 WebView,就是一个单文件二进制,拿来就跑。


三、整体架构图(一图胜千言)

┌─────────────────────────────────────────────────────────────┐
│                     Perry 编译流水线                         │
│                                                             │
│  TypeScript (.ts)                                           │
│       │                                                     │
│       ▼                                                     │
│  ┌─────────────┐   SWC 解析器(Rust 实现,速度极快)          │
│  │  SWC Parser │   ← 解析 TypeScript/ES 语法树              │
│  └──────┬──────┘                                           │
│         │ AST (抽象语法树)                                   │
│         ▼                                                   │
│  ┌─────────────┐   HIR 高层中间表示                          │
│  │  HIR Lower  │   ← 类型擦除、泛型单态化                    │
│  └──────┬──────┘                                           │
│         │ HIR (High-level IR)                              │
│         ▼                                                   │
│  ┌─────────────┐   优化变换阶段                              │
│  │  Transform  │   ← 闭包转换、async/await 展开、内联        │
│  └──────┬──────┘                                           │
│         │                                                   │
│         ▼                                                   │
│  ┌─────────────┐   LLVM 代码生成                            │
│  │  LLVM IR    │   ← NaN-Boxing、逃逸分析、标量替换           │
│  └──────┬──────┘                                           │
│         │                                                   │
│         ▼                                                   │
│  ┌─────────────┐   系统链接器                               │
│  │  Linker     │   ← Xcode CLT / GCC / MSVC               │
│  └──────┬──────┘                                           │
│         │                                                   │
│         ▼                                                   │
│   原生可执行文件 (~330KB ~ 5MB)                              │
└─────────────────────────────────────────────────────────────┘

四、跨平台目标矩阵

Perry 的最大亮点是一套代码,10 个平台目标

┌──────────────────────────────────────────────────────────────┐
│                    Perry 支持的目标平台                        │
├──────────────┬──────────────────┬────────────────────────────┤
│   平台        │  底层 UI 框架     │  编译命令                   │
├──────────────┼──────────────────┼────────────────────────────┤
│ macOS        │ AppKit           │ perry compile (默认)        │
│ iOS / iPadOS │ UIKit            │ --target ios               │
│ visionOS     │ UIKit            │ --target visionos          │
│ tvOS         │ UIKit            │ --target tvos              │
│ watchOS      │ WatchKit         │ --target watchos           │
│ Android      │ Android Views    │ --target android           │
│ Windows      │ Win32            │ perry compile (默认)        │
│ Linux        │ GTK4             │ perry compile (默认)        │
│ Web          │ DOM (JS 输出)    │ --target web               │
│ WebAssembly  │ DOM (WASM)       │ --target wasm              │
└──────────────┴──────────────────┴────────────────────────────┘

五、安装(5 分钟上手)

方式一:npm / npx(推荐,零门槛)

# 项目本地安装(推荐,锁定版本)
npm install @perryts/perry

# 全局安装
npm install -g @perryts/perry

# 零安装,一次性试用
npx -y @perryts/perry compile src/main.ts -o myapp

方式二:Homebrew(macOS)

brew install perryts/perry/perry

方式三:winget(Windows)

winget install PerryTS.Perry

方式四:APT(Ubuntu / Debian)

curl -fsSL https://perryts.github.io/perry-apt/perry.gpg.pub | sudo gpg --dearmor -o /usr/share/keyrings/perry.gpg
echo "deb [signed-by=/usr/share/keyrings/perry.gpg] https://perryts.github.io/perry-apt stable main" | sudo tee /etc/apt/sources.list.d/perry.list
sudo apt update && sudo apt install perry

安装完后验证环境:

perry doctor

六、从零开始:三个由浅入深的示例


🟢 示例一(入门级):Hello World

创建 hello.ts

const greeting: string = "Hello, Perry!";
console.log(greeting);

编译并运行:

perry compile hello.ts -o hello
./hello
# Hello, Perry!

产物是一个约 330KB 的独立二进制,不需要任何运行时依赖


🟡 示例二(进阶级):文件读写 + HTTP 服务器

Perry 内置了与 Node.js 兼容的标准库,直接用熟悉的 API。

// server.ts
import fastify from 'fastify';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';

const app = fastify({ logger: true });

// 读取配置文件
const configPath = join(process.cwd(), 'config.json');
const config = existsSync(configPath)
  ? JSON.parse(readFileSync(configPath, 'utf-8'))
  : { port: 3000 };

// 路由定义
app.get('/health', async () => {
  return { status: 'ok', uptime: process.uptime() };
});

app.get('/api/users', async () => {
  return [
    { id: 1, name: 'Alice', role: 'admin' },
    { id: 2, name: 'Bob', role: 'user' },
  ];
});

app.post('/api/users', async (req, reply) => {
  const body = req.body as { name: string };
  return reply.code(201).send({ id: Date.now(), name: body.name });
});

// 启动服务
app.listen({ port: config.port }, () => {
  console.log(`Server running on http://localhost:${config.port}`);
});

编译成独立服务器:

perry compile server.ts -o server
./server
# 启动!无需 node_modules,无需 Node.js 环境

Perry 对 fastifymysql2ioredisbcrypt 等常用包做了原生 Rust 重实现,导入语法完全相同,性能是 Node.js 版的数倍。


🔴 示例三(高级):多文件项目 + 原生 GUI + 多线程

这个示例展示 Perry 最强大的能力:原生桌面 GUI + 真正的多线程。

项目结构:

my-app/
├── src/
│   ├── main.ts          ← 程序入口
│   ├── ui.ts            ← 原生 GUI 界面
│   └── worker.ts        ← 多线程计算
└── package.json

src/worker.ts — 多线程计算:

import { parallelMap, parallelFilter, spawn } from 'perry/thread';

// 并行 Fibonacci 计算(跑满所有 CPU 核心)
export async function computeFibBatch(inputs: number[]): Promise<number[]> {
  return parallelMap(inputs, (n) => {
    const fib = (x: number): number => x <= 1 ? x : fib(x - 1) + fib(x - 2);
    return fib(n);
  });
}

// 后台线程执行耗时任务,不阻塞 UI
export async function heavyTask(): Promise<string> {
  return spawn(() => {
    // 这段代码在独立 OS 线程中运行
    let sum = 0;
    for (let i = 0; i < 100_000_000; i++) sum += i;
    return `Result: ${sum}`;
  });
}

src/ui.ts — 原生 GUI 界面:

import {
  App, VStack, HStack, Text, Button, Spacer,
  stackSetDistribution, stackSetAlignment,
} from 'perry/ui';
import { computeFibBatch, heavyTask } from './worker';

// 侧边栏
const sidebar = VStack(8, [
  Text("🔢 Perry Demo"),
  Text("Fibonacci"),
  Text("Heavy Task"),
  Spacer(),
]);
sidebar.setEdgeInsets(12, 12, 12, 12);
sidebar.setBackgroundColor("#1e1e2e");

// 主内容区
const resultText = Text("点击按钮开始计算...");

const fibBtn = Button("计算 fib(1..10) 并行", async () => {
  const results = await computeFibBatch([1,2,3,4,5,6,7,8,9,10]);
  resultText.setText(`结果: ${results.join(', ')}`);
});

const heavyBtn = Button("后台耗时任务", async () => {
  resultText.setText("计算中...");
  const result = await heavyTask();
  resultText.setText(result);
});

const actions = HStack(8, [fibBtn, heavyBtn]);
stackSetDistribution(actions, 1); // 两个按钮等宽

const content = VStack(16, [
  Text("Perry 原生 GUI 示例"),
  resultText,
  Spacer(),
  actions,
]);
content.setEdgeInsets(20, 20, 20, 20);
stackSetAlignment(content, 5); // Leading 对齐

export { sidebar, content };

src/main.ts — 程序入口:

import { App, SplitView, splitViewAddChild } from 'perry/ui';
import { sidebar, content } from './ui';

const split = SplitView();
splitViewAddChild(split, sidebar);
splitViewAddChild(split, content);

App({ title: 'Perry Demo', width: 900, height: 600, body: split });

编译并运行:

perry compile src/main.ts -o demo
./demo          # macOS 用 AppKit 渲染
# 或
perry compile src/main.ts --target android -o DemoApp  # Android 原生

UI 架构示意图:

┌────────────────────────────────────────────────┐
│  Perry App (原生窗口,AppKit / Win32 / GTK4)    │
│                                                 │
│  ┌──────────┐  ┌──────────────────────────────┐ │
│  │ Sidebar  │  │        Content               │ │
│  │ VStack   │  │        VStack                │ │
│  │          │  │                              │ │
│  │ Text     │  │  Text "Perry Demo"           │ │
│  │ Text     │  │  Text resultText ← 动态更新  │ │
│  │ Text     │  │  Spacer                      │ │
│  │ Spacer   │  │                              │ │
│  │          │  │  HStack (FillEqually)        │ │
│  │          │  │  ┌──────────┬──────────────┐  │ │
│  │          │  │  │ fibBtn   │  heavyBtn    │  │ │
│  │          │  │  └──────────┴──────────────┘  │ │
│  └──────────┘  └──────────────────────────────┘ │
└────────────────────────────────────────────────┘
        ↑ 真正的原生组件,不是 WebView!

七、性能:真的有多快?

这是 Perry 官方基准测试(Apple Silicon M1,和 Node.js v25 对比):

┌────────────────┬────────┬──────────┬────────┬────────────────────┐
│ 测试项          │ Perry  │ Node.js  │ Bun    │ Perry 优势          │
├────────────────┼────────┼──────────┼────────┼────────────────────┤
│ 闭包创建(1000万)│  10ms309ms51ms  │ 🚀 31x faster      │
│ 阶乘(模运算)    │  31ms596ms98ms  │ 🚀 19x faster      │
│ 类方法调用      │   1ms11ms9ms  │ 🚀 11x faster      │
│ 数值循环(1亿)  │  15ms61ms50ms  │ 🚀 4x faster       │
│ 调和级数(5000万)│  14ms52ms52ms  │ 🚀 3.7x faster     │
│ 斐波那契(40)   │ 320ms1033ms521ms  │ 🚀 3.2x faster     │
│ JSON 往返      │ 314ms377ms250ms  │ ✅ 同量级            │
└────────────────┴────────┴──────────┴────────┴────────────────────┘

二进制大小对比:

Perry Hello World     ████  ~330 KB
Perry + 完整 stdlib  ████████████████████  ~5 MB
Node.js 运行时        ████████████████████████████████████████ ~80 MB
Bun 运行时            ████████████████████████████████████████ ~90 MB

八、Perry 凭什么这么快?编译器优化揭秘

Perry 的性能来自多层优化,用流程图来理解:

┌────────────────────────────────────────────────────────┐
│               Perry 核心优化技术                         │
│                                                        │
│  ① NaN-Boxing                                          │
│     所有值用 64-bit 表示(f64/u64 复用),              │
│     数字无需装箱,直接寄存器操作                         │
│                                                        │
│  ② 逃逸分析 + 标量替换 (Scalar Replacement)            │
│     let p = new Point(x, y); sum += p.x + p.y;        │
│     → p 不逃逸 → 字段直接变成寄存器变量                 │
│     → 零堆分配!                                        │
│                                                        │
│  ③ 内联 Bump 分配器                                     │
│     逃逸对象用 13 指令内联 arena bump                   │
│     → 不调用 malloc,热路径无函数调用开销                │
│                                                        │
│  ④ Fast-Math 标志 (reassoc + contract)                 │
│     → 打破串行累加器依赖链                               │
│     → LLVM 自动向量化为 <2 x double> 并行累加           │
│                                                        │
│  ⑤ i64 特化                                            │
│     纯数值递归函数 → 原生 i64 寄存器                    │
│     → 整数模运算: fptosi→srem→sitofp(比 fmod 快 64x)  │
│                                                        │
│  ⑥ 并行编译 (rayon)                                     │
│     模块代码生成、IR 优化、符号扫描 → 多核并行            │
└────────────────────────────────────────────────────────┘

九、关键特性速览

原生 npm 包支持(无需 Node.js)

Perry 对以下包做了 Rust 原生重实现,直接 import 就用:

分类支持的包
HTTPfastify, axios, node-fetch, ws
数据库mysql2, pg, ioredis, mongodb, better-sqlite3
安全bcrypt, argon2, jsonwebtoken
工具dotenv, uuid, nodemailer, zlib, node-cron
数据处理cheerio, sharp, lodash, date-fns

如果需要使用纯 JS 的 npm 包(不在原生支持列表中),可以启用可选的 V8 嵌入:

perry compile src/main.ts --enable-js-runtime -o myapp
# 体积增加约 15MB,换来完整 npm 生态兼容

编译时国际化(i18n)

import { t, Currency, ShortDate } from 'perry/i18n';

console.log(t('hello'));               // "Hallo"(德语环境)
console.log(t('items', { count: 3 })); // "3 Artikel"(CLDR 复数规则)
console.log(Currency(9.99, 'EUR'));    // "9,99 €"
console.log(ShortDate(Date.now()));    // "24.03.2026"

所有翻译字符串在编译时烘焙进二进制,运行时零查找开销。在 perry.toml 配置:

[i18n]
default_locale = "en"
locales = ["en", "de", "fr", "ja"]

原生 React(perry-react)

用你熟悉的 React 写原生桌面/移动 App:

import { useState } from 'react';
import { createRoot } from 'react-dom/client';

function Counter() {
  const [n, setN] = useState(0);
  return (
    <div>
      <h1>Count: {n}</h1>
      <button onClick={() => setN(n + 1)}>+</button>
    </div>
  );
}

// 渲染到原生窗口,不是 DOM!
createRoot(null, { title: 'Counter', width: 300, height: 200 }).render(<Counter />);

插件系统(编译时链接)

// my-plugin.ts
import { PluginRegistry } from 'perry/plugin';

export function activate(api: any) {
  api.registerTool('my-tool', (args: any) => {
    return `Processed: ${JSON.stringify(args)}`;
  });
}
# 编译成原生动态库
perry compile my-plugin.ts --output-type dylib -o my-plugin.dylib

插件在编译期直接链接,没有运行时 IPC 开销。


十、工程项目结构(生产级)

Perry 官方 8 个示例项目,覆盖常见生产场景:

example-code/
├── express-postgres/      ← Express + PostgreSQL,多文件路由、中间件
├── fastify-redis-mysql/   ← Fastify + Redis + MySQL,限速、缓存层
├── hono-mongodb/          ← 轻量 HTTP + MongoDB 文档数据库
├── nestjs-typeorm/        ← NestJS + TypeORM,装饰器架构、依赖注入
├── nextjs-prisma/         ← Prisma ORM,数据库迁移
├── koa-redis/             ← Koa 中间件组合,Session 存储
├── http-server/           ← 原生 HTTP,低层路由
└── blockchain-demo/       ← 纯 TypeScript 区块链实现

任意一个都可以直接编译运行:

cd example-code/fastify-redis-mysql
npm install
perry compile src/index.ts -o server
./server

十一、发布流程:从代码到 App Store

Perry 提供完整的云端发布流程:

┌────────────┐    perry compile    ┌──────────────┐
│ TypeScript │ ─────────────────→ │  本地二进制   │
│  源代码    │                    │  (开发测试用) │
└────────────┘                    └──────────────┘
      │
      │ perry publish ios/android/macos
      ↓
┌──────────────┐
│ perry-hub   │  ← 云端交叉编译 + 代码签名服务
│ (云端构建)  │
└──────┬───────┘
       │
       ├──→ App Store (iOS / macOS)
       ├──→ Play Store (Android)
       └──→ 直接下载包 (Linux / Windows)
# 一条命令完成交叉编译、签名和提交
perry publish ios
perry publish android
perry publish macos

十二、UI 测试框架(Geisterhand)

Perry 内置了名为 Geisterhand 的 UI 自动化测试框架:

# 编译时启用测试服务器
perry compile src/main.ts --enable-geisterhand -o myapp
./myapp

# 测试服务器在 http://localhost:7676 暴露 HTTP API
# 支持截图、点击、输入等操作,覆盖全平台

十三、对比其他跨平台方案

┌─────────────┬──────────────┬────────────┬──────────────┬──────────────┐
│ 框架         │ 语言         │ 原生控件    │ 运行时开销   │ 二进制大小   │
├─────────────┼──────────────┼────────────┼──────────────┼──────────────┤
│ Perry ★     │ TypeScript   │ ✅ 真原生   │ ❌ 无        │ 2-5 MB       │
│ React Native│ JS/TS        │ ✅ 原生     │ ⚠️ Hermes/V8 │ ~60 MB       │
│ Flutter     │ Dart         │ ❌ 自绘     │ ⚠️ Dart VM  │ ~20 MB       │
│ Electron    │ JS/TS        │ ❌ WebView  │ ⚠️ V8+Chrome │ ~150 MB      │
│ Tauri       │ JS/TS+Rust   │ ❌ WebView  │ ⚠️ 系统WebV  │ ~10 MB       │
│ NativeScript│ JS/TS        │ ✅ 原生     │ ⚠️ V8/JSCore │ ~30 MB       │
└─────────────┴──────────────┴────────────┴──────────────┴──────────────┘

Perry 是目前唯一做到"TypeScript + 零运行时 + 真原生控件"的框架。


十四、编译器内部结构(给想深挖的开发者)

perry/crates/
├── perry/               CLI 入口(compileruncheckinit├── perry-parser/        SWC TypeScript 解析器封装
├── perry-types/         类型系统
├── perry-hir/           HIR 数据结构 + ASTHIR 降级
├── perry-transform/     IR 优化 Pass(闭包转换async 展开内联)
├── perry-codegen/       LLVM 原生代码生成(核心!)
├── perry-codegen-js/    JavaScript 目标代码生成(--target web)
├── perry-codegen-wasm/  WebAssembly 目标代码生成
├── perry-runtime/       运行时(NaN-BoxingGCArena字符串)
├── perry-stdlib/        Node.js API 支持(fastifymysql2redis├── perry-ui-*/          各平台原生 UI(macOSiOSAndroidGTK4├── perry-ui-geisterhand/  UI 测试框架
└── perry-diagnostics/   错误报告

如果你想贡献新特性,流程是:

1. crates/perry-hir/src/ir.rs       → 添加 HIR 节点类型
2. crates/perry-hir/src/lower.rs    → 处理 AST→HIR 降级逻辑
3. crates/perry-codegen/src/codegen.rs → 生成对应 LLVM IR
4. crates/perry-runtime/             → 如需添加运行时函数
5. test-files/test_feature.ts        → 添加测试用例

十五、常用命令速查卡

# 基础编译
perry compile src/main.ts -o myapp

# 编译并直接运行
perry run .

# 初始化新项目
perry init my-project

# TypeScript 兼容性检查(不编译)
perry check src/

# 环境诊断
perry doctor

# 提取 i18n 字符串
perry i18n extract

# 跨平台目标
perry compile src/main.ts --target ios -o MyApp
perry compile src/main.ts --target android -o MyApp
perry compile src/main.ts --target web -o app.html
perry compile src/main.ts --target wasm -o app.wasm

# 编译为动态库(插件)
perry compile plugin.ts --output-type dylib -o plugin.dylib

# 启用 V8 运行时(兼容更多 npm 包)
perry compile src/main.ts --enable-js-runtime -o myapp

# 调试用:打印 HIR
perry compile src/main.ts --print-hir

十六、目前的局限性

Perry 目前不支持的 TypeScript 特性:

  • @Decorator(装饰器)暂不支持
  • 运行时类型检查(类型在编译时全部擦除,运行时用 typeof/instanceof
  • 单线程默认模型(主线程 + Tokio 异步 I/O;多线程需显式用 perry/thread

十七、总结

Perry 是一个非常有野心的项目——它的目标是让 TypeScript 真正成为一门"全平台原生开发语言",而不只是浏览器/Node.js 的专利。

适合 Perry 的场景:

  • CLI 工具(体积小,分发方便,无依赖)
  • 需要高性能的后端服务(避开 V8 JIT 不稳定性)
  • 跨平台桌面/移动 App(一套 TS 代码,10 个平台)
  • 对启动速度和内存极度敏感的场景
  • 想发布到 App Store / Play Store 的 TypeScript 开发者

不太适合的场景:

  • 大量依赖 Node.js 生态原生扩展(.node 文件)的项目
  • 重度使用 Decorator 元编程的项目(暂不支持)
  • 已有成熟 Electron 应用且无性能问题的维护阶段项目

从 2.2k star 和真实应用案例(Mango、Hone、dB Meter 等已上架)来看,Perry 已经在朝着"TypeScript 的 Rust"方向稳步前进,值得每一位 TypeScript 开发者关注和尝鲜。


📌 快速体验npx -y @perryts/perry compile hello.ts -o hello && ./hello

🌐 官网:www.perryts.com | 📦 GitHub:github.com/PerryTS/per…