
01跨端开发的「简史」A Brief History of Cross-Platform
要理解「#TS 直接编译原生」为什么让前端圈沸腾,先要回顾这条从「能跑就行」到「和原生没有区别」的演进之路。

图:跨端开发五大演进阶段 · 从 WebView 到 TS-Native


02技术原理:TS 如何「直接编译」原生?Under the Hood: Direct Compilation
Under the Hood: Direct Compilation
「直接编译」这个说法很诱人,但具体怎么实现?核心思路并不神秘——它和「Java 编译成字节码」或「Go 编译成机器码」在逻辑上是一致的,只是目标平台换成了 iOS 和 Android 的原生运行时。

2.1 阶段一:TypeScript → AST
这一步用 TSC 或 SWC 将 TS 源码解析为抽象语法树。TS 的类型声明(interface、type、enum)在编译期就已经参与分析,而不是像纯 JS 那样完全丢弃。
// 一个普通的 TSX 组件interface ButtonProps {label: string;onPress?: () => void;variant?: "primary" | "secondary";}export function Button({ label, onPress, variant = "primary" }: ButtonProps) {return (<Viewstyle={variant === "primary" ? styles.primary:styles.secondary}><Text>{label}</Text></View>);}
2.2 阶段二:类型擦除 & AST 转换
这是关键一步。编译器把 TS 的类型系统作为 元数据,用来生成目标平台的类型声明。比如 ButtonProps 这个 interface,会被转换成 iOS 的 @objc protocol 或 Android 的 data class。
React 风格的 JSX 则被重写为原生 UI 的构建 DSL——不再通过虚拟 DOM diff,而是直接调用 UIKit 或 Android View 的构造函数。
// 同一份逻辑,编译器生成的原生 Objective-C++@interfaceButtonProps : NSObject@property (nonatomic, strong, nullable) NSString *label;@property (nonatomic, copy, nullable) void(^onPress)(void);@end@implementationTSButton- (void)render:(ButtonProps *)props {self.titleLabel.text = props.label;self.layer.cornerRadius = 8;// ... 无桥接、无 JS 运行时,直接操作 UIKit}@end
2.3 阶段三:Type Checker(类型校验)
与 JS 编译器的「类型擦除后丢弃」不同,这里的类型系统 被保留用于生成代码。类型检查不仅保证 TS 端的正确性,还直接驱动目标平台的类型声明生成——这正是「TS 编译原生」区别于 RN/Flutter 的核心差异。
2.4 阶段四:Code Generator
根据目标平台配置,生成 Swift / Objective-C++(iOS)或 Kotlin / Java(Android)源码。这部分可以是「源码到源码」(类似 Kotlin Multiplatform),也可以是「源码到字节码」(类似 Go 对多架构的交叉编译)。
2.5 阶段五:链接为原生二进制
最终产物是一个 真正的 .app / .apk——其中没有任何 JS 引擎、没有任何 WebView、没有任何跨语言桥接。启动速度、内存占用、崩溃率,与原生项目处于同一量级。


3.1 React Native 的困境
RN 是跨端开发的先驱,但「JS Bridge」是它骨子里的伤。尽管 JSI 大幅改善了同步调用,但 JavaScript 作为解释型语言的天性决定了它永远无法在启动速度和内存占用上与原生完全持平。热更新和生态是 RN 的护城河,但这些优势恰恰是 TS-Native 不需要去抢的赛道。
3.2 Flutter 的傲慢
Flutter 用 Skia 自绘引擎实现了「像素级一致性」,代价是 30-50 MB 的起步包体积。对于金融、医疗等对包体敏感的场景,Flutter 的高昂入场费始终是个问题。而且,Dart 生态与前端/后端的主流语言栈几乎完全隔离。
3.3 TS-Native 的野心
它试图同时解决 RN 的「运行时开销」和 Flutter 的「引擎负担」——通过编译到原生,既不经过 JS 引擎,也不引入自绘引擎。如果这条路走通,它对 RN 和 Flutter 都是降维打击。 但「走通」和「能落地」之间,隔着至少三年的生态建设。
「直接编译原生」意味着理论上性能应该无限接近原生。但实际项目中的差异来自多个层面:动画帧率、冷启动耗时、内存峰值、以及复杂列表滚动下的卡顿率。

