ZeroNative 深度拆解:Vercel 为何用 Zig 而非 Rust,打造下一代跨端原生应用框架
2026年5月9日,Vercel 旗下的 vercel-labs 团队悄悄放出了一个新项目 ZeroNative(vercel-labs/zero-native),用一句话概括它的定位:用 Zig 编写 native runtime,让前端开发者用熟悉的 Web UI 框架构建 macOS、Windows、Linux 桌面应用和 iOS、Android 移动应用。
发布两天,GitHub star 突破 2.5k,12 个 PR,109 个 fork,Apache-2.0 协议开源。文档站 zero-native.dev 同步上线,Quick Start、Web Engines、App Model、Bridge、Security、Packaging 一套齐备。
这个时间节点很有意思。就在一周前,Bun 创始人 Jarred Sumner 被曝出在分支上做「Zig 转 Rust」的 AI 翻译实验——Bun 正在认真考虑是否要从 Zig 切换到 Rust。而 Vercel 反手就把 Zig 摆到了前端工具链的核心位置。两条路线、两个顶级团队,在同一周走向了相反方向。
这篇文章,我们来深度拆解 ZeroNative 的技术架构,探讨 Vercel 为何在这个时间点选择了 Zig,以及它给前端开发者的跨端选型带来了什么新变量。
一、背景:跨端桌面方案的现状与困局
在 ZeroNative 之前,前端开发者想构建跨平台原生桌面应用,主要有两个成熟方案:
1.1 Electron:成熟但沉重的选择
Electron 本质上是 Node.js + Chromium 全家桶,将整个 Chrome 运行时打包进应用。VS Code、Discord、Slack、Teams、GitHub Desktop 这些业界标杆产品都跑在 Electron 上。
优点:生态极度成熟,npm 生态中几乎所有前端库都能直接用,调试体验友好,社区活跃。
缺点:体积庞大。以 Discord 为例,安装包轻松超过 200MB,内存占用起步就是 200-300MB。用户对 Electron 应用的刻板印象「慢、重、吃内存」,并非空穴来风。
# Electron 应用内存占用示例(Discord 客户端)
# RESIDENT RAM: ~300-500 MB
# CPU: 高于原生应用 2-5 倍
1.2 Tauri:轻量但有门槛
Tauri 用 Rust 编写应用外壳,底层调用系统原生 WebView(macOS 的 WKWebView、Windows 的 WebView2、Linux 的 WebKitGTK)。这使得它的安装包体积可以控制在 10MB 以内,内存占用也接近原生应用。
优点:体积小、安全模型干净(Rust 的内存安全)、性能优异。
缺点:需要一定 Rust 知识,门槛高于纯前端方案。移动端支持长期处于 alpha 阶段,Windows/macOS/Linux 之外的平台覆盖不足。
1.3 新的困局:两难选择
| 维度 | Electron | Tauri |
|---|---|---|
| 体积 | 100-500 MB | 5-20 MB |
| 性能 | 差 | 优 |
| 移动端 | 不可行 | alpha 阶段 |
| 生态 | 丰富 | Rust 生态 |
| 学习曲线 | 低(纯前端) | 中(需学 Rust) |
| 社区 | 成熟 | 活跃但较小 |
有没有第三条路?ZeroNative 的出现,正是对这个问题的直接回应。
二、ZeroNative 是什么:定位与核心理念
ZeroNative 的定位是第三条路:用 Zig 编写 native runtime,支持桌面和移动端 day-one,以极致的产物体积和编译速度为目标,对标 Tauri 但选了不同的底层语言。
2.1 核心数字
GitHub: vercel-labs/zero-native
协议: Apache-2.0
当前版本: v0.1.9 (pre-test)
Zig 语言占比: 74.6%
其余: Objective-C++, Objective-C, C(原生层胶水)
安装: npm install -g zero-native
2.2 支持矩阵
前端框架:Next.js、Vue、Svelte、Vite、React
桌面平台:macOS、Linux、Windows
移动平台:iOS(通过 C ABI 库集成)、Android(同上)
WebView 引擎:
- 系统级 WebView(WKWebView / WebView2 / WebKitGTK):轻量,依赖系统自带渲染器
- Chromium / CEF:渲染一致性高,包体积增大
2.3 与 Tauri 的核心差异
| 维度 | Tauri | ZeroNative |
|---|---|---|
| 壳语言 | Rust | Zig |
| 移动端 | alpha | day-one 支持 |
| WebView | 系统 WebView | 系统 WebView 或 Chromium/CEF(可选) |
| 安全模型 | Rust IPC + Rust 沙箱 | WebView 默认不可信 + opt-in 原生能力 |
| 安装包 | ~10-20 MB | 可更小(Zig 二进制优化激进) |
三、技术架构:三层结构深度解析
ZeroNative 的架构分为清晰的三个层次:
┌─────────────────────────────────────────────┐
│ App Layer │
│ (Zig 小对象,描述应用元数据和生命周期) │
├─────────────────────────────────────────────┤
│ Runtime Layer │
│ (事件循环、窗口管理、IPC 桥接、平台服务) │
├─────────────────────────────────────────────┤
│ WebViewSource Layer │
│ (HTML/URL 本地资源加载,WebView 引擎管理) │
└─────────────────────────────────────────────┘
3.1 App 层:应用元数据与生命周期
App 层是 Zig 编写的小对象,定义应用的基本信息和生命周期钩子。这是开发者最直接打交道的层。
// app.zon - ZeroNative 应用清单(类比 Cargo.toml / package.json)
const std = @import("std");
pub const app = .{
.name = "MyApp",
.version = "1.0.0",
.identifier = "com.example.myapp",
.window = .{
.title = "My ZeroNative App",
.width = 1024,
.height = 768,
.min_width = 800,
.min_height = 600,
.resizable = true,
},
.webview = .{
.engine = .system, // .system | .chromium | .cef
.devtools = true,
},
.security = .{
.allow_internet = true,
.local_only = false,
},
};
这个清单文件(app.zon)类比了 Rust 的 Cargo.toml 和 npm 的 package.json,集中声明了应用的所有元信息、窗口配置、Web 引擎选择和安全策略。
3.2 Runtime 层:核心引擎的工程实现
Runtime 层负责最核心的运行时能力:
事件循环:Zig 原生实现,与前端的事件模型自然对接。Zig 的 async/await 在这一层被用于处理窗口事件、系统回调和网络请求。
// Zig 事件循环简化示意
const EventLoop = struct {
pub fn run(self: *EventLoop) !void {
while (self.running) {
// 处理窗口事件
while (self.window_event_queue.pop()) |event| {
try self.handleWindowEvent(event);
}
// 处理 WebView 回调
while (self.webview_callback_queue.pop()) |callback| {
try self.handleWebviewCallback(callback);
}
// 处理原生能力调用
while (self.invoke_queue.pop()) |invoke| {
try self.handleInvoke(invoke);
}
}
}
};
窗口管理:跨平台窗口抽象,macOS 上用 AppKit,Windows 上用 Win32/WinUI,Linux 上用 GTK。ZeroNative 在这一层做了平台检测和接口统一。
// 窗口配置示例
pub const WindowConfig = struct {
title: []const u8,
width: u32,
height: u32,
min_width: u32,
min_height: u32,
resizable: bool,
fullscreen: bool,
decorations: bool, // 窗口边框与标题栏
};
IPC 桥接:WebView 和 native runtime 之间的通信核心。ZeroNative 定义了一套 JSON-RPC 风格的双向通信协议。
3.3 WebViewSource 层:灵活的渲染层
这一层负责加载前端资源,支持三种来源:
pub const WebViewSource = union(enum) {
// 从本地 HTML 文件加载
local_html: struct {
path: []const u8,
},
// 从 URL 加载
url: struct {
address: []const u8,
},
// 从打包的前端资源加载
bundled: struct {
dist_path: []const u8,
},
};
这三种模式覆盖了开发、测试、生产三种场景,让开发者可以灵活切换渲染源。
四、安全模型:从信任边界到能力驱动
ZeroNative 的安全模型是它最值得关注的架构决策之一。
4.1 核心原则:WebView 默认不可信
传统 Web 应用中,JavaScript 可以访问 window 对象上的所有能力。在 ZeroNative 中,这个默认假设被彻底翻转:WebView 中的内容(HTML、JS)被视为不可信来源,原生能力是显式 opt-in 的。
这与 Tauri 的设计哲学高度一致,但实现路径不同:
Tauri 的安全模型依赖 Rust 编译期的内存安全保证 + IPC 层的 Rust 实现。安全边界由 Rust 代码本身保证。
ZeroNative 的安全模型依赖:
- 来源校验:每个从 WebView 发出的原生调用,必须携带来源标识,Runtime 层校验来源域名/path
- 大小限制:IPC 消息有最大长度限制,防止 DoS
- 能力列表:
app.zon中显式声明允许使用的原生能力,未声明的能力调用一律拒绝 - 权限分级:原生能力按风险等级分级,高危能力(如文件系统写入、网络请求)需要额外确认
// 安全配置示例
pub const SecurityConfig = struct {
// 是否允许访问互联网
allow_internet: bool,
// 是否限制在本地网络
local_only: bool,
// 允许的文件系统路径(白名单)
allowed_fs_paths: []const []const u8,
// 允许的原生能力列表
allowed_capabilities: []const Capability,
};
// 能力枚举示例
pub const Capability = enum {
fs_read,
fs_write,
network,
camera,
clipboard,
notification,
device_info,
};
4.2 window.zero.invoke() 桥接机制
WebView 中的 JavaScript 通过 window.zero.invoke() 与 native runtime 通信:
// 调用原生能力示例
async function sendNotification() {
const result = await window.zero.invoke('notification.show', {
title: 'New Message',
body: 'You have 3 unread messages'
});
console.log('Notification sent:', result);
}
// 调用文件读取
async function readConfig() {
const result = await window.zero.invoke('fs.read', {
path: './config.json'
});
return JSON.parse(result.content);
}
// 监听原生事件
window.zero.on('app.foreground', () => {
console.log('App came to foreground');
});
桥接层在 Runtime 中做了严格的请求校验:
// Zig 端桥接处理示意
pub fn handleInvoke(ctx: *Context, msg: InvokeMessage) !InvokeResult {
// 1. 校验调用者来源
if (!ctx.security.isAllowedOrigin(msg.source_origin)) {
return error.OriginNotAllowed;
}
// 2. 检查消息大小
if (msg.data.len > MAX_INVOKE_DATA_SIZE) {
return error.PayloadTooLarge;
}
// 3. 校验能力权限
const cap = try ctx.resolveCapability(msg.method);
if (!ctx.security.hasCapability(cap)) {
return error.CapabilityNotGranted;
}
// 4. 路由到对应处理函数
return try ctx.routeAndExecute(msg.method, msg.data);
}
这套设计让前端开发者能方便地调用原生能力,同时安全模型保持清晰——你清楚地知道你能调用什么、不能调用什么。
五、为什么是 Zig 而不是 Rust:一场语言哲学的对撞
这是 ZeroNative 最值得深挖的问题。Vercel 本身就是 Rust 的重度用户:Turbopack(Rust)、Turborepo(Rust)、SWC(Rust)。按这个惯性,ZeroNative 用 Rust 才是直觉之选。
但他们没选。背后有四个关键理由:
5.1 产物体积与内存占用
Zig 对二进制大小的控制极为激进。与 Rust 不同,Zig 默认不做 panic 处理、不链接标准库以外的运行时、没有 Rust 编译器的额外优化 pass。ZeroNative 的目标产物是桌面和移动应用,产物体积直接关系到用户下载量和安装体验。
// Zig: 极致控制二进制大小
// Zig 编译器默认行为:
// - 无 panic unwinding overhead
// - 无 LLVM 优化引入的额外代码膨胀
// - 可精确控制链接哪些符号
一个 Tauri 桌面应用的安装包通常在 10-20MB,ZeroNative 的目标是在同等功能下更小。Zig 的编译产物体积控制是它的核心竞争力之一。
5.2 编译速度:开发者体验的核心变量
Rust 编译时间长是出了名的。cargo build --release 在中大型项目中轻松超过 5-10 分钟。即便是增量编译,Tauri 用户也经常吐槽开发迭代循环的等待感。
Zig 的编译循环接近 C——原生编译,链路短,没有 LLVM 优化阶段的额外开销。对于一个桌面壳应用(窗口管理 + 事件循环 + IPC 桥接),这个差距非常明显:
# Rust (Tauri): 增量编译典型耗时
cargo build # 30s - 3min (取决于项目规模)
# Zig (ZeroNative): 增量编译典型耗时
zig build # 1s - 10s (编译链路短)
对于前端开发者来说,这个差异决定了「写代码 → 看效果」的反馈循环是否顺畅。
5.3 C ABI 集成:跨平台的硬需求
ZeroNative 需要在多个平台、多种 WebView 引擎之间穿针引线:
- macOS:
WKWebView,AppKit 框架 - iOS:
WKWebView,UIKit 框架 - Windows:
WebView2,Win32 API - Linux:
WebKitGTK,GTK API - 可选:
Chromium Embedded Framework (CEF)
这些全是 C ABI 接口。Zig 的 C ABI 集成是它最成熟的能力之一——可以直接 #include C 头文件,调用任何 C 库,不需要任何绑定层或 FFI 包装:
// Zig 直接调用 macOS AppKit
const cocoa = @cImport({
@cInclude("AppKit/AppKit.h");
});
// Zig 直接调用 Windows Win32
const win32 = @cImport({
@cInclude("windows.h");
});
Rust 同样可以做到,但需要 bindgen 或手写 binding,链路更长,而且生成的 Rust 代码往往需要额外的 unsafe 标注。
5.4 借用检查器:在胶水代码场景下的「过度设计」
这是最微妙的一点。Rust 的借用检查器(borrow checker)在处理复杂内存关系时是巨大优势,但对于「胶水代码」——窗口创建、事件分发、IPC 桥接、FFI 调用——借用检查器往往变成开发节奏的减速带。
借用检查器的收益取决于场景:
| 场景 | 借用检查器收益 | 复杂度负担 |
|---|---|---|
| 服务器高并发内存管理 | 极高 | 值得 |
| 嵌入式资源受限环境 | 极高 | 值得 |
| Web 运行时(JS 引擎内) | 高 | 值得 |
| 桌面壳(IPC 胶水代码) | 低 | 不值得 |
Bun 的 Jarred Sumner 想切换到 Rust,理由是 Bun 面临的是亿次函数调用的内存管理,Rust 的安全性收益巨大。ZeroNative 是一个桌面壳,主要工作是窗口、事件循环和 IPC 桥接——借用检查器在这种场景下的收益低很多。
同样的语言特性,在不同项目里被算成了优点和负担。脱离场景谈语言优劣,没意义。
六、实战:从零构建一个 ZeroNative 应用
下面用一个完整的例子,展示如何用 ZeroNative 构建一个简单的桌面应用。
6.1 环境准备
# 安装 ZeroNative CLI
npm install -g zero-native
# 验证安装
zero-native --version
# 创建新项目(假设选择 Next.js)
npm create next-app@latest my-app
cd my-app
6.2 初始化 ZeroNative 配置
# 在项目目录中初始化
zero-native init
# 交互式配置:
# - 应用名称:My Desktop App
# - 标识符:com.example.myapp
# - 窗口大小:1024x768
# - WebView 引擎:system(macOS WKWebView)
这会在项目目录生成 app.zon:
# app.zon
{
.name = "My Desktop App",
.version = "1.0.0",
.identifier = "com.example.myapp",
.window = .{
.title = "My Desktop App",
.width = 1024,
.height = 768,
.min_width = 800,
.min_height = 600,
.resizable = true,
.decorations = true,
},
.webview = .{
.engine = .system,
.devtools = true,
},
.security = .{
.allow_internet = true,
.local_only = false,
.allowed_fs_paths = &["./data"],
.allowed_capabilities = &.{
.fs_read,
.notification,
.clipboard,
},
},
}
6.3 打包 Web 应用
# 构建前端
npm run build
# 打包为 ZeroNative 应用
zero-native build --platform macos --output ./dist
这会生成 macOS 的 .app 包,内部包含:
- Zig runtime(二进制)
- WebView 引擎
- 打包后的前端资源
6.4 前端调用原生能力
// pages/index.tsx
import { useEffect, useState } from 'react';
export default function Home() {
const [platform, setPlatform] = useState('');
const [notificationStatus, setNotificationStatus] = useState('');
useEffect(() => {
// 获取平台信息
window.zero.invoke('device.info').then((info) => {
setPlatform(`${info.os} ${info.arch}`);
});
}, []);
const sendNotification = async () => {
try {
const result = await window.zero.invoke('notification.show', {
title: 'Hello from ZeroNative!',
body: `Running on ${platform}`,
});
setNotificationStatus(`Success: ${result.id}`);
} catch (err) {
setNotificationStatus(`Error: ${err.message}`);
}
};
const readLocalFile = async () => {
try {
const result = await window.zero.invoke('fs.read', {
path: './data/config.json',
});
const data = JSON.parse(result.content);
console.log('Config loaded:', data);
} catch (err) {
console.error('Failed to read config:', err);
}
};
return (
<div style={{ padding: '2rem' }}>
<h1>ZeroNative Demo</h1>
<p>Platform: {platform}</p>
<button onClick={sendNotification}>
Send Notification
</button>
<p>{notificationStatus}</p>
<button onClick={readLocalFile}>
Load Config
</button>
</div>
);
}
6.5 类型安全的 invoke 封装
为了获得 TypeScript 类型提示,可以封装一层:
// lib/zero-native.ts
type Capability =
| 'device.info'
| 'notification.show'
| 'fs.read'
| 'fs.write'
| 'clipboard.write'
| 'clipboard.read';
interface InvokeResult<T = unknown> {
success: boolean;
data?: T;
error?: string;
}
async function invoke<T>(
capability: Capability,
payload?: Record<string, unknown>
): Promise<T> {
const result = await window.zero.invoke(capability, payload);
if (!result.success) {
throw new Error(result.error || 'Unknown error');
}
return result.data as T;
}
// 使用示例
interface DeviceInfo {
os: string;
arch: string;
version: string;
}
const deviceInfo = await invoke<DeviceInfo>('device.info');
console.log(`Running on ${deviceInfo.os} ${deviceInfo.arch}`);
七、与 Tauri 的全方位对比
| 维度 | Tauri | ZeroNative |
|---|---|---|
| 壳语言 | Rust | Zig |
| 最新版本 | v2.x | v0.1.9 |
| 二进制体积 | ~10-20 MB | 预计更小(Zig 优化激进) |
| 增量编译速度 | 慢(Rust LLVM 优化) | 快(接近 C) |
| 移动端 | alpha | day-one 支持 |
| C ABI 集成 | 需要 bindgen | 原生支持 |
| 安全模型 | Rust IPC + Rust 沙箱 | WebView 默认不可信 + opt-in |
| npm 集成 | 一般 | 优秀(Vercel 生态加成) |
| 生产就绪度 | 高 | 低(pre-test) |
| 生态 | 成熟(Tauri Plugins) | 新兴 |
结论:如果今天要在 Tauri 和 ZeroNative 之间做选择,Tauri 仍然是更稳妥的生产选择——它是经过生产验证的,生态插件丰富。但 ZeroNative 在移动端 day-one 支持、编译速度、C ABI 友好度上有明确的差异化优势,尤其适合需要同时覆盖桌面和移动的前端团队。
八、生产就绪度评估
必须诚实地说:ZeroNative 目前还处于 pre-test 阶段。
8.1 当前状态
版本: v0.1.9
生命周期: 11 个 release
稳定性: pre-test
文档: 基础文档齐备,但生产指南缺失
8.2 适合尝鲜的场景
- 内部工具和效率应用
- 个人项目和小团队产品
- 移动端探索性 MVP(快速验证跨端可行性)
- 对应用体积极度敏感的产品
8.3 需要等待的场景
- 对外发布的商业产品
- 需要高稳定性的企业应用
- 依赖丰富插件生态的场景
- 需要详细调试工具和崩溃报告
8.4 Vercel Labs 的项目成功率参考
Vercel Labs 旗下的项目命运分化明显:
成功进入主线:Turbopack、Next.js(收购)、SWC
实验性但有价值:Turborepo、Telemetry(部分功能)
静悄悄归档:部分早期实验项目
ZeroNative 能不能毕业,取决于接下来 3-6 个月的迭代节奏、社区增长和实际生产案例积累。
九、前端语言格局:2026 年的分叉路口
ZeroNative 的出现,放在更大的背景下看,是 2026 年前端生态语言选择分化的一个缩影。
Bun(Zig → Rust 的探索):JavaScript 运行时,原本用 Zig 重写核心以获得极致性能,现在在认真考虑迁移到 Rust 以获得内存安全保证。
ZeroNative(Zig 原生路径):Vercel Labs 用 Zig 构建跨端框架,看中的是 Zig 的体积控制、编译速度和 C ABI 友好度。
Node.js(渐进引入 Rust):Node.js 官方在边缘组件中逐步引入 Rust 代码(如 node:sqlite 的 native binding),但整体仍是 JavaScript/TypeScript 主导。
Tauri(Rust 成熟路径):Rust 构建桌面壳,生产验证度高,生态稳步增长。
前端语言 2026 年的格局,不再是「选 JS/TS 还是选其他」,
而是「哪些层用哪些语言」:
业务逻辑层: TypeScript / JavaScript(生态无可替代)
UI 渲染层: React / Vue / Svelte(前端框架选型)
胶水/粘合层: Zig(ZeroNative)、Rust(Tauri)
运行时核心: Rust(Bun 探索方向)、JS(Node.js 路线)
基础设施: Go(服务端)、Rust(性能敏感场景)
这种分化背后有一个共同逻辑:每个语言都在找自己最能发挥优势的那一层,而不是试图大一统。
十、总结:桌面跨端选型的新变量
10.1 ZeroNative 的核心价值
- 第三条路:在 Electron 和 Tauri 之间,提供了一个 Zig 路径
- 移动端 day-one:真正意义上从第一天就支持桌面 + 移动
- 体积与速度:Zig 的天然优势,可能带来最轻量的跨端产物
- 前端友好:Vercel 生态加成,npm 一键安装,前端开发者零门槛
- 安全模型清晰:能力驱动而非默认全开,与现代浏览器扩展安全模型一致
10.2 选型建议
Electron:需要最快上线、不在意体积、已有 Electron 经验的团队
Tauri:追求性能和体积、生产级稳定性、Rust 团队或愿意学 Rust 的团队
ZeroNative:需要同时覆盖桌面和移动、追求极致体积和编译速度、
愿意跟踪早期项目、作为技术储备关注 Zig 生态的团队
10.3 对前端开发者的意义
ZeroNative 最大的信号不是它本身能不能成,而是Vercel 在 2026 年公开站队 Zig。这意味着前端工具链的底层语言选型,已经不再是 Rust 一家独大的局面。Zig 作为「系统级胶水语言」的定位,正在被越来越多的顶级团队认可。
如果你对 Zig 感兴趣,或者正在做跨端桌面应用的选型评估,ZeroNative 是一个值得放进观察列表的项目。但生产使用,建议等 v1.0 之后再说。
项目地址:
https://github.com/vercel-labs/zero-native
文档站:https://zero-native.dev