HarmonyOS 鸿蒙Next中Taro转元服务,目标页未 ready 立即返回导致源页事件层卡死

HarmonyOS 鸿蒙Next中Taro转元服务,目标页未 ready 立即返回导致源页事件层卡死 我的页点击正常:点击–>跳转目标页

源页正常进入隐藏/显示:useDidHide -> useDidShow

目标页正常进入等待渲染结束:正常

目标页正常进入等待加载后立即返回:目标页卡死,无法点击,如果有ScrollView组件可以滑动;

点击卡死,且没打印日志,说明不是代码里的弹层吃点击

结论偏向于:鸿蒙元服务/Taro ASCF 在 navigateTo 目标页 JS 尚未初始化时被返回,中断路由转场后留下了原生层/路由层的事件拦截状态。

这就说明:这说明问题发生在 ASCF/鸿蒙路由原生转场阶段,早于Taro页面JS生命周期,navigateTo 发起 -> 原生开始创建目标页/加载分包/加载 JS -> 用户立刻返回 -> 目标页 JS 没启动 -> 路由中断后源页面事件层异常。

预期行为:即使目标页尚未完成初始化,用户返回后,上一页也应该恢复正常事件响应。

实际行为:上一页可见但点击事件无法继续传递,页面内容区卡死,只有底部原生 tab 可点击。

Dome很简单就可以测试,一个跳转加一个页面。Dome如果需要可以评论。


更多关于HarmonyOS 鸿蒙Next中Taro转元服务,目标页未 ready 立即返回导致源页事件层卡死的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

开发者您好,建议在Taro社区反馈,问题根因是路径位于node_modules@tarojs\plugin-framework-react\dist\runtime.js的AppWrapper的unmount中执行elements.splice(idx, 1)时没有对idx做判断,可以自行修改下。

class AppWrapper extends react.Component {
  // ...
    unmount(id, cb) {
        const elements = this.elements;
        const idx = elements.findIndex(item => item.props.tid === id);
        // idx大于-1时才执行代码
        if (idx > -1) {
            elements.splice(idx, 1);
        }
        this.forceUpdate(cb);
    }
    // ...
}

更多关于HarmonyOS 鸿蒙Next中Taro转元服务,目标页未 ready 立即返回导致源页事件层卡死的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这个现象更像是路由栈已经回退,但 React wrapper 的 elements 没有正确卸载,导致页面视觉上回去了,事件层还残留在目标页上。前面提到的 AppWrapper.unmount 里直接 elements.splice(idx, 1) 而没有判断 idx,这个点很关键。

建议先按这个顺序处理:

  1. 临时 patch node_modules/@tarojs/plugin-framework-react/dist/runtime.js,在 splice 前加保护:

    const idx = elements.findIndex((item) => item.ctx === ctx);
    if (idx > -1) {
      elements.splice(idx, 1);
    }
    
  2. 不要长期手改 node_modules,用 patch-packagepnpm patch 或锁定修复后的 Taro 版本,把补丁固化到工程里。

  3. 在交互上加一个路由锁:navigateTo 后等目标页 onLoad / 首屏 ready 再允许返回或重复跳转,目标页加载中时给 loading,避免“目标页还没挂载完成就返回”的竞态。

  4. 检查目标页首屏是否过重、是否有分包 / 网络初始化阻塞。首屏越慢,越容易触发这种跳转与卸载竞态。

如果 patch 后问题消失,基本可以确认是 Taro 运行时卸载逻辑的问题,建议带最小复现提交给 Taro / ASCF 侧跟进版本修复。

能否看下demo呢?

taro通过dev:ascf进行编译,taro版本是3.6.38,然后也进行了分包,下面是在我的页面添加的简易dome,小程序我测试过了页面可以之间打开,元服务中间还有一段js的解析加载过程,会有空白页的情况,在空白页即将消失的时候返回到我的页面,整个页面就会卡住,点击不了。

<View
  className={`flex flex_ai_center flex_jc_sb ${styles["entry-item"]}`}
  onClick={jumpToAscfRouteBugDemo}
>
  <Text>跳转简易dome页</Text>
</View>

const RouteProbe = () => {
  const logRouteDebug = (stage: string) => {
    console.log("[ascf route bug demo][target]", stage, {
      time: Date.now(),
      currentPages: Taro.getCurrentPages?.().map((page: any) => page?.route),
    });
  };

  useEffect(() => {
    logRouteDebug("useEffect mount");
    return () => logRouteDebug("useEffect unmount");
  }, []);

  useReady(() => {
    logRouteDebug("useReady");
  });

  useDidShow(() => {
    logRouteDebug("useDidShow");
  });

  useDidHide(() => {
    logRouteDebug("useDidHide");
  });

  useUnload(() => {
    logRouteDebug("useUnload");
  });

  return (
    <View
      style={{ padding: "80px 24px", backgroundColor: "#fff", minHeight: "100vh" }}
      onTouchStart={() => logRouteDebug("root onTouchStart")}
      onClick={() => logRouteDebug("root onClick")}
    >
      <Text>Route probe page</Text>
    </View>
  );
};

export default RouteProbe;

上面写错一个描述,是目标页正常进入等待加载后立即返回:源页卡死,无法点击,如果源页面有ScrollView组件可以滑动;

鸿蒙Next中Taro元服务的页面切换依赖异步ready机制。目标页未完成初始化(未调用ready)便立即返回时,源页的事件调度层无法正确释放等待资源,导致事件循环阻塞,页面卡死。这是页面栈状态机与事件分发时序未对齐的典型表现。

此问题根本原因在于鸿蒙元服务原生路由转场尚未完成时被中断,遗留了事件拦截层。可通过在源页重新显示时强制刷新事件层规避,示例代码如下(Taro 框架):

import { useDidShow, getCurrentInstance } from '@tarojs/taro';

function SourcePage() {
  useDidShow(() => {
    // 延迟等待原生层释放后,强制触发一次页面渲染以恢复触摸事件分发
    const timer = setTimeout(() => {
      const instance = getCurrentInstance();
      if (instance?.page) {
        instance.page.setData({ __touchFlag: Date.now() }); // 触发视图更新
      }
    }, 150);
    return () => clearTimeout(timer);
  });
  // ... 其他逻辑
}

如果仍无效,可将跳转延时,防止用户在目标页 JS 未初始化时返回:

// 延时 300ms 再 navigateTo,给分包加载留出时间
setTimeout(() => { Taro.navigateTo({ url: '/pages/target/index' }); }, 300);

优先推荐第一种方案,它不改变用户交互节奏。此问题已有对应 Issue 反馈,为临时规避措施。

回到顶部