VectoJS 是一个基于 Canvas 原生构建的 UI 运行时,具备虚拟数学树(Virtual Math Tree)、语义化无障碍映射、WebGL/WebGPU 后端以及支持智能体(Agent)驱动的控件。
过去十年的每一个 UI 框架都做出了不同的押注:DOM 是底层基底 (substrate),而框架的任务是以更快的速度管理它,或者用更好的语法将其封装起来。React 使用虚拟 Diff 来管理它。Svelte 通过编译消除了管理它的 Runtime 开销。Tailwind 减轻了为其编写样式的痛苦。但它们依然都假设:UI 的底层结构是一棵 HTML 元素树。
VectoJS 打破了这一假设。它将所有内容------文本、按钮、可滚动列表,甚至数以千计的实时数据点------全部渲染到一个 <canvas> 元素上。底层没有任何 HTML 树。一个 Button 只是一个普通的 TypeScript 对象,包含位置 (position)、尺寸 (size) 以及一个 render() 方法;整个 UI 是由这些对象构成的保留模式场景图 (retained scene graph),像游戏引擎的场景一样在每一帧进行遍历和绘制,而不是像文档那样进行流式布局 (layout)。
typescript
class DangerButton extends Button {
override render(r: IRenderer): void {
r.beginPath();
r.roundRect(0, 0, this.width, this.height, 8);
r.fill('#dc2626');
super.render(r); // 在顶层绘制文本标签
}
}
没有 CSS 优先级 (specificity),没有层叠 (cascade),也没有因为写完样式后又去读取而导致的布局抖动 (layout thrashing)------对象自身的字段 (fields) 就是唯一存在的状态。继承和组合的运作方式与任何其他 TypeScript 代码库完全一致,因为这就是纯粹的 TypeScript,而不是伪装成代码的模板语言。
显而易见的质疑
如果你读到了这里,你可能会想:好吧,但这不过是重新发明了 <canvas> 游戏而已,而且 Canvas 一直是无障碍访问 (accessibility) 的黑洞。屏幕阅读器只能看到一个不透明的位图,Ctrl+F 也搜不到任何东西。对于在此之前出现的所有 Canvas 应用来说确实如此,这也是大多数团队不考虑将 Canvas 用于真正 UI 开发的实际原因。
为了填补这一空白,我们具体构建了这样一套机制:每一个具有交互性 (interactive) 的实体,都会在一个精确定位于 Canvas 上方的图层中,映射出一个不可见的、真实的 DOM 节点------比如一个 <button>、一个 <input> 或一个 <a> 标签。这个节点的 Transform 属性在每一帧都会重新同步。因此,如果实体移动、缩放或旋转,这个不可见的 DOM 节点也会随之移动。键盘焦点、原生指针事件 (pointer events) 以及屏幕阅读器都会直接作用于这个真实的元素上,因为它确实是一个真实的 DOM 节点------只不过它是透明的,完美地覆盖在绘制内容之上。
这并不是没有代价的,我们也不打算粉饰太平:每一个交互实体对应一个真实的 DOM 节点,并且需要在每一帧保持同步,这就是它的开销。对于按钮、链接和表单控件------这些用户实际需要接触的元素------付出这样的开销是合理的。这也正是为什么我们不对典型 VectoJS 场景中另外 99% 的元素设置映射的原因:独立的粒子、字形 (glyphs) 以及数据点,除了一个 Draw Call 之外,不会产生任何额外开销,因为它们根本不需要交互。
这种分离带来了什么
因为非交互路径是零成本的,VectoJS 真正擅长处理的工作负载,正是那些会让基于 DOM 的 UI 彻底崩溃的场景:一个包含数百个可拖拽、相互连接的节点以及实时贝塞尔曲线的完整节点图编辑器,以 60fps 的帧率重绘,而图表本身零 DOM 元素;一个通过 WebGPU 计算通道驱动数万个粒子运动的场景,并在不支持 WebGPU 时无缝回退到 CPU 处理;一个能根据鼠标指针实时重排整本书籍的文本阅读器,对于这种规模的流式布局操作,没有任何现代浏览器引擎是为了如此流畅地执行它而构建的。
我们不会向你提供一个单一的"快 N 倍"的数字,因为根本没有绝对的标准------这取决于你的场景中有多少比例是真正可交互的,以及你的 GPU 和浏览器性能。我们能提供的是在 Demo 中内置实时的 FPS / 实体数量读数,以便你可以用自己的硬件进行客观测量,而不是盲目相信我们的数据。
亲自体验
这里的所有内容都在 GitHub 上以 MIT 许可证开源:包括引擎 (engine)、你可能正在阅读的文档网站 (docs site),以及一个社区画廊 (gallery)------任何人都可以在画廊中通过单文件 Pull Request 提交自己的 VectoJS 作品。
扩展阅读
- 常见问题 (FAQ) --- 对上述所有问题的真诚解答,包括本文中一带而过的细节。
- 无障碍访问 (Accessibility) --- 完整的映射投影 (Projection) API 文档。
- 重塑前端:VectoJS 的理念 (Rethinking Frontend: The VectoJS Philosophy) --- 从更宏观的角度探讨为何不应将 HTML/CSS 作为应用程序的底层基底。