☰
React Native 遇上 OpenHarmony:确认弹窗组件开发与适配实战
2026/10/23 3:31:03 网站建设 项目流程

这套组合我前后折腾了两周,踩坑的密度比我预想的高很多。先说项目背景:当时手里有个基于 React Native 的老业务要往 OpenHarmony 环境迁移,页面结构、组件树基本不动,但原生层换了一套系统之后,从前没人关心的细节全部浮出水面——第一个让我头疼的就是一个再普通不过的确认取消弹窗。放在 Web 端或 Android 上,Modal 组件一把梭,可到了 OpenHarmony 的 RN 适配环境里,焦点、返回手势、页面栈、事件穿透这些问题交织在一起,一个简单的 confirm 框竟然让我反复调试。

如果你也正在 React Native 和 OpenHarmony 这条路上“跨界”,这篇文章会在你动手之前先把我觉得最重要的东西讲透。

1. 需求建模:确认取消弹窗不只是一个“弹层”

1.1 三种常见交互场景,以及它们背后不同的状态约束

我刚接手这个需求时,产品经理嘴里的“加个确认弹窗”听起来是同一件事。等他讲完细节,我发现至少要区分三类:

  • 信息确认类:比如“确定提交这条数据吗”,用户点确定后进入请求流程,点取消则留在当前页。这类弹窗对状态要求最低,关闭弹窗和后续逻辑之间没有强耦合。
  • 危险操作类:比如“确定删除这条记录吗”,删除不可恢复。这种情况下取消按钮的分量比确认按钮更高,有的产品还会要求默认焦点停在“取消”上,防止误触回车或误点确认。React Native 的默认焦点策略比较难做到完全控制,这点后面细说。
  • 流程拦截类:比如填写表单中途离开,弹窗询问“离开将不保存内容”。这类弹窗往往同时面对两个页面栈的交接,处理不好会出现“弹窗没关就跳页”的闪屏问题。

这三种场景的差别看起来只是文案和回调,但落到状态管理上完全是两回事。信息确认类只需要一个布尔值控制 visible;危险操作类必须确保弹窗关闭后用户动作严格走“确认”或“取消”之一;流程拦截类则可能需要支持多次触发,比如用户在十秒内反复触发两次离开操作,弹窗不能直接叠两个出来。

我的建议是,动手写组件之前,先用状态机把弹窗生命周期理一遍。很多人忽略了这一步,直接把 visible 当成全部状态,结果后面加需求越来越吃力。

1.2 把弹窗生命周期抽象成两端数据流

我在项目里把确认取消弹窗抽象成了一段独立生命周期:关闭态 → 展示态 → 等待用户决策态 → 回调解算态 → 关闭态。

  • 关闭态:visible 为 false,组件不参与渲染,不占事件。
  • 展示态:visible 为 true,开始播放入场动画,此时用户可以看见弹窗,但要等动画结束才能操作,否则会感觉到“按钮不跟手”。
  • 等待用户决策态:点击确认或取消按钮后,先锁定双按钮的重复触发,再做回调。
  • 回调解算态:回调里可能有异步请求,比如删除请求需要两秒,此时弹窗是否保持?多数产品会要求弹窗保持、按钮 loading,直到请求结果返回再关闭。
  • 关闭态:执行关闭动画,动画结束后组件从渲染树移除。

如果直接控制一个布尔值,前四种状态全被压扁了,后面任何一环出问题都很难排查。尤其在 OpenHarmony 的 RN 环境下,原生层的窗口销毁时机和 JS 层不太一致,动画没结束就把节点移除,容易出现“页面跳过去但弹窗残余一帧”的闪烁。

所以我在正式封装组件时,text 变量从visible: boolean扩展为state: { visible: boolean; phase: 'entering' | 'ready' | 'leaving' | 'closed' }。虽然代码量多了,但后续调试和维护都顺很多。

2. 用 React Native 的 Modal 落一个可运行的确认取消组件

2.1 基础版组件:透明遮罩加卡片

先看最基础的一版实现。我用的是 React Native 内置的 Modal,加一层半透明遮罩,上面放白底卡片和两个按钮。

