设计一次,到处运行:跨 Web、iOS 和 Android 的一个 UI 文件
设计一次并在 Web、iOS 和 Android 上运行 UI 真正需要什么:共享布局和动作、特定于平台的部分、测试以及 ExodeUI 适合的位置。
核心要点
设计一次,到处运行意味着一个源文件描述一个 UI 组件,每个平台都在运行时播放它,而不是在 React、SwiftUI 和 Flutter 中重建相同的动作。当布局、状态、动作和输入存在于共享文件中时,它会起作用;当导航、可访问性和系统手势保持特定于平台时,它会保持诚实。 ExodeUI 通过 React、Vue、Svelte、Angular、Solid、React Native、SwiftUI 和 Flutter 的运行时包支持此模型。
“设计一次,到处运行”实际上需要什么
它需要的不仅仅是一个导出按钮。共享文件必须描述组件如何以不同大小布局、每个状态下的外观、它如何在状态之间移动以及应用程序如何通过输入驱动它。每个平台还需要一个可以播放文件并将这些输入连接到真实事件的运行时。如果其中任何一个部分缺失,承诺就会变成另一次交接。
共享文件与每个平台重建
传统的路线是设计一次,重建三次:Web 为 CSS 和 JavaScript,iOS 为 SwiftUI,Android 为 Kotlin 或 Flutter。每次重建都会在时间、间距和手势处理方面引入微小的差异。共享文件路由将组件定义保留在一处,并让每个平台都可以播放它。权衡是运行时位于文件和平台之间,因此您放弃了对特定于平台的渲染的一些直接控制。
什么应该保持特定于平台
共享组件不应假装每个平台都是相同的。导航模式、后退手势、系统对话框、权限和辅助功能 API 有所不同。共享文件应拥有交互式视觉组件;主机应用程序应该拥有平台镶边及其周围的语义控制。例如,共享动画按钮可以位于公开正确角色和标签的本机按钮元素内。
运行时如何工作
运行时读取文件,构建可视化树并在输入更改时更新它。您的应用程序代码设置按下、禁用或加载等输入,并侦听确认或关闭等事件。由于可视化定义是数据而不是每个平台的手写代码,因此运行时使同一文件可移植。运行时也是一个依赖项,因此请衡量它对您自己的构建的影响,而不是假设它是免费的。
在每个环境上测试相同的文件
测试文件将在哪里运行,而不仅仅是在编辑器中。检查支持的最小和最大尺寸的布局,用真实内容验证每个状态,并分别测试触摸、鼠标和键盘输入。然后检查可访问性:焦点顺序、标签、减少运动和屏幕阅读器行为。共享文件可以减少视觉漂移,但并不能消除测试每个平台的需要。
ExodeUI 适合的地方
ExodeUI 将组件存储在打开的 .exode 文件中,并为 React、Vue、Svelte、Angular、Solid、React Native、SwiftUI 和 Flutter 提供运行时包。该文件可以包括布局和断点、时间线、状态机、弹簧物理、输入和事件。可以在浏览器或桌面编辑器中编辑同一文件,AI编码工具可以通过MCP服务器对其进行处理。
何时不使用一个共享文件
当界面严重依赖于纯平台 API、需要对本机渲染进行像素级控制或组件主要是视频或 3D 场景时,共享文件不太合适。如果您的团队无法测试每个目标,那么它也不适合。在这些情况下,特定于平台的代码是诚实的选择,并且共享文件应该仅涵盖真正从中受益的部分。
生产实施片段
import { ExodeUICanvas } from 'exodeui';
// The same component file can be played by the runtime your app already uses.
// Web, React Native, SwiftUI and Flutter packages read the same .exode source.
export function ShareSheet({ open, progress, onClose }) {
return (
<ExodeUICanvas
src="/share_sheet.exode"
inputs={{ open, progress }}
onTrigger={(name) => name === 'close' && onClose()}
/>
);
}要记住的关键 Principle
- 设计一次,随处运行需要共享布局、状态、运动和输入,以及每个平台的运行时
- 导航、权限和辅助功能 API 应保持特定于平台
- 运行时是一种依赖;衡量它对您的构建的影响
- 测试每个目标的布局、输入、运动和可访问性
- ExodeUI 为 React、Vue、Svelte、Angular、Solid、React Native、SwiftUI 和 Flutter 提供运行时