Logo
热心市民王先生

架构总览与演进背景

v1 架构:纯 Native macOS 应用

Raycast v1 本质上是一个用 Swift 基于 AppKit 构建的原生 macOS 应用。这一技术选择反映了团队在产品早期的明确偏好:深度控制每一个交互细节

AppKit 的深度定制

Raycast 团队在 v1 中几乎完全不使用标准 UI 组件:

“We almost never reached for standard UI components. They weren’t built for the kind of keyboard-first, power-user workflows we cared about, so we built our own.”

(我们几乎从不使用标准 UI 组件。它们不是为键盘优先的极客用户工作流设计的,所以我们自己构建了一套。)

具体实施层面,这意味着:

  • 每一行列表、每一个快捷键、每一个默认行为都由团队自行实现
  • 没有依赖 SwiftUI(尽管它与 Raycast 同时成熟),因为性能和控制的门槛始终未达到
  • SwiftUI 仅在每年的 Wrapped 功能中使用,与主应用完全隔离

扩展子系统:Web 技术早期尝试

Raycast 的扩展生态采用了完全不同的技术栈:

  • React + TypeScript + Node.js 构成的声明式 UI 系统将 UI 描述以声明式方式传递给 Native 应用渲染
  • 这一架构设计时就考虑了可移植性——扩展从不假设自己运行在 macOS 上
  • 这个预见性的设计使得 2024 年可以将大量扩展带到 Windows 平台

Notes 功能:Web 技术的成功试水

Raycast Notes 是应用中首个使用 WebView 的重要功能:

  • 编辑器是一个挂载在 Native 窗口 WebView 中的 React 应用
  • 验证了 “纯 Web 技术构建功能而不破坏应用整体体验” 的可行性
  • 被 macOS 和 iOS 的每日大量活跃用户所证明

重构动因:从启动器到生产力平台

时机选择:Windows 支持与架构老化

2023 年底,团队开始认真考虑将 Raycast 带到 Windows。这并非突如其来的想法:

“It was always the plan, from day one, but in the early days we wanted to focus on a single platform and nail the experience there before even considering expanding.”

(从第一天起这就是计划,但早期我们想先专注于单一平台,把体验做到极致,然后再考虑扩展。)

然而此时的 Raycast 已经从一个简单的启动器成长为包含 AI Chat、Notes、扩展、同步、文件搜索等功能的生产力平台。原有架构开始显现出根本性限制:

限制维度具体表现
编译时间逐渐上升,影响开发效率
AppKit 的限制频繁干扰新功能的实现
人才稀缺掌握深度 Native macOS 开发的人才越来越难找

用团队的话说:“Even if Windows wasn’t on the table, we’d have needed to rethink most of this.”(即使不考虑 Windows,我们也需要重新思考大部分架构。)

项目代号:X-Ray

任何大规模重构项目都需要一个代号。Raycast 团队将此项目命名为 “X-Ray”,代表 “cross-platform Raycast”(跨平台 Raycast)。

v2 架构:Four-Runtime 混合模型

Raycast 2.0 采用了一种独特的四层架构,每个组件都由最适合该任务的技术构建:

