Expo UI 中的 Worklet 集成:同步控制 SwiftUI 和 Compose 状态
Expo UI 现已集成 react-native-worklets,让你能在 UI 线程上同步控制 SwiftUI 和 Compose 状态。无需 JS 往返通信。
中文
复制

在 SDK 56 中,Expo UI 首次与 UI runtime worklet 实现了原生集成,这要归功于 react-native-worklets。这意味着我们可以在 UI 线程上同步执行事件回调,并同步控制 UI 状态。也就是说,你可以这样写:
import { Host, TextInput, useNativeState } from '@expo/ui';
export default function Screen() {
const value = useNativeState('');
return (
<Host matchContents>
<TextInput
value={value}
placeholder="Type something"
onChangeText={(value) => {
'worklet';
// Runs synchronously on the UI thread, on every keystroke.
console.log('[UI thread] typed:', value);
}}
/>
</Host>
);
}
注意:项目里需要安装 react-native-reanimated 和 react-native-worklets 才能正常工作。
实际发生了什么
这里有两部分在协同工作:
useNativeState 会创建一个 ObservableState,即一个存在于原生侧的 SharedObject,由 SwiftUI 和 Compose 共同观察。底层在 iOS 上对应 ObservableObject,在 Android 上对应 MutableState。
Worklet 回调(例如 onTextChange)会在原生视图触发事件时直接在 UI 线程上执行。
两者结合,TextField 中每敲一次键都会更新共享的 text 状态、运行你的 worklet,并让 SwiftUI 和 Compose 重新渲染,全程无需跳到 JS 线程。
如果你写过 SwiftUI,这套写法应该很眼熟。上面的 TS 代码几乎可以 1:1 对应到:
struct Screen: View {
@State var text = ""
var body: some View {
TextField("Type something", text: $text)
.onChange(of: text) { _, newValue in
print("[UI thread] typed:", newValue)
}
}
}
useNativeState 扮演的是 @State 的角色,text={text} 相当于 TextField(text: $text),worklet onTextChange 则对应 .onChange(of:)。在 Compose 上用 mutableStateOf 和 onValueChange 也是同样的结构。
无闪烁的同步输入掩码
这套机制最直接的收益是输入掩码终于能正常工作了。worklet 可以在按键到达的同一帧内改写 text.value,用户永远不会看到未掩码的字符,也不存在经过 JS 线程的异步往返。
下面这个信用卡输入框会在用户输入时于 UI 线程上把 4242424242424242 格式化为 4242 4242 4242 4242:
import { Host, TextInput, useNativeState } from '@expo/ui/swift-ui';
export default function CardNumberField() {
const value = useNativeState('');
return (
<Host matchContents>
<TextInput
value={value}
placeholder="Card number"
onChangeText={(value) => {
'worklet';
const digits = value.replace(/\\D/g, '').slice(0, 16);
const masked = digits.replace(/(.{4})/g, '$1 ').trim();
text.value = masked;
}}
/>
</Host>
);
}
视频:blog_video_16x9 — 在 expo.dev 上观看
同样的模式也适用于电话号码、日期、邮政编码、货币,以及任何显示文本需要与原始按键不同的场景。
Worklet 集成为什么重要
Worklet 集成让 Expo UI 能提供更接近原生的体验,并在现有的异步方案之外给出一个同步选项,你可以根据交互需求来选择。
输入掩码只是其中一个用例。同样的「原生状态 + worklet」模式,能让 Expo UI 把更多基于原生状态的 SwiftUI 和 Compose API 带到 React Native 上。
我们很期待看到它接下来的发展。
试用
Worklet 在 @expo/ui/swift-ui 和 @expo/ui/jetpack-compose 上均可使用,并且已经落地到 SDK 56。TextInput 是首批接入的组件之一,后续版本中会有更多表单控件获得同步回调。