import React from 'react'; import { Modal, View, Text, Pressable, StyleSheet } from 'react-native'; export default function ConfirmDialog({ visible, title = '确认操作', message = '', confirmText = '确定', cancelText = '取消', onConfirm, onCancel, danger = false, }) { return ( <Modal visible={visible} transparent animationType="fade" statusBarTranslucent onRequestClose={onCancel} > <View style={styles.overlay}> <View style={styles.card}> <Text style={styles.title}>{title}</Text> {message ? <Text style={styles.message}>{message}</Text> : null} <View style={styles.row}> <Pressable style={styles.cancelBtn} onPress={onCancel}> <Text style={styles.cancelText}>{cancelText}</Text> </Pressable> <Pressable style={[styles.confirmBtn, danger && styles.confirmDanger]} onPress={onConfirm} > <Text style={styles.confirmText}>{confirmText}</Text> </Pressable> </View> </View> </View> </Modal> ); }

样式部分我直接贴出来:

const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.45)', alignItems: 'center', justifyContent: 'center', }, card: { width: '80%', backgroundColor: '#fff', borderRadius: 14, padding: 24, }, title: { fontSize: 16, fontWeight: '600', color: '#1a1a1a', }, message: { marginTop: 8, fontSize: 14, lineHeight: 22, color: '#666', }, row: { flexDirection: 'row', marginTop: 24, justifyContent: 'flex-end', }, cancelBtn: { paddingVertical: 10, paddingHorizontal: 20, borderRadius: 6, backgroundColor: '#f5f5f5', marginRight: 12, }, cancelText: { color: '#333', fontSize: 14, }, confirmBtn: { paddingVertical: 10, paddingHorizontal: 20, borderRadius: 6, backgroundColor: '#3370ff', }, confirmDanger: { backgroundColor: '#ff4d4f', }, confirmText: { color: '#fff', fontSize: 14, }, });

这一版跑起来没什么问题,但也仅限“没什么问题”。你看这个实现,有几个隐患:

  1. onRequestClose={onCancel}依赖平台把“系统返回”事件传进来,在 OpenHarmony 上并不总是触发,后面专门讲。
  2. 点击遮罩区域不会关闭弹窗,因为 overlay 没有绑定 onPress。有的产品要求点击空白处等同于取消,有的要求不能关。这个在封装时应该抽成参数maskClosable。
  3. 按钮没有做disabled防重复。用户快速连点两次确认,在 RN 里onPress会触发两次,回调被调两遍,可能发出两条重复请求。

这些都不是选型问题,是编写时的基本功。可在 OpenHarmony 适配下,这些基本功的失误会被放大,因为底层通知链路比 Android 多一层,事件延迟更明显,连点问题发生概率更高。

2.2 为什么我选择用 Promise 来替代回调

回调式 API 在简单场景看着方便:父组件里传onConfirm={() => doDelete()}。但一旦出现“动画中禁止重复触发”“异步请求后才关闭”这种需求,回调式 API 立刻变得松散,你需要在外层一堆状态里手动协调。

更推荐的做法是把组件封装成 Promise 函数,让用户代码变成同步逻辑的await写法:

import { useCallback, useRef, useState } from 'react'; import { ConfirmDialog } from './ConfirmDialog'; interface ConfirmOptions { title: string; message?: string; confirmText?: string; cancelText?: string; danger?: boolean; } type ConfirmState = { visible: boolean; options: ConfirmOptions; resolver: (value: boolean) => void; } | null; export function useConfirm() { const [state, setState] = useState<ConfirmState>(null); const stateRef = useRef<ConfirmState>(null); stateRef.current = state; const confirm = useCallback((options: ConfirmOptions) => { return new Promise<boolean>((resolve) => { setState({ visible: true, options, resolver: resolve }); }); }, []); const close = useCallback((result: boolean) => { const current = stateRef.current; if (!current) return; current.resolver(result); setState(null); }, []); const dialog = state ? ( <ConfirmDialog visible={state.visible} {...state.options} onConfirm={() => close(true)} onCancel={() => close(false)} /> ) : null; return { confirm, dialog }; }

业务代码里这样用:

const { confirm, dialog } = useConfirm(); async function handleDelete() { const ok = await confirm({ title: '确定删除这条记录?', message: '删除后无法恢复,请谨慎操作。', danger: true, }); if (!ok) { return; } // 这里才真正执行删除逻辑 }

Promise 的好处是把弹窗交互变成了一个可以组合的异步步骤,业务逻辑是顺序书写的,不会出现“回调里嵌回调”的嵌套地狱。

在实践中我还会给confirm增加一个队列管理,防止多个弹窗在同一时间同时出现。最简单的方法是维护一个isShowingRef,如果已有弹窗存在,就把新的确认请求放进一个数组,前一个关闭后再弹下一个。这个细节在业务里很管用,很多人没做,导致快速操作时弹窗叠加,OpenHarmony 上的 RN 适配对多窗口叠层支持还不成熟,叠加弹窗的层级错乱概率极高。

2.3 同一时间只允许一个确认弹窗:队列与优先级设计

队列入参是一个数组,每个元素是一个{ options, resolver }结构。展示时从队首出栈一个。我这里把优先级分成两层:普通优先级和紧急优先级。删除、强退这类危险操作置为紧急,会插队到普通弹窗之前。

type QueueItem = { options: ConfirmOptions; resolver: (value: boolean) => void; priority: 'normal' | 'high'; }; const queueRef = useRef<QueueItem[]>([]); const showingRef = useRef(false); function enqueueConfirm(item: QueueItem) { if (item.priority === 'high') { queueRef.current = [item, ...queueRef.current]; } else { queueRef.current.push(item); } processQueue(); } function processQueue() { if (showingRef.current || queueRef.current.length === 0) return; const next = queueRef.current.shift(); if (!next) return; showingRef.current = true; setState({ visible: true, options: next.options, resolver: next.resolver, }); } function closeCurrent(result: boolean) { const current = stateRef.current; if (current) { current.resolver(result); } showingRef.current = false; setState(null); // 下一个排队弹窗立即进场 requestAnimationFrame(() => { processQueue(); }); }

使用requestAnimationFrame是给前一个弹窗的离场动画留出窗口。如果不加这一个帧的等待,setState(null)和processQueue()在同一个 React 批次里执行,前一个弹窗会尚未卸载,新弹窗就渲染出来。这在普通 RN 上最多是视觉重叠,在 OpenHarmony 的 RN 适配层里则有可能导致原生容器没有正确重建,直接表现为“新弹窗白屏”。

3. 落到 OpenHarmony 上,真正要留意的几个行为差异

3.1 系统返回手势到底会不会自动关闭弹窗

这是我最想说的一点。在 Android 上,React Native 的 Modal 框架会自动监听系统返回键,触发onRequestClose。开发者只需要在回调里执行关闭逻辑就好。但在 OpenHarmony 的适配环境下,我实测发现不同 RN 适配版本对这个事件的传递并不可靠。

具体表现是:弹窗打开时,从屏幕左侧右滑返回,有些版本会直接关掉整个页面,而不是触发onRequestClose。原因我判断是底层把 RN 的 Modal 节点渲染成了一套独立的窗口,但这个窗口没有注册系统返回的手势回调,手势被系统当成“页面返回”处理,弹窗的窗口被整个关闭。

要规避这个问题,有两个方向:

  1. 把弹窗的遮罩层做成全屏可点击区域,并在点击时手动关闭弹窗,交互上引导用户通过点击遮罩或按钮退出。
  2. 在原生层补齐返回事件转发。RNOH 的 NativeModule 里可以注册一个OnBackPress订阅,把系统返回事件转发到 JS 层。我当时的适配版本里onRequestClose还算正常,但我仍然在后端加了一道保险:如果弹窗是打开状态,页面返回时直接拦截并重置路由。

这块没有普适结论,因为 OpenHarmony 的 RN 适配还在快速演进。给你的建议是:在真机上测试返回手势,不要只测虚拟返回键。开发模拟环境里的返回操作和真机手势走的路径不一样,后者才暴露问题。

3.2 焦点与触摸穿透:一个“看不见但能点”的诡异状况

有个 Bug 折腾了我一整天。弹窗正常显示,半透明遮罩也正常把后面的内容盖住了,但用手指点遮罩区域的任意位置,底层页面的按钮会被触发,仿佛遮罩是空气。

最初我怀疑是颜色透明度把遮罩渲染成完全透明了,但肉眼能看见变暗,说明遮罩确实渲染了。又怀疑是pointerEvents配置错误,查了一遍全部是默认值。后来我把 overlay 的背景改成纯黑色不透样式,测试依然能点透。

最后定位到问题:OpenHarmony 的 RN 适配层里,Modal节点单独渲染为一个原生窗口,这个窗口默认不拦截触摸事件,事件透传到了下面的页面窗口。这在 Android 上是不可想象的,因为 Android 的 Dialog 窗口天然会拦截事件。但 OpenHarmony 上,新建窗口如果不显式设置触摸模式,系统可能把它当成非交互型浮层,触摸事件穿透到下层窗口。

解决办法是在 overlay 上的每个子区域设置pointerEvents="auto",并且在原生层给弹窗的容器节点设置触摸可命中属性。如果你没有权限改原生层,也可以在弹窗内部挂一个全屏透明层,专门接收触摸事件并吃掉它:

<Modal visible={visible} transparent> <View style={styles.overlay}> <TouchableWithoutFeedback onPress={() => {}}> <View style={StyleSheet.absoluteFill} /> </TouchableWithoutFeedback> <View style={styles.card}> {/* 按钮和内容 */} </View> </View> </Modal>

这里用TouchableWithoutFeedback包裹的全屏层,作用是消费触摸事件,不让它们继续向下层页面传递。触摸事件的消费,本质上就是一层看不见的守卫。

这种“看不见但能点”的场景,在 web 和传统 RN 上几乎见不到,但 OpenHarmony 的窗口管理逻辑不同,适配层经常会犯低级错误。遇到类似状况,第一步不要怀疑业务代码,先在原生层确认窗口属性和事件分发方式。

3.3 页面切换动画与 Modal 层级错位的处理

OpenHarmony 的页面切换动画与 RN 内部 Modal 的动画是两个独立系统。前者控制整页的平移或缩放,后者控制弹窗自身的渐入渐出。如果使用过程中出现“点击按钮关闭弹窗并跳页”的连续操作,我遇到过这样的画面:弹窗已经关闭,但页面切换的动画期间,弹窗的一个残影跟着页面一起平移过去,看起来像一个透明卡片粘连在屏幕上。

这个问题的根因是 Modal 离场动画没有执行完,原生窗口舍不得销毁,而页面路由切换已经开始,系统把两个渲染栈的过渡帧混合在一起了。

解决路径有两个,都验证过有效:

一是切换路由前,先等待弹窗完全关闭并走完离场动画。React Native 的 Modal 有onDismiss回调,虽然传统上只有 iOS 完整支持,但我在某个 OpenHarmony 适配版本里也收到过。稳妥做法是在关弹窗后加 100~200ms 延迟再跳页:

async function handleSubmitAndGo() { closeCurrent(true); await new Promise((resolve) => setTimeout(resolve, 250)); navigation.navigate('HomeScreen'); }

二是干脆让弹窗的组件节点在路由页面内部不要跨路由存活。很多团队喜欢用一个全局的<ConfirmHost />挂在根部,再配合一个全局Modal开关。这在普通 RN 下很好用,但在 OpenHarmony 上,全局弹窗和具体页面的栈是剥离的两个窗口,切换页面时层级关系可能变得不可控。

我后来把全局弹窗方案改成了页面级弹窗:每个需要弹窗确认的页面自己挂一份ConfirmDialog,用 Context 统一管理状态,但渲染节点必须落在页面内部。这样页面切换时弹窗节点跟着页面走,系统窗口层级更稳定。

3.4 状态栏和沉浸式窗口的配合

OpenHarmony 的设备默认是全面屏,页面通常是沉浸式布局,状态栏半透明浮在页面内容上方。RN 的 Modal 如果设置了statusBarTranslucent,弹窗的遮罩会延伸到状态栏区域,视觉上更协调。但在某些系统版本里,弹窗打开时状态栏图标会压到弹窗卡片上,看起来很别扭。

我的处理是给弹窗卡片设置一个安全区上边距:

<View style={styles.card}> <Text style={styles.title}>{title}</Text> ... </View>

然后在样式里使用react-native-safe-area-context的useSafeAreaInsets计算上边距,确保卡片不被状态栏遮挡。

同时,弹窗打开期间建议隐藏底部导航条,避免用户误触返回。RN 侧可以调用系统能力隐藏导航条,但不同设备的行为不一致。我的建议是不要强制隐藏,而是在视觉上拉高遮罩的覆盖范围,让底部导航条区域也被半透明遮罩盖住,减少视觉跳跃感。

4. 真机调试记录:三个让人抓狂的 Bug 与排查链路

4.1 Modal 内容不更新,停留在上一次的快照

现象:第一次弹窗打开是“确定删除”的文案,关闭后第二次打开,文案还停留在“确定删除”,按钮的事件也指向旧回调。看起来像是组件没有重新渲染。

我一开始怀疑是 state 没更新,打印了 props,发现新值已经传进去了,但界面就是旧的。又怀疑 React 的 diff 出错,把key加在 Modal 上强制重建,依然不行。

最后排查到原生层:OpenHarmony 的 RN 适配里,同一个 Modal 节点在第二次打开时,复用了上次的原生窗口,而窗口上的内容更新没有触发强制重绘。这是适配层的缓存问题,JS 侧数据变了,原生侧却用了旧内存。

解决方案有两个:

  1. 每次关闭后把 Modal 的visible恢复到 false,并且等待扇区的动画完全结束后再置为 true。
  2. 给每个弹窗实例设置与众不同的key,让 React 认为这是一个全新节点,强制原生新建窗口。我最终用的是方案二,配合队列管理,效果稳定:
<ConfirmDialog key={dialogKey} visible={state.visible} {...state.options} onConfirm={() => closeCurrent(true)} onCancel={() => closeCurrent(false)} />

dialogKey每次弹出时递增一个计数。这个 key 不是 React 惯性意义上的列表优化,而是绕开原生层缓存的关键手段。

4.2 弹窗打开后软键盘顶起卡片

场景:弹窗内部有一个输入框,用于让用户输入二次确认的备注。弹窗打开后聚焦输入框,系统软键盘弹出,把整个卡片顶偏了。

移动端键盘行为一直是个老大难。在 Android 上,可以通过android:windowSoftInputMode="adjustResize"让窗口自动压缩。但 OpenHarmony 上很多设备对软键盘的避让处理只针对系统窗口,RN 的 Modal 窗口并没有自动注册避让逻辑,导致键盘弹出时卡片被无脑顶起,甚至顶出屏幕。

我的解决方案是在弹窗内部套一层KeyboardAvoidingView,并根据系统判断行为:

import { KeyboardAvoidingView, Platform } from 'react-native'; <Modal visible={visible} transparent> <KeyboardAvoidingView style={{ flex: 1 }} behavior={Platform.OS === 'ios' ? 'padding' : 'height'} > <View style={styles.overlay}> <View style={styles.card}>...</View> </View> </KeyboardAvoidingView> </Modal>

大多数开发者下意识把Platform.OS当成 Android or iOS 二元判断,但在 OpenHarmony 上它可能返回'harmony'或自定义字符串。所以这里的behavior需要额外判断一次,不能把所有非 iOS 系统都当成 height 模式。我在工程里写了一个工具函数:

function getModalKeyboardBehavior() { if (Platform.OS === 'ios') return 'padding'; if (Platform.OS === 'android' || Platform.OS === 'harmony') return 'height'; return 'padding'; }

别小看这个细节。OpenHarmony 适配版里Platform.OS到底返回什么,不同版本有差异,我在某个版本的 DevTools 里看过返回的是'harmony',另一个版本却返回'android'。这也是跨端开发最坑的地方,平台标记都不统一。最好的做法是通过react-native-app-info这类库拿真实系统信息,而不是一味依赖Platform.OS。

4.3 全屏页面离开后 Modal 瞬闪一次

现象:在 A 页面打开弹窗,点击“确认”后调用remove()方法跳转到 B 页面。B 页面出来后能看到一个极短暂的白色闪光,像是弹窗的卡片残留了一帧。

这类问题在 Android 上也有,但 OpenHarmony 上出现的概率更高。我推测是 Modal 关闭动画和页面 Stack 的跳转动画在同时进行,两个系统合成器没有协调好,A 页面的快照被短暂地渲染到了 B 页面之上。

排查步骤是把每个动画的耗时打印出来,确认 Modal 的离场动画是 150ms,页面切换动画约 300ms。问题显而易见:弹窗关闭动画先结束,页面还在切换,中间这段空档里 A 页面的内容已经被系统抽走,但 B 页面还没完全铺满屏幕,露出了底色。

解决思路是不要并行执行两个动画。我借鉴了 iOS 上presentingViewController的处理习惯,先关闭弹窗,等它走完onDismiss之后再执行页面跳转。代码写出来就是这个效果:

function handleConfirmAndNavigate() { closeCurrent(true); setTimeout(() => { navigation.remove(); }, 180); }

180ms 这个数字得根据你的弹窗动画时长动态计算。我后来把动画时长定义成常量,关闭弹窗后总是等待animationDuration + 30毫秒再跳页,虽然多耗了不到零点几秒,但视觉上干净了很多。

如果你连这点延迟都不能接受,还有一种激进方案:把弹窗卡片嵌在页面的最顶层,而不是使用 RN 内置 Modal 的系统窗口。这样弹窗和页面在同一个渲染栈里,页面跳转时弹窗跟着页面一起滚动,就不存在两个独立窗口的协调问题。代价是你需要自己实现遮罩、事件拦截、返回键监听,工程量翻倍。对于两个确认按钮这种通用需求,我不建议这么干。

5. 扩展思路与实战后的三条习惯

5.1 让弹窗服务成为全局基础设施,而不是散装组件

项目大了之后,各个页面都会用到确认取消弹窗。如果每个页面各自实例化一个ConfirmDialog,复用是做到了,但状态管理会变得零散,尤其面对“全局唯一弹窗”的约束时很难控制。

我最终的做法是做一个ConfirmProvider挂在路由页面之上的常驻节点里,内部持有useConfirm()返回的能力,通过 Context 向下分发。页面里的业务代码只需要:

const { confirm } = useConfirmContext(); await confirm({ title: '确定退出编辑?', message: '未保存内容将丢失', });

这个 Provider 内部同时管理队列、优先级和弹窗渲染节点,实现了页面只负责“调用”,Provider 负责“调度”的职责分离。但注意前文提到的层级问题,在 OpenHarmony 下这种常驻全局弹窗会跨页面存活,我做了额外的处理:路由监听器在页面切换时自动关闭当前弹窗,避免残留。

5.2 自动化测试用例设计重点

弹窗这类组件非常值得写自动化用例,尤其是确认/取消两个分支。我按这个清单来覆盖:

  • 点击确认按钮后,confirm返回 true,后续异步逻辑被执行。
  • 点击取消按钮后,confirm返回 false,后续逻辑被跳过。
  • 弹窗显示期间,多次点击确认按钮,只触发一次回调。
  • 弹窗打开时,模拟系统返回操作,onRequestClose被调用,弹窗关闭。
  • 队列场景:连续调用两次confirm,第二个不会立即弹出,第一个关闭后第二个才显示。
  • 遮罩点击:若maskClosable为 true,点击遮罩等同于取消;为 false 时点击无操作。

自动化测试在这个工程里主要用 Jest + React Test Renderer 跑组件逻辑。对于 OpenHarmony 特有的原生层问题,单纯 JS 测试覆盖不到,所以我还加了一遍设备上的 E2E 冒烟测试,重点验证弹窗打开、关闭、返回手势、事件穿透这四个场景。

5.3 我踩坑之后养成的三条习惯

第一,每次改动弹窗逻辑,先跑一遍噪声测试。所谓噪声测试,就是快速、无规律地连续开关弹窗、迅速点击按钮、同时切换页面,让各种边缘条件碰撞。很多问题都是在“看起来不太可能发生”的操作序列里暴露的。

第二,保持原生层和 RN 侧的版本一致。OpenHarmony 的 RN 适配版迭代很快,不同版本对 Modal 的支持差异非常明显。我遇到过一次 JS 层完全没有问题,但换了一个适配版本后 Modal 整个打不开的情况。项目里把 RN 依赖、OHOS 适配层版本都锁死在 package-lock 和 oh-package 里,升级前先跑全量回归。

第三,不在弹窗里塞太多功能。一开始有人提议在确认弹窗里顺便做个二次输入校验、再加个定时关闭、甚至嵌入一个图表预览。我全部砍掉了。确认取消弹窗的核心就是“让用户回答一个是或否”。功能一旦复杂,你就需要面对输入框焦点、数据校验、异步状态同步等一堆新问题,而这些在 OpenHarmony 的 Modal 窗口里都是坑。真想加复杂交互,单独做一个全屏抽屉页面,比塞进弹窗里稳定得多。

写到这儿,我回头看了看最初的那版代码,跟现在项目里沉淀下来的东西相比,已经面目全非。从普普通通的 visible + 两个按钮,一步步演进成支持队列、优先级、异步回调、多端适配的全局弹窗服务,这个过程本身就是一次跨端开发的深度训练。

如果你正在上一个“React Native 遇到 OpenHarmony”的项目,我的建议很简单:不要迷信框架的跨端性,任何边角能力都要在真机上踩一遍。Modal 只是一个开始,后面还有手势、布局、权限、原生模块一长串问题等着你。把这套排查思路带过去,你会顺手很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询