flowchart TB
    subgraph "Host Apps"
        A1[Swift + AppKit<br/>macOS Host]
        A2[C# + .NET 8 + WPF<br/>Windows Host]
    end
    
    subgraph "Shared Layers"
        B[Web Frontend<br/>React + TypeScript]
        C[Node Backend<br/>Business Logic]
        D[Rust Core<br/>Performance Critical]
    end
    
    A1 -->|Loads| B
    A2 -->|Loads| B
    B <-->|IPC| C
    C <-->|Shared Lib| D
    
    style A1 fill:#c6e0ff
    style A2 fill:#c6e0ff
    style B fill:#ffe6cc
    style C fill:#d4edda
    style D fill:#f8d7da

1. Host App(平台特定外壳)

每个平台有自己的原生应用:

macOS 版本:

  • 语言:Swift
  • 框架:AppKit
  • 职责:窗口管理、全局快捷键监听、菜单栏/托盘配置、加载 Web 前端到 WKWebView
  • 额外角色:监督 Node 后端进程

Windows 版本:

  • 语言:C#
  • 框架:.NET 8 + WPF
  • 职责与 macOS 版本对称
  • WebView:WebView2(基于 Chromium)

2. Web Frontend(共享 UI 层)

  • 单一代码库:一个 React + TypeScript 项目同时服务于两个平台
  • 多入口点:为不同窗口(Launcher、AI Chat、Notes、Settings 等)构建独立的 entry points
  • 100% 代码共享:两个操作系统使用完全相同的 UI 代码库

3. Node Backend(共享业务逻辑层)

  • 单长生命期进程:持续运行的 Node 进程承载应用的核心业务逻辑
  • 职责范围
    • 数据库访问
    • 扩展运行时(运行用户安装的 JavaScript 扩展)
    • 其他长驻服务
  • 跨平台价值:Node 是 Mac 和 Windows 共享的层,功能开发只需进行一次

4. Rust Core(性能关键层)

Rust 被用于性能或跨平台能力比开发便利性更重要的场景

组件用途
数据层与 iOS 应用共享的数据模型
云同步引擎与服务器端共享 schema
文件索引器自定义实现的文件搜索,用于在数秒内扫描整个硬盘

层间通信机制

四个 Runtime 之间需要高效通信。Raycast 采用混合方案:

flowchart LR
    A[WebView] <-->|Platform Message Handlers| B[Host App]
    B <-->|Stdio Transport| C[Node Backend]
    C <-->|Library Call| D[Rust Core]
    
    style A fill:#ffe6cc
    style B fill:#c6e0ff
    style C fill:#d4edda
    style D fill:#f8d7da

通信方式选择:

  • WebView ↔ Host App:平台特定的 message handlers(WKWebView/WWebView2 原生支持)
  • Host App ↔ Node Backend:通过 stdio 传输(stdin/stdout 管道)
  • Node Backend ↔ Rust Core:直接的库调用(通过 FFI 或 native addon)

类型安全保证:

“To make this safe to work with, interfaces are declared in one place and typed clients are generated for every side.”

(为了确保类型安全,接口在一个地方声明,然后为每个 Runtime 生成带类型的客户端。)

这意味着跨语言调用在编译时即可获得类型检查,避免运行时类型错误。

开发工作分布

实际开发中,团队的工作重心分布如下:

层级人员投入工作内容
Web Frontend + Node Backend大多数团队成员功能开发主战场
Native Shell专职 Native 工程师仅在需要暴露新的 OS API 或优化 Native 体验时介入

“Once the boundaries between the four parts are set, most product work doesn’t have to cross them.”

(一旦四个部分之间的边界设定好,大部分产品工作无需跨越这些边界。)

架构演进的核心逻辑

从 v1 到 v2 的架构变迁体现了一套清晰的技术决策框架:

flowchart TD
    A[v1: Swift Native<br/>高质量但开发慢] --> B{Windows 支持?}
    B -->|Yes| C[必须跨平台]
    B -->|No| D[人才稀缺<br/>限制发展]
    C --> E[评估方案: Electron/Tauri/Flutter/Qt]
    D --> E
    E --> F[Electron: 生态最好<br/>但控制力不足]
    E --> G[Tauri: 太年轻<br/>风险过高]
    E --> H[Flutter/Qt: 控制力不够<br/>或生态不成熟]
    F --> I[选择: Custom Hybrid<br/>Swift/C# + WebView]
    G --> I
    H --> I
    I --> J[v2: Four-Runtime<br/>平衡控制与效率]

这一架构的核心设计原则是:“A native app that uses web for its UI.”(一个使用 Web 作为 UI 的 Native 应用)——与常见的 “a web app with native hooks” 定位截然相反。这个定位上的巨大差异直接决定了团队在技术实现上的优先级取舍。