⚠️ 注意:以上为行业基准与合理估算,非特定产品实测
不同项目、不同机型差异巨大。TS-Native 的实际性能取决于编译器优化程度、代码生成质量以及目标平台 SDK 版本。目前的开源实现尚处于早期,实际数据仍有较大变数。
4.1 关键瓶颈不在「运行」,而在「编译」
把 TS 编译为原生代码,最大的挑战不是运行时性能,而是 编译链路的复杂度和稳定性。原生编译涉及 ABI 兼容、多线程模型差异、内存管理(ARC vs GC)等底层问题。一个编译器 Bug,可能导致整个应用闪退。
05冷静下来:三个被忽略的现实问题Three Reality Checks
5.1 「直接编译」的前提是「放弃热更新」
这是 TS-Native 最容易被忽视的代价。RN 可以通过 CodePush 实现 OTA 热更新,Flutter 可以通过渠道包分发。而 TS-Native 编译出的原生二进制,每次更新都必须经过 App Store / Google Play 审核。对于需要快速迭代的产品团队,这是致命的。
🚨 热更新 ≠ 小事
对电商大促、紧急 Bug 修复、A/B 实验等场景,无法热更新意味着每次改动都要走完整的审核发布流程。这在商业上可能是不可接受的。
5.2 原生双端同步 ≠ 真正「一套代码」
iOS 和 Android 的 UI 体系、手势系统、动画 API、甚至线程模型都存在根本差异。编译器可以解决大部分通用代码,但平台特定的原生能力(如 NFC、蓝牙、推送、后台任务)几乎不可能完全抽象。这意味着 「一套代码」更多是一个营销话术——关键模块仍然需要两端分别写。
5.3 生态建设需要 3-5 年
RN 和 Flutter 分别走了 9 年和 8 年才达到今天的成熟度。第三方 SDK 适配、CI/CD 工具链、调试能力、性能 profiling——这些不是「有编译器」就能自动解决的问题。TS-Native 目前最大的软肋,是 没有足够的生产项目验证。
✅ 正面信号
TypeScript 的用户基数、npm 生态的广度、以及前端团队对「跨端」的长期诉求,为 TS-Native 提供了最肥沃的土壤。如果它能在 1-2 年内解决热更新和生态问题,将极具爆发潜力。
06未来格局:不是「谁灭谁」,而是「分层共存」

RN、Flutter、TS-Native,以及 Kotlin Multiplatform、Swift/JS——它们不会互相「灭掉」,而是会沿着各自的赛道分化,形成 分层共存的生态格局。
6.1 短期(1-2 年):TS-Native 处于验证期
开源社区会有若干实验性编译器出现,部分中小团队开始尝试。但由于生态不足和热更新缺失,大规模商用不会发生。这一阶段的关键观察指标是:是否有知名大厂将其纳入生产环境。
6.2 中期(3-5 年):格局分化
- ▸RN
:稳定在需要热更新的中后端产品(电商、金融) - ▸Flutter
:在品牌 App、游戏化界面、对视觉一致性要求高的场景保持优势 - ▸TS-Native
:在追求原生性能但不接受 RN 运行时开销的中大型 App 中崛起 - ▸原生混合
:核心模块用 TS-Native 编译,平台专属能力用原生补充
6.3 长期(5 年+):「跨端」这个词可能消失
当所有方案都能输出「接近原生」的体验时,「跨端」就只是一个工程效率问题,而非技术选择。真正的前沿问题将转向:AI 辅助生成跨端应用、声明式 UI 的跨平台描述、以及 WebAssembly 能否覆盖移动端。
夜雨聆风