Valdi——一个提供原生性能的跨平台 UI 框架

从一个日报起头:移动端团队被要求在两周里把活动页同时上线 iOS、Android 和 macOS。时间紧,交互细,动画还不能掉帧。传统解法要么写三套原生界面,要么走"Web 容器"取巧,结果要么开发效率吃紧,要么性能打折。此时,一个名字近乎低调的框架闯进视野------Valdi 。它把界面的声明式写法留在 TypeScript,把渲染权交给平台原生视图,试图把"效率与性能只能二选一"的旧逻辑翻过来。(GitHub)

它到底是什么

官方给出的定义非常直接:使用 声明式 TypeScript/TSX 写 UI,编译为 iOS、Android、macOS 的原生视图不依赖 WebView,也没有传统 JS bridge 。因此,列表滚动、复杂手势、系统级动画都沿用原生表现,避免"跨平台层"成为性能瓶颈。该框架在 Snap 的生产环境已经长期服役(内部使用达数年),如今以 MIT 许可证开源,当前对外标注为 Beta (主要是文档与工具链仍在对开源生态打磨)。(GitHub)

Valdi GitHubgithub.com/Snapchat/Va...

写法长什么样

在 Valdi 里,界面被写成 TSX 组件。下面的示例展示了"声明---渲染---属性"这一套直觉式流程(语义与官方示例一致,但做了适度裁剪与注释):

ts 复制代码
// HelloWorld.tsx
import { Component } from 'valdi_core/src/Component';

export class HelloWorld extends Component {
  onRender() {
    const message = 'Hello Valdi';
    // 这里的 <view>、<label> 会被编译成平台原生视图(非 WebView)
    <view backgroundColor="#FFFC00" padding={24}>
      <label color="black" value={message} />
    </view>;
  }
}

这种写法的关键不在"看起来像 React",而在"最终落地的是原生控件 "。渲染层的工作交给平台本身,框架则在编译期与运行期开启一系列性能手段(视图池复用、按可视区域增量渲染、组件级别的独立重渲),尽量不把重负留在跨层通信上。(GitHub)

Valdi 文档与入门github.com/Snapchat/Va...**

为什么在意"原生性能"

跨平台 UI 的历史里,性能问题往往来自两处: 其一是 渲染容器 ------WebView 或自绘引擎在"贴近系统"上天生吃亏;其二是 桥接成本------当状态频繁变动、动画密集、列表超长时,跨语言/跨进程通信会变成隐形税。Valdi 的路径是**"把 UI 编译成原生控件"**,并围绕这个落点做优化:

  • 全局 视图池 回收与复用,降低视图创建/销毁成本;
  • 独立组件重渲,避免祖先节点无谓更新;
  • 可视区域感知 布局与渲染,天生适配长列表与分页;
  • 布局引擎以 C++ 实现并运行在主线程,减少跨层编组。(GitHub)

这些点不是花拳绣腿,而是移动端耗时热点的"直线突击"。在信息流、商城、消息类应用中尤其明显:首屏更快出现,滚动更稳,滑动手感更接近系统原生控件。

工程化与开发体验

效率层面,Valdi 不只是在语法上"像 React"。它辅以 即时热重载 (毫秒级见效)、VS Code 调试与性能剖析 、与 Bazel 的集成以保障构建复用与可重复。需要原生接口时,可通过 多语言模块(Polyglot Modules) 生成 TypeScript ↔ Kotlin/Swift/ObjC 的类型安全绑定,既能复用既有原生库,又能把性能关键路径留在原生侧实现。(GitHub)

Bazelbazel.build/

迁移与落地的"故事线"

一个常见落地顺序是这样的: 先把一个独立页面新功能入口 用 Valdi 重写,内嵌到 UIKit/Android View 的树里做 A/B;性能验证后,再逐步扩大接入面。此时,原生与 Valdi 的双向嵌入 能力("在原生里放 Valdi 视图"与"在 Valdi 里嵌原生控件")就像安全带,既不破坏既有工程,又给新栈足够的发挥空间。(GitHub)

在数据通路上,团队可以把网络层、图片加载、埋点等继续留在原生模块里,通过类型安全的绑定暴露给 TypeScript。较重的计算(如协议编解码、复杂 diff、媒体管线)可以走 C++ 或平台原生语言;Valdi 提供的 worker 线程 能让 JS 侧在不阻塞 UI 的情况下完成部分并行任务。(GitHub)

与"别的跨平台"的差异

  • WebView 系(Hybrid/H5)不同:渲染不是网页,UI 不是 DOM;因此滚动、手势、导航条这类系统元素更贴近平台期望。
  • 桥接式 JS Runtime(重度依赖桥的方案)不同:Valdi 通过编译期把"声明式树"降解为原生视图,并尽量在渲染与布局阶段减少跨层通信频率。
  • 纯自绘引擎 不同:Valdi 倾向"调系统控件",从而继承各平台在无障碍、输入法、可达性、手势冲突处理等方面的成熟经验。以上差异点均来自其官方的渲染路线与性能要点描述。(GitHub)

Hacker News 讨论帖news.ycombinator.com/item?id=458...**

一个更完整的页面例子

下面构造一个稍微"像样"的列表页面:顶部标题、主题切换、可复用的 Cell,以及一个在可视区域内才渲染的长列表。逻辑演示为主:

ts 复制代码
// Feed.tsx
import { Component, state } from 'valdi_core/src/Component';

type Item = { id: string; title: string; subtitle: string };

function FeedCell(props: { item: Item }) {
  <view padding={16}>
    <label value={props.item.title} fontSize={17} weight="semibold" />
    <label value={props.item.subtitle} color="#666" marginTop={6} />
  </view>;
}

export class Feed extends Component {
  @state private dark = false;
  @state private items: Item[] = [];

  onMount() {
    // 模拟异步请求
    this.items = Array.from({ length: 2000 }).map((_, i) => ({
      id: String(i),
      title: `Card ${i}`,
      subtitle: `Valdi compiles to native views`,
    }));
  }

  private toggleTheme() {
    this.dark = !this.dark;
  }

  onRender() {
    <view backgroundColor={this.dark ? '#111' : '#fff'}>
      <view padding={16} direction="row" align="center" justify="space-between">
        <label value="Explore" fontSize={20} />
        <button onTap={() => this.toggleTheme()}>
          <label value={this.dark ? 'Light' : 'Dark'} />
        </button>
      </view>

      {/* 列表仅对可视区域进行增量渲染,长列表默认可流畅滚动 */}
      <list
        data={this.items}
        keyFor={(it: Item) => it.id}
        renderItem={(it: Item) => <FeedCell item={it} />}
        estimatedItemHeight={72}
      />
    </view>;
  }
}

这个例子对应了 Valdi 的三件"拿手活":视图池复用 (大量 Cell 循环利用)、组件级独立重渲 (切换暗黑只让受影响区域更新)、以及按可视窗口渲染 (大列表默认丝滑)。这些都在其 README 的性能段落中明确列出。(GitHub)

风险与边界

任何跨平台方案都不是"银弹"。Valdi 当前仍处 Beta 对外阶段,开源生态的文档、第三方组件、最佳实践沉淀需要时间;同时,团队需要具备原生与 TypeScript 并行的工程能力(特别是当接入平台能力、排查系统层问题时)。不过,从路线选择看,它将"性能优先"的矛盾点拉回到原生栈里,减少了"解释层/桥接层"把控体验的变量。(GitHub)


Valdi GitHubgithub.com/Snapchat/Va...** Valdi 入门与示例github.com/Snapchat/Va...** Bazel(构建工具)bazel.build/**


小结

Valdi 的价值主张并不花哨:用 TypeScript 写声明式 UI,把渲染交还给原生 。这一取舍,使其在"多端一致与原生体验"之间取得少见的平衡。对于正在寻找"性能不妥协、又能快速迭代"的团队,这是值得严肃评估的一条技术路径。(GitHub)

参考依据:框架定位、编译为原生视图、性能特性(视图池、布局引擎、可视区域渲染)、工程化能力(热重载、VS Code 调试、Bazel、多语言绑定)、开源许可与 Beta 状态等,均摘自 Valdi 官方 GitHub 仓库与开源发布信息。(GitHub)

相关推荐
用户0273851840268 天前
【Android】ViewPager2实现手/自动轮播图
weui·ui kit
红尘散仙12 天前
TRNovel王者归来:让小说阅读"声"临其境的终端神器
前端·rust·ui kit
齐生13 天前
iOS - UIViewController 生命周期
ui kit
JordanHaidee1 个月前
【Rust GUI开发入门】编写一个本地音乐播放器(11. 支持动态明暗主题切换)
前端·ui kit
songgeb3 个月前
UIScene in iOS
ios·ui kit
大熊猫侯佩5 个月前
UIKit 在 UICollectionView 中拖放交换 Cell 视图的极简实现
swift·apple·ui kit
仿生狮子8 个月前
Reka UI 是个啥?
vue.js·nuxt.js·ui kit
Sinyu10128 个月前
Flutter 自定义 CustomPaint 实现流体液态加载动画
前端·flutter·ui kit